Ticket #120: mc-4.7.0-pre1.ydiff-full.patch
File mc-4.7.0-pre1.ydiff-full.patch, 121.3 KB (added by angel_il, 15 years ago) |
---|
-
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/config.h.in mc-4.7.0-pre1/config.h.in
old new 739 739 /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ 740 740 #undef TIME_WITH_SYS_TIME 741 741 742 /* Define to enable diff viewer */ 743 #undef USE_DIFF_VIEW 744 742 745 /* Define to enable undelete support on ext2 */ 743 746 #undef USE_EXT2FSLIB 744 747 -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/configure mc-4.7.0-pre1/configure
old new 801 801 USE_VFS_NET_TRUE 802 802 USE_VFS_FALSE 803 803 USE_VFS_TRUE 804 USE_DIFF_FALSE 805 USE_DIFF_TRUE 804 806 USE_EDIT_FALSE 805 807 USE_EDIT_TRUE 806 808 subdirs … … 1095 1097 with_slang_includes 1096 1098 with_slang_libs 1097 1099 with_edit 1100 with_diff 1098 1101 enable_background 1099 1102 enable_charset 1100 1103 ' … … 1825 1828 --with-slang-libs=[DIR] set path to SLANG library [default=/usr/lib]; may 1826 1829 sense only if --with-screen=slang 1827 1830 --with-edit Enable internal editor [yes] 1831 --with-diff Enable diff viewer [yes] 1828 1832 1829 1833 Some influential environment variables: 1830 1834 CC C compiler command … … 40743 40747 fi 40744 40748 40745 40749 40750 40751 # Check whether --with-diff was given. 40752 if test "${with_diff+set}" = set; then 40753 withval=$with_diff; 40754 fi 40755 40756 40757 if test x$with_diff != xno; then 40758 40759 cat >>confdefs.h <<\_ACEOF 40760 #define USE_DIFF_VIEW 1 40761 _ACEOF 40762 40763 use_diff=yes 40764 diff_msg="yes" 40765 { $as_echo "$as_me:$LINENO: using diff viewer" >&5 40766 $as_echo "$as_me: using diff viewer" >&6;} 40767 else 40768 diff_msg="no" 40769 fi 40770 40771 40746 40772 cons_saver="" 40747 40773 case $host_os in 40748 40774 linux*) … … 40820 40846 USE_EDIT_FALSE= 40821 40847 fi 40822 40848 40849 if test -n "$use_diff"; then 40850 USE_DIFF_TRUE= 40851 USE_DIFF_FALSE='#' 40852 else 40853 USE_DIFF_TRUE='#' 40854 USE_DIFF_FALSE= 40855 fi 40856 40823 40857 if test "x$use_vfs" = xyes; then 40824 40858 USE_VFS_TRUE= 40825 40859 USE_VFS_FALSE='#' … … 41285 41319 Usually this means the macro was only invoked conditionally." >&2;} 41286 41320 { (exit 1); exit 1; }; } 41287 41321 fi 41322 if test -z "${USE_DIFF_TRUE}" && test -z "${USE_DIFF_FALSE}"; then 41323 { { $as_echo "$as_me:$LINENO: error: conditional \"USE_DIFF\" was never defined. 41324 Usually this means the macro was only invoked conditionally." >&5 41325 $as_echo "$as_me: error: conditional \"USE_DIFF\" was never defined. 41326 Usually this means the macro was only invoked conditionally." >&2;} 41327 { (exit 1); exit 1; }; } 41328 fi 41288 41329 if test -z "${USE_VFS_TRUE}" && test -z "${USE_VFS_FALSE}"; then 41289 41330 { { $as_echo "$as_me:$LINENO: error: conditional \"USE_VFS\" was never defined. 41290 41331 Usually this means the macro was only invoked conditionally." >&5 … … 43958 43999 X11 events support: ${textmode_x11_support} 43959 44000 With subshell support: ${subshell} 43960 44001 Internal editor: ${edit_msg} 44002 Diff viewer: ${diff_msg} 43961 44003 Support for charset: ${charset_msg} 43962 44004 Search type: ${SEARCH_TYPE} 43963 44005 " -
configure.ac
diff -Naur mc-4.7.0-pre1~/configure.ac mc-4.7.0-pre1/configure.ac
old new 475 475 fi 476 476 477 477 478 dnl 479 dnl Diff viewer support. 480 dnl 481 AC_ARG_WITH(diff, 482 [ --with-diff Enable diff viewer [[yes]]]) 483 484 if test x$with_diff != xno; then 485 AC_DEFINE(USE_DIFF_VIEW, 1, [Define to enable diff viewer]) 486 use_diff=yes 487 diff_msg="yes" 488 AC_MSG_NOTICE([using diff viewer]) 489 else 490 diff_msg="no" 491 fi 492 493 478 494 dnl Check if the OS is supported by the console saver. 479 495 cons_saver="" 480 496 case $host_os in … … 533 549 fi 534 550 535 551 AM_CONDITIONAL(USE_EDIT, [test -n "$use_edit"]) 552 AM_CONDITIONAL(USE_DIFF, [test -n "$use_diff"]) 536 553 AM_CONDITIONAL(USE_VFS, [test "x$use_vfs" = xyes]) 537 554 AM_CONDITIONAL(USE_VFS_NET, [test x"$use_net_code" = xtrue]) 538 555 AM_CONDITIONAL(USE_UNDEL_FS, [test -n "$use_undelfs"]) … … 628 645 X11 events support: ${textmode_x11_support} 629 646 With subshell support: ${subshell} 630 647 Internal editor: ${edit_msg} 648 Diff viewer: ${diff_msg} 631 649 Support for charset: ${charset_msg} 632 650 Search type: ${SEARCH_TYPE} 633 651 " -
doc/man/mc.1.in
diff -Naur mc-4.7.0-pre1~/doc/man/mc.1.in mc-4.7.0-pre1/doc/man/mc.1.in
old new 2642 2642 is disabled, the dialog pops up only if you press 2643 2643 .B Alt-Tab 2644 2644 for the second time, for the first time MC just beeps. 2645 .\"NODE "Diff Viewer" 2646 .SH "Diff Viewer" 2647 Side-by-side diff output. 2648 .PP 2649 .B C-l 2650 Refresh the screen. 2651 .PP 2652 .B C-o 2653 Switch to the subshell and show the command screen. 2654 .PP 2655 .B C-u 2656 Swap the contents of the two panels. 2657 .PP 2658 .B C-r 2659 Redo diff. 2660 .I BUG: 2661 does not re-read files from 2662 .\"LINK2" 2663 VFS\&. 2664 .\"Virtual File System" 2665 .PP 2666 .B f 2667 Display each file full size (use 2668 .I C-u 2669 to switch between files). 2670 .PP 2671 .BR < ", " = ", " > 2672 Alter panel split. 2673 .PP 2674 .B s 2675 Display diff symbols (useful in 2676 .I nocolor 2677 mode). 2678 .PP 2679 .B l 2680 Display line numbers in each panel. 2681 .PP 2682 .B c 2683 Hide carriage returns. 2684 .PP 2685 .B h 2686 Toggle horizontal diff (if available). 2687 .PP 2688 .BR 2 ", " 3 ", " 4 ", " 8 2689 Set tabstop at desired number of spaces. 2690 .PP 2691 .B n 2692 Go to next hunk. 2693 .PP 2694 .B p 2695 Go to previous hunk. 2696 .PP 2697 .BR g ", " G 2698 Go to line number in left, respectively right panel. 2699 .PP 2700 .BR backspace 2701 Forget last search. 2702 .PP 2703 .BR F4 ", " F14 2704 Edit left, respectively right file. 2705 .PP 2706 .B F7 2707 Search in left file. 2708 .I BUG: 2709 incomplete: 2710 regexp/scanf style is not implemented; 2711 performs search only at line level. 2712 .PP 2713 .B F17 2714 Start search if there was no previous search expression else find next match. 2715 .I BUG: 2716 see above 2717 .PP 2718 .BR home ", " C-prev-page 2719 Go to first line. 2720 .PP 2721 .BR end ", " C-next-page 2722 Go to last line. 2723 .PP 2724 .B up 2725 Move one line up. 2726 .PP 2727 .B down 2728 Move one line down. 2729 .PP 2730 .B prev-page 2731 Move one page up. 2732 .PP 2733 .B next-page 2734 Move one page down. 2735 .PP 2736 .B left 2737 Move one column left. 2738 .PP 2739 .B right 2740 Move one column right. 2741 .PP 2742 .B C-left 2743 Move 8 columns left. 2744 .PP 2745 .B C-right 2746 Move 8 columns right. 2747 .PP 2748 .B C-a 2749 Move to the first column. 2750 .PP 2751 .BR q ", " C-g ", " escape 2752 Quit. 2753 .\"NODE "Diff Options" 2754 .SH "Diff Options" 2755 .PP 2756 .B Ignore case 2757 Ignore changes in case; consider upper- and lower-case letters equivalent. 2758 .PP 2759 .B ignore tab Expansion 2760 Ignore the distinction between tabs and spaces on input. A tab is considered 2761 to be equivalent to the number of spaces to the next tab stop. `diff' assumes 2762 that tab stops are set every 8 print columns. 2763 .PP 2764 .B ignore Space change 2765 This option is stronger. Ignore white space at line end, and consider all 2766 other sequences of one or more white space characters to be equivalent. 2767 .PP 2768 .B ignore all Whitespace 2769 This option is stronger still. Ignore difference even if one line has 2770 white space where the other line has none. "White space" characters include 2771 tab, newline, vertical tab, form feed, carriage return, and space; some 2772 locales may define additional characters to be white space. 2773 .PP 2774 .B strip trailing CR 2775 Treat input lines that end in carriage return followed by newline as if 2776 they end in plain newline. This can be useful when comparing text that is 2777 imperfectly imported from many personal computer operating systems. This 2778 option does not affect the way the files are displayed, only how lines are 2779 read, which in turn affects how they are compared. 2780 .PP 2781 .B Normal 2782 Run `diff' with default performance settings. 2783 .PP 2784 .B Fastest 2785 When the files you are comparing are large and have small groups of changes 2786 scattered throughout them, you can use this option to make a different 2787 modification to the algorithm that `diff' uses. If the input files have a 2788 constant small density of changes, this option speeds up the comparisons 2789 without changing the output. If not, `diff' might produce a larger set of 2790 differences; however, the output will still be correct. 2791 .PP 2792 .B Minimal 2793 Tell `diff' to use a modified algorithm that sometimes produces a smaller set 2794 of differences. It can also cause `diff' to run more slowly than usual. 2795 .\"NODE "Directory Diff Viewer" 2796 .SH "Directory Diff Viewer" 2797 Side-by-side directory diff. 2798 .PP 2799 .B C-l 2800 Refresh the screen. 2801 .PP 2802 .B C-o 2803 Switch to the subshell and show the command screen. 2804 .PP 2805 .B C-u 2806 Swap the contents of the two panels. 2807 .PP 2808 .B C-r 2809 Redo diff. 2810 .PP 2811 .B f 2812 Display each file full size (use 2813 .I C-u 2814 to switch between files). 2815 .PP 2816 .BR < ", " = ", " > 2817 Alter panel split. 2818 .PP 2819 .B s 2820 Display diff symbols (useful in 2821 .I nocolor 2822 mode). 2823 .PP 2824 .B l 2825 Display line numbers in each panel. 2826 .PP 2827 .B enter 2828 Diff items. 2829 .PP 2830 .B n 2831 Go to next hunk. 2832 .PP 2833 .B p 2834 Go to previous hunk. 2835 .PP 2836 .BR g ", " G 2837 Go to line number in left, respectively right panel. 2838 .PP 2839 .BR backspace 2840 Forget last search. 2841 .PP 2842 .BR F4 ", " F14 2843 Edit left, respectively right file. 2844 .PP 2845 .B F7 2846 Search in left panel. 2847 .I BUG: 2848 incomplete: 2849 regexp/scanf style is not implemented. 2850 .PP 2851 .B F17 2852 Start search if there was no previous search expression else find next match. 2853 .I BUG: 2854 see above 2855 .PP 2856 .BR home ", " C-prev-page 2857 Go to first line. 2858 .PP 2859 .BR end ", " C-next-page 2860 Go to last line. 2861 .PP 2862 .B up 2863 Move one line up. 2864 .PP 2865 .B down 2866 Move one line down. 2867 .PP 2868 .B prev-page 2869 Move one page up. 2870 .PP 2871 .B next-page 2872 Move one page down. 2873 .PP 2874 .B left 2875 Move one column left. 2876 .PP 2877 .B right 2878 Move one column right. 2879 .PP 2880 .B C-left 2881 Move 8 columns left. 2882 .PP 2883 .B C-right 2884 Move 8 columns right. 2885 .PP 2886 .B C-a 2887 Move to the first column. 2888 .PP 2889 .BR q ", " C-g ", " escape 2890 Quit. 2891 .\"NODE "Directory Diff Options" 2892 .SH "Directory Diff Options" 2893 .PP 2894 .B Recursive 2895 Diff subdirectories recursively. 2645 2896 .\"NODE "Virtual File System" 2646 2897 .SH "Virtual File System" 2647 2898 The Midnight Commander is provided with a code layer to access the file -
src/Makefile.am
diff -Naur mc-4.7.0-pre1~/src/Makefile.am mc-4.7.0-pre1/src/Makefile.am
old new 65 65 tree.c tree.h treestore.c treestore.h timefmt.h tty.c tty.h user.c \ 66 66 user.h util.c util.h utilunix.c view.c view.h vfsdummy.h widget.c \ 67 67 widget.h win.c win.h wtools.c wtools.h unixcompat.h \ 68 x11conn.h x11conn.c ecs.h ecs.c \68 x11conn.h x11conn.c ecs.h ecs.c ydiff.c ydiff.h zdiff.c zdiff.h \ 69 69 strutil.h strutil.c strutilascii.c strutil8bit.c strutilutf8.c \ 70 70 search/search.h strescape.c strescape.h 71 71 -
src/Makefile.in
diff -Naur mc-4.7.0-pre1~/src/Makefile.in mc-4.7.0-pre1/src/Makefile.in
old new 120 120 timefmt.h tty.c tty.h user.c user.h util.c util.h utilunix.c \ 121 121 view.c view.h vfsdummy.h widget.c widget.h win.c win.h \ 122 122 wtools.c wtools.h unixcompat.h x11conn.h x11conn.c ecs.h ecs.c \ 123 strutil.h strutil.c strutilascii.c strutil8bit.c strutilutf8.c \ 124 search/search.h strescape.c strescape.h charsets.c charsets.h \ 125 selcodepage.c selcodepage.h 123 ydiff.c ydiff.h zdiff.c zdiff.h strutil.h strutil.c \ 124 strutilascii.c strutil8bit.c strutilutf8.c search/search.h \ 125 strescape.c strescape.h charsets.c charsets.h selcodepage.c \ 126 selcodepage.h 126 127 am__objects_1 = achown.$(OBJEXT) background.$(OBJEXT) boxes.$(OBJEXT) \ 127 128 chmod.$(OBJEXT) chown.$(OBJEXT) cmd.$(OBJEXT) color.$(OBJEXT) \ 128 129 command.$(OBJEXT) complete.$(OBJEXT) cons.handler.$(OBJEXT) \ … … 141 142 treestore.$(OBJEXT) tty.$(OBJEXT) user.$(OBJEXT) \ 142 143 util.$(OBJEXT) utilunix.$(OBJEXT) view.$(OBJEXT) \ 143 144 widget.$(OBJEXT) win.$(OBJEXT) wtools.$(OBJEXT) \ 144 x11conn.$(OBJEXT) ecs.$(OBJEXT) strutil.$(OBJEXT) \ 145 strutilascii.$(OBJEXT) strutil8bit.$(OBJEXT) \ 146 strutilutf8.$(OBJEXT) strescape.$(OBJEXT) 145 x11conn.$(OBJEXT) ecs.$(OBJEXT) ydiff.$(OBJEXT) \ 146 zdiff.$(OBJEXT) strutil.$(OBJEXT) strutilascii.$(OBJEXT) \ 147 strutil8bit.$(OBJEXT) strutilutf8.$(OBJEXT) \ 148 strescape.$(OBJEXT) 147 149 am__objects_2 = charsets.$(OBJEXT) selcodepage.$(OBJEXT) 148 150 @CHARSET_FALSE@am_mc_OBJECTS = $(am__objects_1) 149 151 @CHARSET_TRUE@am_mc_OBJECTS = $(am__objects_1) $(am__objects_2) … … 420 422 tree.c tree.h treestore.c treestore.h timefmt.h tty.c tty.h user.c \ 421 423 user.h util.c util.h utilunix.c view.c view.h vfsdummy.h widget.c \ 422 424 widget.h win.c win.h wtools.c wtools.h unixcompat.h \ 423 x11conn.h x11conn.c ecs.h ecs.c \425 x11conn.h x11conn.c ecs.h ecs.c ydiff.c ydiff.h zdiff.c zdiff.h \ 424 426 strutil.h strutil.c strutilascii.c strutil8bit.c strutilutf8.c \ 425 427 search/search.h strescape.c strescape.h 426 428 … … 623 625 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/win.Po@am__quote@ 624 626 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wtools.Po@am__quote@ 625 627 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x11conn.Po@am__quote@ 628 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ydiff.Po@am__quote@ 629 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zdiff.Po@am__quote@ 626 630 627 631 .c.o: 628 632 @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/cmd.c mc-4.7.0-pre1/src/cmd.c
old new 57 57 #include "tty.h" /* LINES */ 58 58 #include "dialog.h" /* Widget */ 59 59 #include "view.h" /* mc_internal_viewer() */ 60 #include "zdiff.h" /* view_diff_cmd() */ 60 61 #include "wtools.h" /* message() */ 61 62 #include "widget.h" /* push_history() */ 62 63 #include "key.h" /* application_keypad_mode() */ … … 833 834 "listing mode to use this command ")); 834 835 } 835 836 } 837 838 #ifdef USE_DIFF_VIEW 839 void 840 diff_view_cmd (void) 841 { 842 view_diff_cmd(NULL); 843 } 844 #endif 836 845 837 846 void 838 847 history_cmd (void) -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/cmd.h mc-4.7.0-pre1/src/cmd.h
old new 41 41 void edit_mc_menu_cmd (void); 42 42 void quick_chdir_cmd (void); 43 43 void compare_dirs_cmd (void); 44 void diff_view_cmd (void); 44 45 void history_cmd (void); 45 46 void tree_cmd (void); 46 47 void link_cmd (void); -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/color.c mc-4.7.0-pre1/src/color.c
old new 110 110 /* error dialog colors start at 39 */ 111 111 { "errdhotnormal=", 0, 0 }, /* Error dialog normal/hot */ /* 39 */ 112 112 { "errdhotfocus=", 0, 0 }, /* Error dialog focused/hot */ 113 114 /* diff viewer colors start at 41 */ 115 { "dffadd=", 0, 0 }, /* added line */ 116 { "dffchg=", 0, 0 }, /* changed line */ 117 { "dffchh=", 0, 0 }, /* changed line (highlight) */ 118 { "dffchd=", 0, 0 }, /* changed line (deleted) */ 119 { "dffdel=", 0, 0 }, /* deleted line */ 113 120 }; 114 121 115 122 struct color_table_s { … … 262 269 "editwhitespace=brightblue,blue:" 263 270 "editlinestate=white,cyan:" 264 271 "errdhotnormal=yellow,red:" 265 "errdhotfocus=yellow,lightgray", NULL); 272 "errdhotfocus=yellow,lightgray:" 273 , 274 "dffadd=black,green:" 275 "dffchg=black,brown:" 276 "dffchh=black,magenta:" 277 "dffchd=lightgray,black:" 278 "dffdel=lightgray,red" 279 , 280 NULL); 266 281 267 282 extern char *command_line_colors; 268 283 -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/color.h mc-4.7.0-pre1/src/color.h
old new 93 93 #define ERROR_HOT_NORMAL IF_COLOR (39, 0) 94 94 #define ERROR_HOT_FOCUS IF_COLOR (40, 0) 95 95 96 /* Diff colors */ 97 #define DFFADD_COLOR IF_COLOR(41, A_BOLD) 98 #define DFFCHG_COLOR IF_COLOR(42, A_UNDERLINE) 99 #define DFFCHH_COLOR IF_COLOR(43, A_UNDERLINE) 100 #define DFFCHD_COLOR IF_COLOR(44, A_REVERSE) 101 #define DFFDEL_COLOR IF_COLOR(45, A_REVERSE) 102 96 103 #ifdef HAVE_SLANG 97 104 # define CTYPE const char * 98 105 #else -
src/history.h
diff -Naur mc-4.7.0-pre1~/src/history.h mc-4.7.0-pre1/src/history.h
old new 38 38 39 39 #define MC_HISTORY_HOTLIST_ADD "mc.hotlist.add" 40 40 41 #define MC_HISTORY_YDIFF_GOTO_LINE "mc.ydiff.goto-line" 42 #define MC_HISTORY_ZDIFF_GOTO_LINE "mc.zdiff.goto-line" 43 41 44 #endif -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/main.c mc-4.7.0-pre1/src/main.c
old new 917 917 {' ', N_("s&Wap panels C-u"), NULL_HOTKEY, swap_cmd}, 918 918 {' ', N_("switch &Panels on/off C-o"), NULL_HOTKEY, view_other_cmd}, 919 919 {' ', N_("&Compare directories C-x d"), NULL_HOTKEY, compare_dirs_cmd}, 920 #ifdef USE_DIFF_VIEW 921 {' ', N_("&View diff files C-x C-y"), NULL_HOTKEY, diff_view_cmd}, 922 #endif 920 923 {' ', N_("e&Xternal panelize C-x !"), NULL_HOTKEY, external_panelize}, 921 924 {' ', N_("show directory s&Izes"), NULL_HOTKEY, dirsizes_cmd}, 922 925 {' ', "", NULL_HOTKEY, 0}, … … 1262 1265 static const key_map ctl_x_map[] = { 1263 1266 {XCTRL ('c'), quit_cmd}, 1264 1267 {'d', compare_dirs_cmd}, 1268 #ifdef USE_DIFF_VIEW 1269 {XCTRL ('y'), diff_view_cmd}, 1270 #endif 1265 1271 #ifdef USE_VFS 1266 1272 {'a', reselect_vfs}, 1267 1273 #endif /* USE_VFS */ … … 2009 2015 " errdhotfocus\n" 2010 2016 " Menus: menu, menuhot, menusel, menuhotsel\n" 2011 2017 " Editor: editnormal, editbold, editmarked, editwhitespace,\n" 2012 " editlinestate\n"), stdout); 2018 " editlinestate\n" 2019 " Diff viewer: dffadd, dffchg, dffchh, dffchd, dffdel\n"), stdout); 2013 2020 fputs (_ 2014 2021 ( 2015 2022 " Help: helpnormal, helpitalic, helpbold, helplink, helpslink\n" -
src/myslang.h
diff -Naur mc-4.7.0-pre1~/src/myslang.h mc-4.7.0-pre1/src/myslang.h
old new 29 29 #define ACS_LLCORNER SLSMG_LLCORN_CHAR 30 30 #define ACS_URCORNER SLSMG_URCORN_CHAR 31 31 #define ACS_LRCORNER SLSMG_LRCORN_CHAR 32 #define ACS_TTEE SLSMG_UTEE_CHAR 33 #define ACS_BTEE SLSMG_DTEE_CHAR 32 34 33 35 #define acs() SLsmg_set_char_set(1) 34 36 #define noacs() SLsmg_set_char_set (0) -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/tty.c mc-4.7.0-pre1/src/tty.c
old new 171 171 } 172 172 173 173 extern void 174 tty_print_nstring(const char *s, int n) 175 { 176 #ifdef HAVE_SLANG 177 SLsmg_write_nstring(str_unconst(s), n); 178 #else 179 addnstr(s, n); 180 #endif 181 } 182 183 extern void 174 184 tty_print_one_hline(void) 175 185 { 176 186 if (slow_terminal) -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/tty.h mc-4.7.0-pre1/src/tty.h
old new 60 60 extern void tty_print_char(int); 61 61 extern void tty_print_alt_char(int); 62 62 extern void tty_print_string(const char *); 63 extern void tty_print_nstring(const char *s, int n); 63 64 extern void tty_print_one_vline(void); 64 65 extern void tty_print_one_hline(void); 65 66 extern void tty_print_vline(int top, int left, int length); -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/ydiff.c mc-4.7.0-pre1/src/ydiff.c
old new 1 /* 2 * Copyright (c) 2007 Daniel Borca All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 19 20 #include <config.h> 21 #include <ctype.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <stdlib.h> 25 #include <sys/stat.h> 26 #include "global.h" 27 #include "tty.h" 28 #include "cmd.h" 29 #include "dialog.h" 30 #include "widget.h" 31 #include "color.h" 32 #include "help.h" 33 #include "key.h" 34 #include "wtools.h" 35 #include "charsets.h" 36 #include "history.h" 37 #include "ydiff.h" 38 39 40 #ifdef USE_DIFF_VIEW 41 42 typedef struct { 43 int len, max; 44 void *data; 45 int error; 46 int eltsize; 47 int growth; 48 } ARRAY; 49 50 #define FILE_READ_BUF 4096 51 #define FILE_FLAG_TEMP (1 << 0) 52 53 typedef struct { 54 int fd; 55 int pos; 56 int len; 57 char *buf; 58 int flags; 59 void *data; 60 } FBUF; 61 62 #define ADD_CH '+' 63 #define DEL_CH '-' 64 #define CHG_CH '*' 65 #define EQU_CH ' ' 66 67 typedef struct { 68 int a[2][2]; 69 int cmd; 70 } DIFFCMD; 71 72 typedef int (*DFUNC) (void *ctx, int ch, int line, off_t off, size_t sz, const char *str); 73 74 #define HDIFF_ENABLE 1 75 #define HDIFF_MINCTX 5 76 #define HDIFF_DEPTH 10 77 78 typedef struct { 79 int off; 80 int len; 81 } BRACKET[2]; 82 83 typedef int PAIR[2]; 84 85 #define TAB_SKIP(ts, pos) ((ts) - (pos) % (ts)) 86 87 typedef enum { 88 DATA_SRC_MEM = 0, 89 DATA_SRC_TMP = 1, 90 DATA_SRC_ORG = 2 91 } DSRC; 92 93 typedef struct { 94 int ch; 95 int line; 96 union { 97 off_t off; 98 size_t len; 99 } u; 100 void *p; 101 } DIFFLN; 102 103 typedef struct { 104 FBUF *f; 105 ARRAY *a; 106 DSRC dsrc; 107 } PRINTER_CTX; 108 109 typedef struct { 110 Widget widget; 111 112 const char *args; /* Args passed to diff */ 113 const char *file[2]; /* filenames */ 114 const char *label[2]; 115 FBUF *f[2]; 116 ARRAY a[2]; 117 ARRAY **hdiff; 118 int ndiff; /* number of hunks */ 119 DSRC dsrc; /* data source: memory or temporary file */ 120 121 int view_quit:1; /* Quit flag */ 122 123 int height; 124 int half1; 125 int half2; 126 int width1; 127 int width2; 128 int bias; 129 int new_frame; 130 int skip_rows; 131 int skip_cols; 132 int display_symbols; 133 int display_numbers; 134 int show_cr; 135 int show_hdiff; 136 int tab_size; 137 int ord; 138 int full; 139 int last_found; 140 141 struct { 142 int quality; 143 int strip_trailing_cr; 144 int ignore_tab_expansion; 145 int ignore_space_change; 146 int ignore_all_space; 147 int ignore_case; 148 } opt; 149 } WDiff; 150 151 152 #define OPTX 50 153 #define OPTY 12 154 155 static const char *quality_str[] = { 156 N_("&Normal"), 157 N_("&Fastest"), 158 N_("&Minimal") 159 }; 160 161 static QuickWidget diffopt_widgets[] = { 162 { quick_button, 6, 10, 9, OPTY, N_("&Cancel"), 0, B_CANCEL, NULL, NULL, NULL }, 163 { quick_button, 3, 10, 9, OPTY, N_("&OK"), 0, B_ENTER, NULL, NULL, NULL }, 164 { quick_radio, 34, OPTX, 4, OPTY, "", 3, 2, NULL, const_cast(char **, quality_str), NULL }, 165 { quick_checkbox, 4, OPTX, 7, OPTY, N_("strip trailing &CR"), 0, 0, NULL, NULL, NULL }, 166 { quick_checkbox, 4, OPTX, 6, OPTY, N_("ignore all &Whitespace"), 0, 0, NULL, NULL, NULL }, 167 { quick_checkbox, 4, OPTX, 5, OPTY, N_("ignore &Space change"), 0, 0, NULL, NULL, NULL }, 168 { quick_checkbox, 4, OPTX, 4, OPTY, N_("ignore tab &Expansion"), 0, 0, NULL, NULL, NULL }, 169 { quick_checkbox, 4, OPTX, 3, OPTY, N_("&Ignore case"), 0, 0, NULL, NULL, NULL }, 170 NULL_QuickWidget 171 }; 172 173 static QuickDialog diffopt = { 174 OPTX, OPTY, -1, -1, 175 N_(" Diff Options "), "[Diff Options]", 176 diffopt_widgets, 0 177 }; 178 179 #define SEARCH_DLG_WIDTH 58 180 #define SEARCH_DLG_HEIGHT 10 181 182 static const char *search_str[] = { 183 N_("&Normal") 184 }; 185 186 static QuickWidget search_widgets[] = { 187 { quick_button, 6, 10, 7, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, NULL, NULL, NULL }, 188 { quick_button, 2, 10, 7, SEARCH_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, NULL, NULL, NULL }, 189 { quick_checkbox, 33, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("&Whole words only"), 0, 0, NULL, NULL, NULL }, 190 { quick_checkbox, 33, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Backwards"), 0, 0, NULL, NULL, NULL }, 191 { quick_checkbox, 33, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, N_("case &Sensitive"), 0, 0, NULL, NULL, NULL }, 192 { quick_radio, 4, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, "", 1, 0, NULL, const_cast(char **, search_str), NULL }, 193 { quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0, NULL, NULL, "diff-search" }, 194 { quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, NULL, NULL, NULL }, 195 NULL_QuickWidget 196 }; 197 198 static QuickDialog search_input = { 199 SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, 200 N_("Search"), "[Input Line Keys]", 201 search_widgets, 0 202 }; 203 204 static const char *wholechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; 205 206 #define error_dialog(h, s) query_dialog(h, s, D_ERROR, 1, _("&Dismiss")) 207 208 209 /* array *********************************************************************/ 210 211 212 /** 213 * Initialize array. 214 * 215 * \param a array, must be non-NULL 216 * \param eltsize element size 217 * \param growth growth constant 218 */ 219 static void 220 arr_init (ARRAY *a, int eltsize, int growth) 221 { 222 a->len = a->max = 0; 223 a->data = NULL; 224 a->error = 0; 225 a->eltsize = eltsize; 226 a->growth = growth; 227 } 228 229 230 /** 231 * Reset array length without dealocating storage. 232 * 233 * \param a array, must be non-NULL 234 */ 235 static void 236 arr_reset (ARRAY *a) 237 { 238 if (a->error) { 239 return; 240 } 241 a->len = 0; 242 } 243 244 245 /** 246 * Enlarge array. 247 * 248 * \param a array, must be non-NULL 249 * 250 * \return new element, or NULL if error 251 */ 252 static void * 253 arr_enlarge (ARRAY *a) 254 { 255 void *p; 256 if (a->error) { 257 return NULL; 258 } 259 if (a->len == a->max) { 260 int max = a->max + a->growth; 261 p = realloc(a->data, max * a->eltsize); 262 if (p == NULL) { 263 a->error = 1; 264 return NULL; 265 } 266 a->max = max; 267 a->data = p; 268 } 269 p = (void *)((char *)a->data + a->eltsize * a->len++); 270 return p; 271 } 272 273 274 /** 275 * Free array. 276 * 277 * \param a array, must be non-NULL 278 * \param func function to be called on each element 279 */ 280 static void 281 arr_free (ARRAY *a, void (*func) (void *)) 282 { 283 if (func != NULL) { 284 int i; 285 for (i = 0; i < a->len; i++) { 286 func((void *)((char *)a->data + a->eltsize * i)); 287 } 288 } 289 free(a->data); 290 arr_init(a, a->eltsize, a->growth); 291 } 292 293 294 /* buffered I/O **************************************************************/ 295 296 297 #define FILE_DIRTY(fs) \ 298 do { \ 299 (fs)->pos = 0; \ 300 (fs)->len = 0; \ 301 } while (0) 302 303 304 /** 305 * Try to open a temporary file. 306 * 307 * \param[out] name address of a pointer to store the temporary name 308 * 309 * \return file descriptor on success, negative on error 310 * 311 * \note the name is not altered if this function fails 312 * \note tries mc_tmpdir() and then current directory 313 */ 314 static int 315 open_temp (void **name) 316 { 317 int fd; 318 int len; 319 char *temp; 320 const char *pattern = "mcdiffXXXXXX"; 321 const char *env = mc_tmpdir(); 322 323 if (env == NULL) { 324 env = ""; 325 } 326 327 len = strlen(env); 328 temp = malloc(len + 1 + strlen(pattern) + 1); 329 if (temp == NULL) { 330 return -1; 331 } 332 333 if (len) { 334 strcpy(temp, env); 335 if (temp[len - 1] != PATH_SEP) { 336 temp[len++] = PATH_SEP; 337 } 338 } 339 strcpy(temp + len, pattern); 340 341 fd = mkstemp(temp); 342 if (fd < 0) { 343 if (len) { 344 strcpy(temp, pattern); 345 fd = mkstemp(temp); 346 } 347 if (fd < 0) { 348 free(temp); 349 return -1; 350 } 351 } 352 353 *name = temp; 354 return fd; 355 } 356 357 358 /** 359 * Alocate file structure and associate file descriptor to it. 360 * 361 * \param fd file descriptor 362 * 363 * \return file structure 364 */ 365 static FBUF * 366 f_dopen (int fd) 367 { 368 FBUF *fs; 369 370 if (fd < 0) { 371 return NULL; 372 } 373 374 fs = malloc(sizeof(FBUF)); 375 if (fs == NULL) { 376 return NULL; 377 } 378 379 fs->buf = malloc(FILE_READ_BUF); 380 if (fs->buf == NULL) { 381 free(fs); 382 return NULL; 383 } 384 385 fs->fd = fd; 386 FILE_DIRTY(fs); 387 fs->flags = 0; 388 fs->data = NULL; 389 390 return fs; 391 } 392 393 394 /** 395 * Free file structure without closing the file. 396 * 397 * \param fs file structure 398 * 399 * \return 0 on success, non-zero on error 400 */ 401 static int 402 f_free (FBUF *fs) 403 { 404 int rv = 0; 405 if (fs->flags & FILE_FLAG_TEMP) { 406 rv = unlink(fs->data); 407 free(fs->data); 408 } 409 free(fs->buf); 410 free(fs); 411 return rv; 412 } 413 414 415 /** 416 * Open a binary temporary file in R/W mode. 417 * 418 * \return file structure 419 * 420 * \note the file will be deleted when closed 421 */ 422 static FBUF * 423 f_temp (void) 424 { 425 int fd; 426 FBUF *fs; 427 428 fs = f_dopen(0); 429 if (fs == NULL) { 430 return NULL; 431 } 432 433 fd = open_temp(&fs->data); 434 if (fd < 0) { 435 f_free(fs); 436 return NULL; 437 } 438 439 fs->fd = fd; 440 fs->flags = FILE_FLAG_TEMP; 441 return fs; 442 } 443 444 445 /** 446 * Open a binary file in specified mode. 447 * 448 * \param filename file name 449 * \param flags open mode, a combination of O_RDONLY, O_WRONLY, O_RDWR 450 * 451 * \return file structure 452 */ 453 static FBUF * 454 f_open (const char *filename, int flags) 455 { 456 int fd; 457 FBUF *fs; 458 459 fs = f_dopen(0); 460 if (fs == NULL) { 461 return NULL; 462 } 463 464 fd = open(filename, flags); 465 if (fd < 0) { 466 f_free(fs); 467 return NULL; 468 } 469 470 fs->fd = fd; 471 return fs; 472 } 473 474 475 /** 476 * Read a line of bytes from file until newline or EOF. 477 * 478 * \param buf destination buffer 479 * \param size size of buffer 480 * \param fs file structure 481 * 482 * \return number of bytes read 483 * 484 * \note does not stop on null-byte 485 * \note buf will not be null-terminated 486 */ 487 static size_t 488 f_gets (char *buf, size_t size, FBUF *fs) 489 { 490 size_t j = 0; 491 492 do { 493 int i; 494 int stop = 0; 495 496 for (i = fs->pos; j < size && i < fs->len && !stop; i++, j++) { 497 buf[j] = fs->buf[i]; 498 if (buf[j] == '\n') { 499 stop = 1; 500 } 501 } 502 fs->pos = i; 503 504 if (j == size || stop) { 505 break; 506 } 507 508 fs->pos = 0; 509 fs->len = read(fs->fd, fs->buf, FILE_READ_BUF); 510 } while (fs->len > 0); 511 512 return j; 513 } 514 515 516 /** 517 * Read one character from file. 518 * 519 * \param fs file structure 520 * 521 * \return character 522 */ 523 static int 524 f_getc (FBUF *fs) 525 { 526 do { 527 if (fs->pos < fs->len) { 528 return (unsigned char)fs->buf[fs->pos++]; 529 } 530 531 fs->pos = 0; 532 fs->len = read(fs->fd, fs->buf, FILE_READ_BUF); 533 } while (fs->len > 0); 534 535 return -1; 536 } 537 538 539 /** 540 * Seek into file. 541 * 542 * \param fs file structure 543 * \param off offset 544 * \param whence seek directive: SEEK_SET, SEEK_CUR or SEEK_END 545 * 546 * \return position in file, starting from begginning 547 * 548 * \note avoids thrashing read cache when possible 549 */ 550 static off_t 551 f_seek (FBUF *fs, off_t off, int whence) 552 { 553 off_t rv; 554 555 if (fs->len && whence != SEEK_END) { 556 rv = lseek(fs->fd, 0, SEEK_CUR); 557 if (rv != -1) { 558 if (whence == SEEK_CUR) { 559 whence = SEEK_SET; 560 off += rv - fs->len + fs->pos; 561 } 562 if (off - rv >= -fs->len && off - rv <= 0) { 563 fs->pos = fs->len + off - rv; 564 return off; 565 } 566 } 567 } 568 569 rv = lseek(fs->fd, off, whence); 570 if (rv != -1) { 571 FILE_DIRTY(fs); 572 } 573 return rv; 574 } 575 576 577 /** 578 * Seek to the beginning of file, thrashing read cache. 579 * 580 * \param fs file structure 581 * 582 * \return 0 if success, non-zero on error 583 */ 584 static off_t 585 f_reset (FBUF *fs) 586 { 587 off_t rv = lseek(fs->fd, 0, SEEK_SET); 588 if (rv != -1) { 589 FILE_DIRTY(fs); 590 } 591 return rv; 592 } 593 594 595 /** 596 * Write bytes to file. 597 * 598 * \param fs file structure 599 * \param buf source buffer 600 * \param size size of buffer 601 * 602 * \return number of written bytes, -1 on error 603 * 604 * \note thrashes read cache 605 */ 606 static ssize_t 607 f_write (FBUF *fs, const char *buf, size_t size) 608 { 609 ssize_t rv = write(fs->fd, buf, size); 610 if (rv >= 0) { 611 FILE_DIRTY(fs); 612 } 613 return rv; 614 } 615 616 617 /** 618 * Truncate file to the current position. 619 * 620 * \param fs file structure 621 * 622 * \return current file size on success, negative on error 623 * 624 * \note thrashes read cache 625 */ 626 static off_t 627 f_trunc (FBUF *fs) 628 { 629 off_t off = lseek(fs->fd, 0, SEEK_CUR); 630 if (off != -1) { 631 int rv = ftruncate(fs->fd, off); 632 if (rv != 0) { 633 off = -1; 634 } else { 635 FILE_DIRTY(fs); 636 } 637 } 638 return off; 639 } 640 641 642 /** 643 * Close file. 644 * 645 * \param fs file structure 646 * 647 * \return 0 on success, non-zero on error 648 * 649 * \note if this is temporary file, it is deleted 650 */ 651 static int 652 f_close (FBUF *fs) 653 { 654 int rv = close(fs->fd); 655 f_free(fs); 656 return rv; 657 } 658 659 660 /** 661 * Create pipe stream to process. 662 * 663 * \param cmd shell command line 664 * \param flags open mode, either O_RDONLY or O_WRONLY 665 * 666 * \return file structure 667 */ 668 static FBUF * 669 p_open (const char *cmd, int flags) 670 { 671 FILE *f; 672 FBUF *fs; 673 const char *type = NULL; 674 675 if (flags == O_RDONLY) { 676 type = "r"; 677 } 678 if (flags == O_WRONLY) { 679 type = "w"; 680 } 681 682 if (type == NULL) { 683 return NULL; 684 } 685 686 fs = f_dopen(0); 687 if (fs == NULL) { 688 return NULL; 689 } 690 691 f = popen(cmd, type); 692 if (f == NULL) { 693 f_free(fs); 694 return NULL; 695 } 696 697 fs->fd = fileno(f); 698 fs->data = f; 699 return fs; 700 } 701 702 703 /** 704 * Close pipe stream. 705 * 706 * \param fs structure 707 * 708 * \return 0 on success, non-zero on error 709 */ 710 static int 711 p_close (FBUF *fs) 712 { 713 int rv = pclose(fs->data); 714 f_free(fs); 715 return rv; 716 } 717 718 719 /* diff parse ****************************************************************/ 720 721 722 /** 723 * Read decimal number from string. 724 * 725 * \param[in,out] str string to parse 726 * \param[out] n extracted number 727 * 728 * \return 0 if success, otherwise non-zero 729 */ 730 static int 731 scan_deci (const char **str, int *n) 732 { 733 const char *p = *str; 734 char *q; 735 errno = 0; 736 *n = strtol(p, &q, 10); 737 if (errno || p == q) { 738 return -1; 739 } 740 *str = q; 741 return 0; 742 } 743 744 745 /** 746 * Parse line for diff statement. 747 * 748 * \param p string to parse 749 * \param ops list of diff statements 750 * 751 * \return 0 if success, otherwise non-zero 752 */ 753 static int 754 scan_line (const char *p, ARRAY *ops) 755 { 756 DIFFCMD *op; 757 758 int f1, f2; 759 int t1, t2; 760 int cmd; 761 762 int range; 763 764 /* handle the following cases: 765 * NUMaNUM[,NUM] 766 * NUM[,NUM]cNUM[,NUM] 767 * NUM[,NUM]dNUM 768 * where NUM is a positive integer 769 */ 770 771 if (scan_deci(&p, &f1) != 0 || f1 < 0) { 772 return -1; 773 } 774 f2 = f1; 775 range = 0; 776 if (*p == ',') { 777 p++; 778 if (scan_deci(&p, &f2) != 0 || f2 < f1) { 779 return -1; 780 } 781 range = 1; 782 } 783 784 cmd = *p++; 785 if (cmd == 'a') { 786 if (range) { 787 return -1; 788 } 789 } else if (cmd != 'c' && cmd != 'd') { 790 return -1; 791 } 792 793 if (scan_deci(&p, &t1) != 0 || t1 < 0) { 794 return -1; 795 } 796 t2 = t1; 797 range = 0; 798 if (*p == ',') { 799 p++; 800 if (scan_deci(&p, &t2) != 0 || t2 < t1) { 801 return -1; 802 } 803 range = 1; 804 } 805 806 if (cmd == 'd') { 807 if (range) { 808 return -1; 809 } 810 } 811 812 op = arr_enlarge(ops); 813 if (op == NULL) { 814 return -1; 815 } 816 op->a[0][0] = f1; 817 op->a[0][1] = f2; 818 op->cmd = cmd; 819 op->a[1][0] = t1; 820 op->a[1][1] = t2; 821 return 0; 822 } 823 824 825 /** 826 * Parse diff output and extract diff statements. 827 * 828 * \param f stream to read from 829 * \param ops list of diff statements to fill 830 * 831 * \return positive number indicating number of hunks, otherwise negative 832 */ 833 static int 834 scan_diff (FBUF *f, ARRAY *ops) 835 { 836 int sz; 837 char buf[BUFSIZ]; 838 839 while ((sz = f_gets(buf, sizeof(buf) - 1, f))) { 840 if (isdigit(buf[0])) { 841 if (buf[sz - 1] != '\n') { 842 return -1; 843 } 844 buf[sz] = '\0'; 845 if (scan_line(buf, ops) != 0) { 846 return -1; 847 } 848 continue; 849 } 850 while (buf[sz - 1] != '\n' && (sz = f_gets(buf, sizeof(buf), f))) { 851 } 852 } 853 854 return ops->len; 855 } 856 857 858 /** 859 * Invoke diff and extract diff statements. 860 * 861 * \param args extra arguments to be passed to diff 862 * \param extra more arguments to be passed to diff 863 * \param file1 first file to compare 864 * \param file2 second file to compare 865 * \param ops list of diff statements to fill 866 * 867 * \return positive number indicating number of hunks, otherwise negative 868 */ 869 static int 870 dff_execute (const char *args, const char *extra, const char *file1, const char *file2, ARRAY *ops) 871 { 872 static const char *opt = 873 " --old-group-format='%df%(f=l?:,%dl)d%dE\n'" 874 " --new-group-format='%dea%dF%(F=L?:,%dL)\n'" 875 " --changed-group-format='%df%(f=l?:,%dl)c%dF%(F=L?:,%dL)\n'" 876 " --unchanged-group-format=''"; 877 878 int rv; 879 FBUF *f; 880 char *cmd; 881 int code; 882 883 cmd = malloc(14 + strlen(args) + strlen(extra) + strlen(opt) + strlen(file1) + strlen(file2)); 884 if (cmd == NULL) { 885 return -1; 886 } 887 sprintf(cmd, "diff %s %s %s \"%s\" \"%s\"", args, extra, opt, file1, file2); 888 889 f = p_open(cmd, O_RDONLY); 890 free(cmd); 891 if (f == NULL) { 892 return -1; 893 } 894 895 arr_init(ops, sizeof(DIFFCMD), 64); 896 rv = scan_diff(f, ops); 897 code = p_close(f); 898 899 if (rv < 0 || code == -1 || !WIFEXITED(code) || WEXITSTATUS(code) == 2) { 900 arr_free(ops, NULL); 901 return -1; 902 } 903 904 return rv; 905 } 906 907 908 /** 909 * Reparse and display file according to diff statements. 910 * 911 * \param ord 0 if displaying first file, 1 if displaying 2nd file 912 * \param filename file name to display 913 * \param ops list of diff statements 914 * \param printer printf-like function to be used for displaying 915 * \param ctx printer context 916 * 917 * \return 0 if success, otherwise non-zero 918 */ 919 static int 920 dff_reparse (int ord, const char *filename, const ARRAY *ops, DFUNC printer, void *ctx) 921 { 922 int i; 923 FBUF *f; 924 size_t sz; 925 char buf[BUFSIZ]; 926 int line = 0; 927 off_t off = 0; 928 const DIFFCMD *op; 929 int eff, tee; 930 int add_cmd; 931 int del_cmd; 932 933 f = f_open(filename, O_RDONLY); 934 if (f == NULL) { 935 return -1; 936 } 937 938 ord &= 1; 939 eff = ord; 940 tee = ord ^ 1; 941 942 add_cmd = 'a'; 943 del_cmd = 'd'; 944 if (ord) { 945 add_cmd = 'd'; 946 del_cmd = 'a'; 947 } 948 949 #define F1 a[eff][0] 950 #define F2 a[eff][1] 951 #define T1 a[tee][0] 952 #define T2 a[tee][1] 953 for (op = ops->data, i = 0; i < ops->len; i++, op++) { 954 int n = op->F1 - (op->cmd != add_cmd); 955 while (line < n && (sz = f_gets(buf, sizeof(buf), f))) { 956 line++; 957 printer(ctx, EQU_CH, line, off, sz, buf); 958 off += sz; 959 while (buf[sz - 1] != '\n') { 960 if (!(sz = f_gets(buf, sizeof(buf), f))) { 961 printer(ctx, 0, 0, 0, 1, "\n"); 962 break; 963 } 964 printer(ctx, 0, 0, 0, sz, buf); 965 off += sz; 966 } 967 } 968 if (line != n) { 969 goto err; 970 } 971 972 if (op->cmd == add_cmd) { 973 n = op->T2 - op->T1 + 1; 974 while (n) { 975 printer(ctx, DEL_CH, 0, 0, 1, "\n"); 976 n--; 977 } 978 } 979 if (op->cmd == del_cmd) { 980 n = op->F2 - op->F1 + 1; 981 while (n && (sz = f_gets(buf, sizeof(buf), f))) { 982 line++; 983 printer(ctx, ADD_CH, line, off, sz, buf); 984 off += sz; 985 while (buf[sz - 1] != '\n') { 986 if (!(sz = f_gets(buf, sizeof(buf), f))) { 987 printer(ctx, 0, 0, 0, 1, "\n"); 988 break; 989 } 990 printer(ctx, 0, 0, 0, sz, buf); 991 off += sz; 992 } 993 n--; 994 } 995 if (n) { 996 goto err; 997 } 998 } 999 if (op->cmd == 'c') { 1000 n = op->F2 - op->F1 + 1; 1001 while (n && (sz = f_gets(buf, sizeof(buf), f))) { 1002 line++; 1003 printer(ctx, CHG_CH, line, off, sz, buf); 1004 off += sz; 1005 while (buf[sz - 1] != '\n') { 1006 if (!(sz = f_gets(buf, sizeof(buf), f))) { 1007 printer(ctx, 0, 0, 0, 1, "\n"); 1008 break; 1009 } 1010 printer(ctx, 0, 0, 0, sz, buf); 1011 off += sz; 1012 } 1013 n--; 1014 } 1015 if (n) { 1016 goto err; 1017 } 1018 n = op->T2 - op->T1 - (op->F2 - op->F1); 1019 while (n > 0) { 1020 printer(ctx, CHG_CH, 0, 0, 1, "\n"); 1021 n--; 1022 } 1023 } 1024 } 1025 #undef T2 1026 #undef T1 1027 #undef F2 1028 #undef F1 1029 1030 while ((sz = f_gets(buf, sizeof(buf), f))) { 1031 line++; 1032 printer(ctx, EQU_CH, line, off, sz, buf); 1033 off += sz; 1034 while (buf[sz - 1] != '\n') { 1035 if (!(sz = f_gets(buf, sizeof(buf), f))) { 1036 printer(ctx, 0, 0, 0, 1, "\n"); 1037 break; 1038 } 1039 printer(ctx, 0, 0, 0, sz, buf); 1040 off += sz; 1041 } 1042 } 1043 1044 f_close(f); 1045 return 0; 1046 1047 err: 1048 f_close(f); 1049 return -1; 1050 } 1051 1052 1053 /* horizontal diff ***********************************************************/ 1054 1055 1056 /** 1057 * Longest common substring. 1058 * 1059 * \param s first string 1060 * \param m length of first string 1061 * \param t second string 1062 * \param n length of second string 1063 * \param ret list of offsets for longest common substrings inside each string 1064 * \param min minimum length of common substrings 1065 * 1066 * \return 0 if success, nonzero otherwise 1067 */ 1068 static int 1069 lcsubstr (const char *s, int m, const char *t, int n, ARRAY *ret, int min) 1070 { 1071 int i, j; 1072 1073 int *Lprev, *Lcurr; 1074 1075 int z = 0; 1076 1077 arr_init(ret, sizeof(PAIR), 4); 1078 1079 if (m < min || n < min) { 1080 /* XXX early culling */ 1081 return 0; 1082 } 1083 1084 Lprev = calloc(n + 1, sizeof(int)); 1085 if (Lprev == NULL) { 1086 goto err_0; 1087 } 1088 Lcurr = calloc(n + 1, sizeof(int)); 1089 if (Lcurr == NULL) { 1090 goto err_1; 1091 } 1092 1093 for (i = 0; i < m; i++) { 1094 int *L = Lprev; 1095 Lprev = Lcurr; 1096 Lcurr = L; 1097 #ifdef USE_MEMSET_IN_LCS 1098 memset(Lcurr, 0, (n + 1) * sizeof(int)); 1099 #endif 1100 for (j = 0; j < n; j++) { 1101 #ifndef USE_MEMSET_IN_LCS 1102 Lcurr[j + 1] = 0; 1103 #endif 1104 if (s[i] == t[j]) { 1105 int v = Lprev[j] + 1; 1106 Lcurr[j + 1] = v; 1107 if (z < v) { 1108 z = v; 1109 arr_reset(ret); 1110 } 1111 if (z == v && z >= min) { 1112 int off0 = i - z + 1; 1113 int off1 = j - z + 1; 1114 int k; 1115 PAIR *p; 1116 for (p = ret->data, k = 0; k < ret->len; k++, p++) { 1117 if ((*p)[0] == off0) { 1118 break; 1119 } 1120 if ((*p)[1] == off1) { 1121 break; 1122 } 1123 } 1124 if (k == ret->len) { 1125 p = arr_enlarge(ret); 1126 if (p == NULL) { 1127 goto err_2; 1128 } 1129 (*p)[0] = off0; 1130 (*p)[1] = off1; 1131 } 1132 } 1133 } 1134 } 1135 } 1136 1137 free(Lcurr); 1138 free(Lprev); 1139 return z; 1140 1141 err_2: 1142 free(Lcurr); 1143 err_1: 1144 free(Lprev); 1145 err_0: 1146 arr_free(ret, NULL); 1147 return -1; 1148 } 1149 1150 1151 /** 1152 * Scan recursively for common substrings and build ranges. 1153 * 1154 * \param s first string 1155 * \param t second string 1156 * \param bracket current limits for both of the strings 1157 * \param min minimum length of common substrings 1158 * \param hdiff list of horizontal diff ranges to fill 1159 * \param depth recursion depth 1160 * 1161 * \return 0 if success, nonzero otherwise 1162 */ 1163 static int 1164 hdiff_multi (const char *s, const char *t, const BRACKET bracket, int min, ARRAY *hdiff, unsigned int depth) 1165 { 1166 BRACKET *p; 1167 1168 if (depth--) { 1169 ARRAY ret; 1170 BRACKET b; 1171 int len = lcsubstr(s + bracket[0].off, bracket[0].len, 1172 t + bracket[1].off, bracket[1].len, &ret, min); 1173 if (ret.len) { 1174 int k = 0; 1175 const PAIR *data = ret.data; 1176 1177 b[0].off = bracket[0].off; 1178 b[0].len = data[k][0]; 1179 b[1].off = bracket[1].off; 1180 b[1].len = data[k][1]; 1181 hdiff_multi(s, t, b, min, hdiff, depth); 1182 1183 for (k = 0; k < ret.len - 1; k++) { 1184 b[0].off = bracket[0].off + data[k][0] + len; 1185 b[0].len = data[k + 1][0] - data[k][0] - len; 1186 b[1].off = bracket[1].off + data[k][1] + len; 1187 b[1].len = data[k + 1][1] - data[k][1] - len; 1188 hdiff_multi(s, t, b, min, hdiff, depth); 1189 } 1190 1191 b[0].off = bracket[0].off + data[k][0] + len; 1192 b[0].len = bracket[0].len - data[k][0] - len; 1193 b[1].off = bracket[1].off + data[k][1] + len; 1194 b[1].len = bracket[1].len - data[k][1] - len; 1195 hdiff_multi(s, t, b, min, hdiff, depth); 1196 1197 arr_free(&ret, NULL); 1198 return 0; 1199 } 1200 } 1201 1202 p = arr_enlarge(hdiff); 1203 if (p == NULL) { 1204 return -1; 1205 } 1206 (*p)[0].off = bracket[0].off; 1207 (*p)[0].len = bracket[0].len; 1208 (*p)[1].off = bracket[1].off; 1209 (*p)[1].len = bracket[1].len; 1210 1211 return 0; 1212 } 1213 1214 1215 /** 1216 * Build list of horizontal diff ranges. 1217 * 1218 * \param s first string 1219 * \param m length of first string 1220 * \param t second string 1221 * \param n length of second string 1222 * \param min minimum length of common substrings 1223 * \param hdiff list of horizontal diff ranges to fill 1224 * \param depth recursion depth 1225 * 1226 * \return 0 if success, nonzero otherwise 1227 */ 1228 static int 1229 hdiff_scan (const char *s, int m, const char *t, int n, int min, ARRAY *hdiff, unsigned int depth) 1230 { 1231 int i; 1232 BRACKET b; 1233 1234 /* dumbscan (single horizontal diff) -- does not compress whitespace */ 1235 1236 for (i = 0; i < m && i < n && s[i] == t[i]; i++) { 1237 } 1238 for (; m > i && n > i && s[m - 1] == t[n - 1]; m--, n--) { 1239 } 1240 b[0].off = i; 1241 b[0].len = m - i; 1242 b[1].off = i; 1243 b[1].len = n - i; 1244 1245 /* smartscan (multiple horizontal diff) */ 1246 1247 arr_init(hdiff, sizeof(BRACKET), 4); 1248 hdiff_multi(s, t, b, min, hdiff, depth); 1249 if (hdiff->error) { 1250 arr_free(hdiff, NULL); 1251 return -1; 1252 } 1253 1254 return 0; 1255 } 1256 1257 1258 /* read line *****************************************************************/ 1259 1260 1261 /** 1262 * Check if character is inside horizontal diff limits. 1263 * 1264 * \param k rank of character inside line 1265 * \param hdiff horizontal diff structure 1266 * \param ord 0 if reading from first file, 1 if reading from 2nd file 1267 * 1268 * \return TRUE if inside hdiff limits, FALSE otherwise 1269 */ 1270 static int 1271 is_inside (int k, ARRAY *hdiff, int ord) 1272 { 1273 int i; 1274 BRACKET *b; 1275 for (b = hdiff->data, i = 0; i < hdiff->len; i++, b++) { 1276 int start = (*b)[ord].off; 1277 int end = start + (*b)[ord].len; 1278 if (k >= start && k < end) { 1279 return 1; 1280 } 1281 } 1282 return 0; 1283 } 1284 1285 1286 /** 1287 * Copy `src' to `dst' expanding tabs. 1288 * 1289 * \param dst destination buffer 1290 * \param src source buffer 1291 * \param srcsize size of src buffer 1292 * \param base virtual base of this string, needed to calculate tabs 1293 * \param ts tab size 1294 * 1295 * \return new virtual base 1296 * 1297 * \note The procedure returns when all bytes are consumed from `src' 1298 */ 1299 static int 1300 cvt_cpy (char *dst, const char *src, size_t srcsize, int base, int ts) 1301 { 1302 int i; 1303 for (i = 0; srcsize; i++, src++, dst++, srcsize--) { 1304 *dst = *src; 1305 if (*src == '\t') { 1306 int j = TAB_SKIP(ts, i + base); 1307 i += j - 1; 1308 while (j-- > 0) { 1309 *dst++ = ' '; 1310 } 1311 dst--; 1312 } 1313 } 1314 return i + base; 1315 } 1316 1317 1318 /** 1319 * Copy `src' to `dst' expanding tabs. 1320 * 1321 * \param dst destination buffer 1322 * \param dstsize size of dst buffer 1323 * \param[in,out] _src source buffer 1324 * \param srcsize size of src buffer 1325 * \param base virtual base of this string, needed to calculate tabs 1326 * \param ts tab size 1327 * 1328 * \return new virtual base 1329 * 1330 * \note The procedure returns when all bytes are consumed from `src' 1331 * or `dstsize' bytes are written to `dst' 1332 * \note Upon return, `src' points to the first unwritten character in source 1333 */ 1334 static int 1335 cvt_ncpy (char *dst, int dstsize, const char **_src, size_t srcsize, int base, int ts) 1336 { 1337 int i; 1338 const char *src = *_src; 1339 for (i = 0; i < dstsize && srcsize; i++, src++, dst++, srcsize--) { 1340 *dst = *src; 1341 if (*src == '\t') { 1342 int j = TAB_SKIP(ts, i + base); 1343 if (j > dstsize - i) { 1344 j = dstsize - i; 1345 } 1346 i += j - 1; 1347 while (j-- > 0) { 1348 *dst++ = ' '; 1349 } 1350 dst--; 1351 } 1352 } 1353 *_src = src; 1354 return i + base; 1355 } 1356 1357 1358 /** 1359 * Read line from memory, converting tabs to spaces and padding with spaces. 1360 * 1361 * \param src buffer to read from 1362 * \param srcsize size of src buffer 1363 * \param dst buffer to read to 1364 * \param dstsize size of dst buffer, excluding trailing null 1365 * \param skip number of characters to skip 1366 * \param ts tab size 1367 * \param show_cr show trailing carriage return as ^M 1368 * 1369 * \return negative on error, otherwise number of bytes except padding 1370 */ 1371 static int 1372 cvt_mget (const char *src, size_t srcsize, char *dst, int dstsize, int skip, int ts, int show_cr) 1373 { 1374 int sz = 0; 1375 if (src != NULL) { 1376 int i; 1377 char *tmp = dst; 1378 const int base = 0; 1379 for (i = 0; dstsize && srcsize && *src != '\n'; i++, src++, srcsize--) { 1380 if (*src == '\t') { 1381 int j = TAB_SKIP(ts, i + base); 1382 i += j - 1; 1383 while (j-- > 0) { 1384 if (skip) { 1385 skip--; 1386 } else if (dstsize) { 1387 dstsize--; 1388 *dst++ = ' '; 1389 } 1390 } 1391 } else if (src[0] == '\r' && (srcsize == 1 || src[1] == '\n')) { 1392 if (!skip && show_cr) { 1393 if (dstsize > 1) { 1394 dstsize -= 2; 1395 *dst++ = '^'; 1396 *dst++ = 'M'; 1397 } else { 1398 dstsize--; 1399 *dst++ = '.'; 1400 } 1401 } 1402 break; 1403 } else { 1404 if (skip) { 1405 skip--; 1406 } else { 1407 dstsize--; 1408 *dst++ = is_printable(*src) ? *src : '.'; 1409 } 1410 } 1411 } 1412 sz = dst - tmp; 1413 } 1414 while (dstsize) { 1415 dstsize--; 1416 *dst++ = ' '; 1417 } 1418 *dst = '\0'; 1419 return sz; 1420 } 1421 1422 1423 /** 1424 * Read line from memory and build attribute array. 1425 * 1426 * \param src buffer to read from 1427 * \param srcsize size of src buffer 1428 * \param dst buffer to read to 1429 * \param dstsize size of dst buffer, excluding trailing null 1430 * \param skip number of characters to skip 1431 * \param ts tab size 1432 * \param show_cr show trailing carriage return as ^M 1433 * \param hdiff horizontal diff structure 1434 * \param ord 0 if reading from first file, 1 if reading from 2nd file 1435 * \param att buffer of attributes 1436 * 1437 * \return negative on error, otherwise number of bytes except padding 1438 */ 1439 static int 1440 cvt_mgeta (const char *src, size_t srcsize, char *dst, int dstsize, int skip, int ts, int show_cr, ARRAY *hdiff, int ord, char *att) 1441 { 1442 int sz = 0; 1443 if (src != NULL) { 1444 int i, k; 1445 char *tmp = dst; 1446 const int base = 0; 1447 for (i = 0, k = 0; dstsize && srcsize && *src != '\n'; i++, k++, src++, srcsize--) { 1448 if (*src == '\t') { 1449 int j = TAB_SKIP(ts, i + base); 1450 i += j - 1; 1451 while (j-- > 0) { 1452 if (skip) { 1453 skip--; 1454 } else if (dstsize) { 1455 dstsize--; 1456 *att++ = is_inside(k, hdiff, ord); 1457 *dst++ = ' '; 1458 } 1459 } 1460 } else if (src[0] == '\r' && (srcsize == 1 || src[1] == '\n')) { 1461 if (!skip && show_cr) { 1462 if (dstsize > 1) { 1463 dstsize -= 2; 1464 *att++ = is_inside(k, hdiff, ord); 1465 *dst++ = '^'; 1466 *att++ = is_inside(k, hdiff, ord); 1467 *dst++ = 'M'; 1468 } else { 1469 dstsize--; 1470 *att++ = is_inside(k, hdiff, ord); 1471 *dst++ = '.'; 1472 } 1473 } 1474 break; 1475 } else { 1476 if (skip) { 1477 skip--; 1478 } else { 1479 dstsize--; 1480 *att++ = is_inside(k, hdiff, ord); 1481 *dst++ = is_printable(*src) ? *src : '.'; 1482 } 1483 } 1484 } 1485 sz = dst - tmp; 1486 } 1487 while (dstsize) { 1488 dstsize--; 1489 *att++ = 0; 1490 *dst++ = ' '; 1491 } 1492 *dst = '\0'; 1493 return sz; 1494 } 1495 1496 1497 /** 1498 * Read line from file, converting tabs to spaces and padding with spaces. 1499 * 1500 * \param f file stream to read from 1501 * \param off offset of line inside file 1502 * \param dst buffer to read to 1503 * \param dstsize size of dst buffer, excluding trailing null 1504 * \param skip number of characters to skip 1505 * \param ts tab size 1506 * \param show_cr show trailing carriage return as ^M 1507 * 1508 * \return negative on error, otherwise number of bytes except padding 1509 */ 1510 static int 1511 cvt_fget (FBUF *f, off_t off, char *dst, int dstsize, int skip, int ts, int show_cr) 1512 { 1513 int base = 0; 1514 int old_base = base; 1515 const int amount = dstsize; 1516 1517 int useful; 1518 int offset; 1519 1520 ssize_t i; 1521 size_t sz; 1522 1523 int lastch = '\0'; 1524 1525 const char *q = NULL; 1526 char tmp[BUFSIZ]; /* XXX capacity must be >= max{dstsize + 1, amount} */ 1527 char cvt[BUFSIZ]; /* XXX capacity must be >= MAX_TAB_WIDTH * amount */ 1528 1529 if ((int)sizeof(tmp) < amount || (int)sizeof(tmp) <= dstsize || (int)sizeof(cvt) < 8 * amount) { 1530 /* abnormal, but avoid buffer overflow */ 1531 memset(dst, ' ', dstsize); 1532 dst[dstsize] = '\0'; 1533 return 0; 1534 } 1535 1536 f_seek(f, off, SEEK_SET); 1537 1538 while (skip > base) { 1539 old_base = base; 1540 if (!(sz = f_gets(tmp, amount, f))) { 1541 break; 1542 } 1543 base = cvt_cpy(cvt, tmp, sz, old_base, ts); 1544 if (cvt[base - old_base - 1] == '\n') { 1545 q = &cvt[base - old_base - 1]; 1546 base = old_base + q - cvt + 1; 1547 break; 1548 } 1549 } 1550 1551 useful = base - skip; 1552 offset = skip - old_base; 1553 1554 if (useful < 0) { 1555 memset(dst, ' ', dstsize); 1556 dst[dstsize] = '\0'; 1557 return 0; 1558 } 1559 1560 if (useful <= dstsize) { 1561 if (useful) { 1562 memmove(dst, cvt + offset, useful); 1563 } 1564 if (q == NULL && (sz = f_gets(tmp, dstsize - useful + 1, f))) { 1565 const char *ptr = tmp; 1566 useful += cvt_ncpy(dst + useful, dstsize - useful, &ptr, sz, base, ts) - base; 1567 if (ptr < tmp + sz) { 1568 lastch = *ptr; 1569 } 1570 } 1571 sz = useful; 1572 } else { 1573 memmove(dst, cvt + offset, dstsize); 1574 sz = dstsize; 1575 lastch = cvt[offset + dstsize]; 1576 } 1577 1578 dst[sz] = lastch; 1579 for (i = 0; i < sz && dst[i] != '\n'; i++) { 1580 if (dst[i] == '\r' && dst[i + 1] == '\n') { 1581 if (show_cr) { 1582 if (i + 1 < dstsize) { 1583 dst[i++] = '^'; 1584 dst[i++] = 'M'; 1585 } else { 1586 dst[i++] = '.'; 1587 } 1588 } 1589 break; 1590 } else if (!is_printable(dst[i])) { 1591 dst[i] = '.'; 1592 } 1593 } 1594 for (; i < dstsize; i++) { 1595 dst[i] = ' '; 1596 } 1597 dst[i] = '\0'; 1598 return sz; 1599 } 1600 1601 1602 /* diff printers et al *******************************************************/ 1603 1604 1605 static void 1606 cc_free_elt (void *elt) 1607 { 1608 DIFFLN *p = elt; 1609 if (p->p) { 1610 free(p->p); 1611 } 1612 } 1613 1614 1615 static int 1616 printer (void *ctx, int ch, int line, off_t off, size_t sz, const char *str) 1617 { 1618 DIFFLN *p; 1619 ARRAY *a = ((PRINTER_CTX *)ctx)->a; 1620 DSRC dsrc = ((PRINTER_CTX *)ctx)->dsrc; 1621 if (a->error) { 1622 return -1; 1623 } 1624 if (ch) { 1625 p = arr_enlarge(a); 1626 if (p == NULL) { 1627 return -1; 1628 } 1629 p->p = NULL; 1630 p->ch = ch; 1631 p->line = line; 1632 p->u.off = off; 1633 if (dsrc == DATA_SRC_MEM && line) { 1634 if (sz && str[sz - 1] == '\n') { 1635 sz--; 1636 } 1637 if (sz) { 1638 p->p = malloc(sz); 1639 if (p->p == NULL) { 1640 a->error = 1; 1641 return -1; 1642 } 1643 memcpy(p->p, str, sz); 1644 } 1645 p->u.len = sz; 1646 } 1647 } else if (dsrc == DATA_SRC_MEM) { 1648 if (!a->len) { 1649 a->error = 1; 1650 return -1; 1651 } 1652 p = (DIFFLN *)a->data + a->len - 1; 1653 if (sz && str[sz - 1] == '\n') { 1654 sz--; 1655 } 1656 if (sz) { 1657 size_t new_size = p->u.len + sz; 1658 char *q = realloc(p->p, new_size); 1659 if (q == NULL) { 1660 a->error = 1; 1661 return -1; 1662 } 1663 memcpy(q + p->u.len, str, sz); 1664 p->p = q; 1665 } 1666 p->u.len += sz; 1667 } 1668 if (dsrc == DATA_SRC_TMP && (line || !ch)) { 1669 FBUF *f = ((PRINTER_CTX *)ctx)->f; 1670 f_write(f, str, sz); 1671 } 1672 return 0; 1673 } 1674 1675 1676 static int 1677 redo_diff (WDiff *view) 1678 { 1679 FBUF *const *f = view->f; 1680 ARRAY *a = view->a; 1681 1682 PRINTER_CTX ctx; 1683 ARRAY ops; 1684 int ndiff; 1685 int rv; 1686 1687 char extra[256]; 1688 1689 extra[0] = '\0'; 1690 if (view->opt.quality == 2) { 1691 strcat(extra, " -d"); 1692 } 1693 if (view->opt.quality == 1) { 1694 strcat(extra, " --speed-large-files"); 1695 } 1696 if (view->opt.strip_trailing_cr) { 1697 strcat(extra, " --strip-trailing-cr"); 1698 } 1699 if (view->opt.ignore_tab_expansion) { 1700 strcat(extra, " -E"); 1701 } 1702 if (view->opt.ignore_space_change) { 1703 strcat(extra, " -b"); 1704 } 1705 if (view->opt.ignore_all_space) { 1706 strcat(extra, " -w"); 1707 } 1708 if (view->opt.ignore_case) { 1709 strcat(extra, " -i"); 1710 } 1711 1712 if (view->dsrc != DATA_SRC_MEM) { 1713 f_reset(f[0]); 1714 f_reset(f[1]); 1715 } 1716 1717 ndiff = dff_execute(view->args, extra, view->file[0], view->file[1], &ops); 1718 if (ndiff < 0) { 1719 return -1; 1720 } 1721 1722 ctx.dsrc = view->dsrc; 1723 1724 rv = 0; 1725 1726 arr_init(&a[0], sizeof(DIFFLN), 256); 1727 ctx.a = &a[0]; 1728 ctx.f = f[0]; 1729 rv |= dff_reparse(0, view->file[0], &ops, printer, &ctx); 1730 1731 arr_init(&a[1], sizeof(DIFFLN), 256); 1732 ctx.a = &a[1]; 1733 ctx.f = f[1]; 1734 rv |= dff_reparse(1, view->file[1], &ops, printer, &ctx); 1735 1736 arr_free(&ops, NULL); 1737 1738 if (rv || a[0].error || a[1].error || a[0].len != a[1].len) { 1739 arr_free(&a[0], cc_free_elt); 1740 arr_free(&a[1], cc_free_elt); 1741 return -1; 1742 } 1743 1744 if (view->dsrc == DATA_SRC_TMP) { 1745 f_trunc(f[0]); 1746 f_trunc(f[1]); 1747 } 1748 1749 if (view->dsrc == DATA_SRC_MEM && HDIFF_ENABLE) { 1750 view->hdiff = malloc(a[0].len * sizeof(ARRAY *)); 1751 if (view->hdiff != NULL) { 1752 int i; 1753 const DIFFLN *p; 1754 const DIFFLN *q; 1755 for (p = a[0].data, q = a[1].data, i = 0; i < a[0].len; i++, q++, p++) { 1756 ARRAY *h = NULL; 1757 if (p->line && q->line && p->ch == CHG_CH) { 1758 h = malloc(sizeof(ARRAY)); 1759 if (h != NULL) { 1760 int rv = hdiff_scan(p->p, p->u.len, q->p, q->u.len, HDIFF_MINCTX, h, HDIFF_DEPTH); 1761 if (rv != 0) { 1762 free(h); 1763 h = NULL; 1764 } 1765 } 1766 } 1767 view->hdiff[i] = h; 1768 } 1769 } 1770 } 1771 1772 return ndiff; 1773 } 1774 1775 1776 static void 1777 destroy_hdiff (WDiff *view) 1778 { 1779 if (view->hdiff != NULL) { 1780 int i; 1781 int len = view->a[0].len; 1782 for (i = 0; i < len; i++) { 1783 ARRAY *h = view->hdiff[i]; 1784 if (h != NULL) { 1785 arr_free(h, NULL); 1786 free(h); 1787 } 1788 } 1789 free(view->hdiff); 1790 view->hdiff = NULL; 1791 } 1792 } 1793 1794 1795 /* stuff *********************************************************************/ 1796 1797 1798 static int 1799 get_digits (unsigned int n) 1800 { 1801 int d = 1; 1802 while (n /= 10) { 1803 d++; 1804 } 1805 return d; 1806 } 1807 1808 1809 static int 1810 get_line_numbers (const ARRAY *a, int pos, int *linenum, int *lineofs) 1811 { 1812 const DIFFLN *p; 1813 1814 *linenum = 0; 1815 *lineofs = 0; 1816 1817 if (a->len) { 1818 if (pos >= a->len) { 1819 pos = a->len - 1; 1820 } 1821 1822 p = (DIFFLN *)a->data + pos; 1823 1824 if (!p->line) { 1825 int n; 1826 for (n = pos; n > 0; n--) { 1827 p--; 1828 if (p->line) { 1829 break; 1830 } 1831 } 1832 *lineofs = pos - n + 1; 1833 } 1834 1835 *linenum = p->line; 1836 } 1837 return 0; 1838 } 1839 1840 1841 static int 1842 calc_nwidth (const ARRAY *const a) 1843 { 1844 int l1, o1; 1845 int l2, o2; 1846 get_line_numbers(&a[0], a[0].len - 1, &l1, &o1); 1847 get_line_numbers(&a[1], a[1].len - 1, &l2, &o2); 1848 if (l1 < l2) { 1849 l1 = l2; 1850 } 1851 return get_digits(l1); 1852 } 1853 1854 1855 static int 1856 find_prev_hunk (const ARRAY *a, int pos) 1857 { 1858 #if 1 1859 while (pos > 0 && ((DIFFLN *)a->data)[pos].ch != EQU_CH) { 1860 pos--; 1861 } 1862 while (pos > 0 && ((DIFFLN *)a->data)[pos].ch == EQU_CH) { 1863 pos--; 1864 } 1865 #else 1866 while (pos > 0 && ((DIFFLN *)a->data)[pos - 1].ch == EQU_CH) { 1867 pos--; 1868 } 1869 while (pos > 0 && ((DIFFLN *)a->data)[pos - 1].ch != EQU_CH) { 1870 pos--; 1871 } 1872 #endif 1873 1874 return pos; 1875 } 1876 1877 1878 static int 1879 find_next_hunk (const ARRAY *a, int pos) 1880 { 1881 while (pos < a->len && ((DIFFLN *)a->data)[pos].ch != EQU_CH) { 1882 pos++; 1883 } 1884 while (pos < a->len && ((DIFFLN *)a->data)[pos].ch == EQU_CH) { 1885 pos++; 1886 } 1887 1888 return pos; 1889 } 1890 1891 1892 /* view routines and callbacks ***********************************************/ 1893 1894 1895 static void 1896 view_compute_split (WDiff *view, int i) 1897 { 1898 view->bias += i; 1899 if (view->bias < 2 - view->half1) { 1900 view->bias = 2 - view->half1; 1901 } 1902 if (view->bias > view->half2 - 2) { 1903 view->bias = view->half2 - 2; 1904 } 1905 } 1906 1907 1908 static void 1909 view_compute_areas (WDiff *view) 1910 { 1911 view->height = LINES - 2; 1912 view->half1 = COLS / 2; 1913 view->half2 = COLS - view->half1; 1914 1915 view_compute_split(view, 0); 1916 } 1917 1918 1919 static int 1920 view_init (WDiff *view, const char *args, const char *file1, const char *file2, const char *label1, const char *label2, DSRC dsrc) 1921 { 1922 int ndiff; 1923 FBUF *f[2]; 1924 1925 f[0] = NULL; 1926 f[1] = NULL; 1927 1928 if (dsrc == DATA_SRC_TMP) { 1929 f[0] = f_temp(); 1930 if (f[0] == NULL) { 1931 goto err_2; 1932 } 1933 f[1] = f_temp(); 1934 if (f[1] == NULL) { 1935 f_close(f[0]); 1936 goto err_2; 1937 } 1938 } 1939 if (dsrc == DATA_SRC_ORG) { 1940 f[0] = f_open(file1, O_RDONLY); 1941 if (f[0] == NULL) { 1942 goto err_2; 1943 } 1944 f[1] = f_open(file2, O_RDONLY); 1945 if (f[1] == NULL) { 1946 f_close(f[0]); 1947 goto err_2; 1948 } 1949 } 1950 1951 view->args = args; 1952 view->file[0] = file1; 1953 view->file[1] = file2; 1954 view->label[0] = label1; 1955 view->label[1] = label2; 1956 view->f[0] = f[0]; 1957 view->f[1] = f[1]; 1958 view->hdiff = NULL; 1959 view->dsrc = dsrc; 1960 1961 ndiff = redo_diff(view); 1962 if (ndiff < 0) { 1963 goto err_3; 1964 } 1965 1966 view->ndiff = ndiff; 1967 1968 view->view_quit = 0; 1969 1970 view->bias = 0; 1971 view->new_frame = 1; 1972 view->skip_rows = 0; 1973 view->skip_cols = 0; 1974 view->display_symbols = 0; 1975 view->display_numbers = 0; 1976 view->show_cr = 1; 1977 view->show_hdiff = 1; 1978 view->tab_size = 8; 1979 view->ord = 0; 1980 view->full = 0; 1981 view->last_found = -1; 1982 1983 view->opt.quality = 0; 1984 view->opt.strip_trailing_cr = 0; 1985 view->opt.ignore_tab_expansion = 0; 1986 view->opt.ignore_space_change = 0; 1987 view->opt.ignore_all_space = 0; 1988 view->opt.ignore_case = 0; 1989 1990 view_compute_areas(view); 1991 return 0; 1992 1993 err_3: 1994 if (dsrc != DATA_SRC_MEM) { 1995 f_close(f[1]); 1996 f_close(f[0]); 1997 } 1998 err_2: 1999 return -1; 2000 } 2001 2002 2003 static int 2004 view_reinit (WDiff *view) 2005 { 2006 int ndiff = view->ndiff; 2007 2008 diffopt_widgets[2].value = view->opt.quality; 2009 diffopt_widgets[2].result = &view->opt.quality; 2010 diffopt_widgets[3].result = &view->opt.strip_trailing_cr; 2011 diffopt_widgets[4].result = &view->opt.ignore_all_space; 2012 diffopt_widgets[5].result = &view->opt.ignore_space_change; 2013 diffopt_widgets[6].result = &view->opt.ignore_tab_expansion; 2014 diffopt_widgets[7].result = &view->opt.ignore_case; 2015 2016 if (quick_dialog(&diffopt) != B_CANCEL) { 2017 destroy_hdiff(view); 2018 arr_free(&view->a[1], cc_free_elt); 2019 arr_free(&view->a[0], cc_free_elt); 2020 ndiff = redo_diff(view); 2021 if (ndiff >= 0) { 2022 view->ndiff = ndiff; 2023 } 2024 } 2025 return ndiff; 2026 } 2027 2028 2029 static void 2030 view_fini (WDiff *view) 2031 { 2032 if (view->dsrc != DATA_SRC_MEM) { 2033 f_close(view->f[1]); 2034 f_close(view->f[0]); 2035 } 2036 2037 destroy_hdiff(view); 2038 arr_free(&view->a[1], cc_free_elt); 2039 arr_free(&view->a[0], cc_free_elt); 2040 } 2041 2042 2043 static int 2044 view_display_file (const WDiff *view, int ord, 2045 int r, int c, int height, int width) 2046 { 2047 int i, j, k; 2048 char buf[BUFSIZ]; 2049 FBUF *f = view->f[ord]; 2050 const ARRAY *a = &view->a[ord]; 2051 int skip = view->skip_cols; 2052 int display_symbols = view->display_symbols; 2053 int display_numbers = view->display_numbers; 2054 int show_cr = view->show_cr; 2055 int tab_size = view->tab_size; 2056 const DIFFLN *p; 2057 2058 int nwidth = display_numbers; 2059 int xwidth = display_symbols + display_numbers; 2060 2061 if (xwidth) { 2062 if (xwidth > width && display_symbols) { 2063 xwidth--; 2064 display_symbols = 0; 2065 } 2066 if (xwidth > width && display_numbers) { 2067 xwidth = width; 2068 display_numbers = width; 2069 } 2070 2071 xwidth++; 2072 2073 c += xwidth; 2074 width -= xwidth; 2075 2076 if (width < 0) { 2077 width = 0; 2078 } 2079 } 2080 2081 if ((int)sizeof(buf) <= width || (int)sizeof(buf) <= nwidth) { 2082 /* abnormal, but avoid buffer overflow */ 2083 return -1; 2084 } 2085 2086 for (i = view->skip_rows, j = 0, p = (DIFFLN *)a->data + i; i < a->len && j < height; p++, j++, i++) { 2087 int ch = p->ch; 2088 tty_setcolor(NORMAL_COLOR); 2089 if (display_symbols) { 2090 tty_gotoyx(r + j, c - 2); 2091 tty_print_char(ch); 2092 } 2093 if (p->line) { 2094 if (display_numbers) { 2095 tty_gotoyx(r + j, c - xwidth); 2096 snprintf(buf, display_numbers + 1, "%*d", nwidth, p->line); 2097 tty_print_string(buf); 2098 } 2099 if (ch == ADD_CH) { 2100 tty_setcolor(DFFADD_COLOR); 2101 } 2102 if (ch == CHG_CH) { 2103 tty_setcolor(DFFCHG_COLOR); 2104 } 2105 if (f == NULL) { 2106 if (i == view->last_found) { 2107 tty_setcolor(MARKED_SELECTED_COLOR); 2108 } else if (view->show_hdiff) { 2109 if (view->hdiff != NULL && view->hdiff[i] != NULL) { 2110 char att[BUFSIZ]; 2111 cvt_mgeta(p->p, p->u.len, buf, width, skip, tab_size, show_cr, view->hdiff[i], ord, att); 2112 tty_gotoyx(r + j, c); 2113 for (k = 0; k < width; k++) { 2114 tty_setcolor(att[k] ? DFFCHH_COLOR : DFFCHG_COLOR); 2115 tty_print_char(buf[k]); 2116 } 2117 continue; 2118 } else if (ch == CHG_CH) { 2119 tty_setcolor(DFFCHH_COLOR); 2120 } 2121 } 2122 cvt_mget(p->p, p->u.len, buf, width, skip, tab_size, show_cr); 2123 } else { 2124 cvt_fget(f, p->u.off, buf, width, skip, tab_size, show_cr); 2125 } 2126 } else { 2127 if (display_numbers) { 2128 tty_gotoyx(r + j, c - xwidth); 2129 memset(buf, ' ', display_numbers); 2130 buf[display_numbers] = '\0'; 2131 tty_print_nstring(buf, display_numbers); 2132 } 2133 if (ch == DEL_CH) { 2134 tty_setcolor(DFFDEL_COLOR); 2135 } 2136 if (ch == CHG_CH) { 2137 tty_setcolor(DFFCHD_COLOR); 2138 } 2139 memset(buf, ' ', width); 2140 buf[width] = '\0'; 2141 } 2142 tty_gotoyx(r + j, c); 2143 tty_print_nstring(buf, width); 2144 } 2145 tty_setcolor(NORMAL_COLOR); 2146 k = width; 2147 if (width < xwidth - 1) { 2148 k = xwidth - 1; 2149 } 2150 memset(buf, ' ', k); 2151 buf[k] = '\0'; 2152 for (; j < height; j++) { 2153 if (xwidth) { 2154 tty_gotoyx(r + j, c - xwidth); 2155 tty_print_nstring(buf, xwidth - 1); 2156 } 2157 tty_gotoyx(r + j, c); 2158 tty_print_nstring(buf, width); 2159 } 2160 2161 return 0; 2162 } 2163 2164 2165 static void 2166 view_status (const WDiff *view, int ord, int width, int c) 2167 { 2168 int skip_rows = view->skip_rows; 2169 int skip_cols = view->skip_cols; 2170 2171 char buf[BUFSIZ]; 2172 int filename_width; 2173 int linenum, lineofs; 2174 2175 tty_setcolor(SELECTED_COLOR); 2176 2177 tty_gotoyx(0, c); 2178 get_line_numbers(&view->a[ord], skip_rows, &linenum, &lineofs); 2179 2180 filename_width = width - 22; 2181 if (filename_width < 8) { 2182 filename_width = 8; 2183 } 2184 if (filename_width >= (int)sizeof(buf)) { 2185 /* abnormal, but avoid buffer overflow */ 2186 filename_width = sizeof(buf) - 1; 2187 } 2188 trim(strip_home_and_password(view->label[ord]), buf, filename_width); 2189 if (ord == 0) { 2190 tty_printf("%-*s %6d+%-4d Col %-4d ", filename_width, buf, linenum, lineofs, skip_cols); 2191 } else { 2192 tty_printf("%-*s %6d+%-4d Dif %-4d ", filename_width, buf, linenum, lineofs, view->ndiff); 2193 } 2194 } 2195 2196 2197 static void 2198 view_update (WDiff *view) 2199 { 2200 int height = view->height; 2201 int width1; 2202 int width2; 2203 2204 int last = view->a[0].len - 1; 2205 2206 if (view->skip_rows > last) { 2207 view->skip_rows = last; 2208 } 2209 if (view->skip_rows < 0) { 2210 view->skip_rows = 0; 2211 } 2212 if (view->skip_cols < 0) { 2213 view->skip_cols = 0; 2214 } 2215 2216 if (height < 2) { 2217 return; 2218 } 2219 2220 width1 = view->half1 + view->bias; 2221 width2 = view->half2 - view->bias; 2222 if (view->full) { 2223 width1 = COLS; 2224 width2 = 0; 2225 } 2226 2227 if (view->new_frame) { 2228 Dlg_head *h = view->widget.parent; 2229 2230 int xwidth = view->display_symbols + view->display_numbers; 2231 2232 tty_setcolor(NORMAL_COLOR); 2233 if (width1 > 1) { 2234 draw_double_box(h, 1, 0, height, width1); 2235 } 2236 if (width2 > 1) { 2237 draw_double_box(h, 1, width1, height, width2); 2238 } 2239 2240 if (xwidth) { 2241 xwidth++; 2242 if (xwidth < width1 - 1) { 2243 tty_gotoyx(1, xwidth); 2244 tty_print_alt_char(ACS_TTEE); 2245 tty_gotoyx(height, xwidth); 2246 tty_print_alt_char(ACS_BTEE); 2247 tty_print_vline(2, xwidth, height - 2); 2248 } 2249 if (xwidth < width2 - 1) { 2250 tty_gotoyx(1, width1 + xwidth); 2251 tty_print_alt_char(ACS_TTEE); 2252 tty_gotoyx(height, width1 + xwidth); 2253 tty_print_alt_char(ACS_BTEE); 2254 tty_print_vline(2, width1 + xwidth, height - 2); 2255 } 2256 } 2257 2258 view->new_frame = 0; 2259 } 2260 2261 if (width1 > 2) { 2262 view_status(view, view->ord, width1, 0); 2263 view_display_file(view, view->ord, 2, 1, height - 2, width1 - 2); 2264 } 2265 if (width2 > 2) { 2266 view_status(view, view->ord ^ 1, width2, width1); 2267 view_display_file(view, view->ord ^ 1, 2, width1 + 1, height - 2, width2 - 2); 2268 } 2269 } 2270 2271 2272 static void 2273 view_redo (WDiff *view) 2274 { 2275 if (view_reinit(view) < 0) { 2276 view->view_quit = 1; 2277 } else if (view->display_numbers) { 2278 int old = view->display_numbers; 2279 view->display_numbers = calc_nwidth(view->a); 2280 view->new_frame = (old != view->display_numbers); 2281 } 2282 } 2283 2284 2285 #define IS_WHOLE_OR_DONT_CARE() \ 2286 (!whole || ( \ 2287 (i == 0 || strchr(wholechars, haystack[i - 1]) == NULL) && \ 2288 (i + nlen == hlen || strchr(wholechars, haystack[i + nlen]) == NULL) \ 2289 )) 2290 2291 2292 static const unsigned char * 2293 memmem_dummy (const unsigned char *haystack, size_t i, size_t hlen, const unsigned char *needle, size_t nlen, int whole) 2294 { 2295 for (; i + nlen <= hlen; i++) { 2296 if (haystack[i] == needle[0]) { 2297 size_t j; 2298 for (j = 1; j < nlen; j++) { 2299 if (haystack[i + j] != needle[j]) { 2300 break; 2301 } 2302 } 2303 if (j == nlen && IS_WHOLE_OR_DONT_CARE()) { 2304 return haystack + i; 2305 } 2306 } 2307 } 2308 2309 return NULL; 2310 } 2311 2312 2313 static const unsigned char * 2314 memmem_dummy_nocase (const unsigned char *haystack, size_t i, size_t hlen, const unsigned char *needle, size_t nlen, int whole) 2315 { 2316 for (; i + nlen <= hlen; i++) { 2317 if (toupper(haystack[i]) == toupper(needle[0])) { 2318 size_t j; 2319 for (j = 1; j < nlen; j++) { 2320 if (toupper(haystack[i + j]) != toupper(needle[j])) { 2321 break; 2322 } 2323 } 2324 if (j == nlen && IS_WHOLE_OR_DONT_CARE()) { 2325 return haystack + i; 2326 } 2327 } 2328 } 2329 2330 return NULL; 2331 } 2332 2333 2334 static const unsigned char * 2335 memmem_dummy_rev (const unsigned char *haystack, size_t i, size_t hlen, const unsigned char *needle, size_t nlen, int whole) 2336 { 2337 while (i--) { 2338 if (haystack[i] == needle[0] && i + nlen <= hlen) { 2339 size_t j; 2340 for (j = 1; j < nlen; j++) { 2341 if (haystack[i + j] != needle[j]) { 2342 break; 2343 } 2344 } 2345 if (j == nlen && IS_WHOLE_OR_DONT_CARE()) { 2346 return haystack + i; 2347 } 2348 } 2349 } 2350 2351 return NULL; 2352 } 2353 2354 2355 static const unsigned char * 2356 memmem_dummy_rev_nocase (const unsigned char *haystack, size_t i, size_t hlen, const unsigned char *needle, size_t nlen, int whole) 2357 { 2358 while (i--) { 2359 if (toupper(haystack[i]) == toupper(needle[0]) && i + nlen <= hlen) { 2360 size_t j; 2361 for (j = 1; j < nlen; j++) { 2362 if (toupper(haystack[i + j]) != toupper(needle[j])) { 2363 break; 2364 } 2365 } 2366 if (j == nlen && IS_WHOLE_OR_DONT_CARE()) { 2367 return haystack + i; 2368 } 2369 } 2370 } 2371 2372 return NULL; 2373 } 2374 2375 2376 static const unsigned char * 2377 search_string (const DIFFLN *p, size_t xpos, const void *needle, size_t nlen, int whole, int ccase) 2378 { 2379 const unsigned char *haystack = p->p; 2380 size_t hlen = p->u.len; 2381 2382 if (xpos > hlen || nlen <= 0 || haystack == NULL || needle == NULL) { 2383 return NULL; 2384 } 2385 2386 /* XXX I should use Boyer-Moore */ 2387 if (ccase) { 2388 return memmem_dummy(haystack, xpos, hlen, needle, nlen, whole); 2389 } else { 2390 return memmem_dummy_nocase(haystack, xpos, hlen, needle, nlen, whole); 2391 } 2392 } 2393 2394 2395 static int 2396 view_search_string (WDiff *view, const char *needle, int ccase, int back, int whole) 2397 { 2398 size_t nlen = strlen(needle); 2399 size_t xpos = 0; 2400 2401 int ord = view->ord; 2402 const ARRAY *a = &view->a[ord]; 2403 const DIFFLN *p; 2404 2405 int i = view->last_found; 2406 2407 if (back) { 2408 if (i == -1) { 2409 i = view->skip_rows; 2410 } 2411 for (--i, p = (DIFFLN *)a->data + i; i >= 0; p--, i--) { 2412 const unsigned char *q = search_string(p, xpos, needle, nlen, whole, ccase); 2413 if (q != NULL) { 2414 return i; 2415 } 2416 } 2417 } else { 2418 if (i == -1) { 2419 i = view->skip_rows - 1; 2420 } 2421 for (++i, p = (DIFFLN *)a->data + i; i < a->len; p++, i++) { 2422 const unsigned char *q = search_string(p, xpos, needle, nlen, whole, ccase); 2423 if (q != NULL) { 2424 return i; 2425 } 2426 } 2427 } 2428 2429 return -1; 2430 } 2431 2432 2433 static void 2434 view_search (WDiff *view, int again) 2435 { 2436 /* XXX some statics here, to be remembered between runs */ 2437 static char *searchopt_text = NULL; 2438 static int searchopt_type; 2439 static int searchopt_case; 2440 static int searchopt_backwards; 2441 static int searchopt_whole; 2442 2443 static int compiled = 0; 2444 2445 if (again < 0) { 2446 g_free(searchopt_text); 2447 searchopt_text = NULL; 2448 if (compiled) { 2449 compiled = 0; 2450 /*XXX free search exp*/ 2451 } 2452 return; 2453 } 2454 2455 if (view->dsrc != DATA_SRC_MEM) { 2456 error_dialog(_("Search"), _(" Search is disabled ")); 2457 return; 2458 } 2459 2460 if (!again || searchopt_text == NULL) { 2461 char *tsearchopt_text; 2462 int tsearchopt_type = searchopt_type; 2463 int tsearchopt_case = searchopt_case; 2464 int tsearchopt_backwards = searchopt_backwards; 2465 int tsearchopt_whole = searchopt_whole; 2466 2467 search_widgets[2].result = &tsearchopt_whole; 2468 search_widgets[3].result = &tsearchopt_backwards; 2469 search_widgets[4].result = &tsearchopt_case; 2470 search_widgets[5].result = &tsearchopt_type; 2471 search_widgets[6].str_result = &tsearchopt_text; 2472 search_widgets[6].text = searchopt_text; 2473 2474 if (quick_dialog(&search_input) == B_CANCEL) { 2475 return; 2476 } 2477 if (tsearchopt_text == NULL || !*tsearchopt_text) { 2478 g_free(tsearchopt_text); 2479 return; 2480 } 2481 g_free(searchopt_text); 2482 2483 searchopt_text = tsearchopt_text; 2484 searchopt_type = tsearchopt_type; 2485 searchopt_case = tsearchopt_case; 2486 searchopt_backwards = tsearchopt_backwards; 2487 searchopt_whole = tsearchopt_whole; 2488 } 2489 2490 if (compiled) { 2491 compiled = 0; 2492 /*XXX free search exp*/ 2493 } 2494 if (0/*XXX new search exp*/) { 2495 error_dialog(_("Error"), _(" Cannot search ")); 2496 return; 2497 } 2498 compiled = 1; 2499 if (searchopt_type == 0) { 2500 view->last_found = view_search_string(view, searchopt_text, searchopt_case, searchopt_backwards, searchopt_whole); 2501 } 2502 2503 if (view->last_found == -1) { 2504 error_dialog(_("Search"), _(" Search string not found ")); 2505 } else { 2506 view->skip_rows = view->last_found; 2507 view_update(view); 2508 } 2509 } 2510 2511 2512 static void 2513 view_search_cmd (WDiff *view) 2514 { 2515 view_search(view, 0); 2516 } 2517 2518 2519 static void 2520 view_edit (WDiff *view, int ord) 2521 { 2522 int linenum, lineofs; 2523 2524 if (view->dsrc == DATA_SRC_TMP) { 2525 error_dialog(_("Edit"), _(" Edit is disabled ")); 2526 return; 2527 } 2528 2529 get_line_numbers(&view->a[ord], view->skip_rows, &linenum, &lineofs); 2530 do_edit_at_line(view->file[ord], linenum); 2531 view_redo(view); 2532 view_update(view); 2533 } 2534 2535 2536 static void 2537 view_edit_cmd (WDiff *view) 2538 { 2539 view_edit(view, view->ord); 2540 } 2541 2542 2543 static void 2544 view_goto_cmd (WDiff *view, int ord) 2545 { 2546 static const char *title[2] = { " Goto line (left) ", " Goto line (right) " }; 2547 static char prev[256]; 2548 /* XXX some statics here, to be remembered between runs */ 2549 2550 int newline; 2551 char *input; 2552 2553 input = input_dialog(_(title[ord]), _(" Enter line: "), MC_HISTORY_YDIFF_GOTO_LINE, prev); 2554 if (input != NULL) { 2555 const char *s = input; 2556 if (scan_deci(&s, &newline) == 0 && *s == '\0') { 2557 int i = 0; 2558 if (newline > 0) { 2559 const DIFFLN *p; 2560 for (p = view->a[ord].data; i < view->a[ord].len; i++, p++) { 2561 if (p->line == newline) { 2562 break; 2563 } 2564 } 2565 } 2566 view->skip_rows = i; 2567 snprintf(prev, sizeof(prev), "%d", newline); 2568 } 2569 g_free(input); 2570 } 2571 } 2572 2573 2574 static void 2575 view_help_cmd (void) 2576 { 2577 interactive_display(NULL, "[Diff Viewer]"); 2578 } 2579 2580 2581 static void 2582 view_quit_cmd (WDiff *view) 2583 { 2584 dlg_stop(view->widget.parent); 2585 } 2586 2587 2588 static void 2589 view_labels (WDiff *view) 2590 { 2591 Dlg_head *h = view->widget.parent; 2592 2593 buttonbar_set_label(h, 1, Q_("ButtonBar|Help"), view_help_cmd); 2594 2595 buttonbar_set_label_data(h, 4, Q_("ButtonBar|Edit"), (buttonbarfn)view_edit_cmd, view); 2596 buttonbar_set_label_data(h, 7, Q_("ButtonBar|Search"), (buttonbarfn)view_search_cmd, view); 2597 buttonbar_set_label_data(h, 10, Q_("ButtonBar|Quit"), (buttonbarfn)view_quit_cmd, view); 2598 } 2599 2600 2601 static int 2602 view_event (Gpm_Event *event, void *x) 2603 { 2604 WDiff *view = (WDiff *)x; 2605 int result = MOU_NORMAL; 2606 2607 /* We are not interested in the release events */ 2608 if (!(event->type & (GPM_DOWN | GPM_DRAG))) { 2609 return result; 2610 } 2611 2612 /* Wheel events */ 2613 if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN)) { 2614 view->skip_rows -= 2; 2615 view_update(view); 2616 return result; 2617 } 2618 if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN)) { 2619 view->skip_rows += 2; 2620 view_update(view); 2621 return result; 2622 } 2623 2624 return result; 2625 } 2626 2627 2628 static cb_ret_t 2629 view_handle_key (WDiff *view, int c) 2630 { 2631 c = convert_from_input_c(c); 2632 2633 switch (c) { 2634 case 's': 2635 view->display_symbols ^= 1; 2636 view->new_frame = 1; 2637 return MSG_HANDLED; 2638 2639 case 'l': 2640 view->display_numbers ^= calc_nwidth(view->a); 2641 view->new_frame = 1; 2642 return MSG_HANDLED; 2643 2644 case 'f': 2645 view->full ^= 1; 2646 view->new_frame = 1; 2647 return MSG_HANDLED; 2648 2649 case '=': 2650 if (!view->full) { 2651 view->bias = 0; 2652 view->new_frame = 1; 2653 } 2654 return MSG_HANDLED; 2655 2656 case '>': 2657 if (!view->full) { 2658 view_compute_split(view, 1); 2659 view->new_frame = 1; 2660 } 2661 return MSG_HANDLED; 2662 2663 case '<': 2664 if (!view->full) { 2665 view_compute_split(view, -1); 2666 view->new_frame = 1; 2667 } 2668 return MSG_HANDLED; 2669 2670 case 'c': 2671 view->show_cr ^= 1; 2672 return MSG_HANDLED; 2673 2674 case 'h': 2675 view->show_hdiff ^= 1; 2676 return MSG_HANDLED; 2677 2678 case '2': 2679 case '3': 2680 case '4': 2681 case '8': 2682 view->tab_size = c - '0'; 2683 return MSG_HANDLED; 2684 2685 case XCTRL('u'): 2686 view->ord ^= 1; 2687 return MSG_HANDLED; 2688 2689 case XCTRL('r'): 2690 view_redo(view); 2691 return MSG_HANDLED; 2692 2693 case 'n': 2694 view->skip_rows = find_next_hunk(&view->a[0], view->skip_rows); 2695 return MSG_HANDLED; 2696 2697 case 'p': 2698 view->skip_rows = find_prev_hunk(&view->a[0], view->skip_rows); 2699 return MSG_HANDLED; 2700 2701 case 'g': 2702 case 'G': 2703 view_goto_cmd(view, c == 'G'); 2704 return MSG_HANDLED; 2705 2706 case KEY_BACKSPACE: 2707 view->last_found = -1; 2708 return MSG_HANDLED; 2709 2710 case KEY_F(4): 2711 view_edit(view, view->ord); 2712 return MSG_HANDLED; 2713 2714 case KEY_F(14): 2715 view_edit(view, view->ord ^ 1); 2716 return MSG_HANDLED; 2717 2718 case KEY_F(17): 2719 view_search(view, 1); 2720 return MSG_HANDLED; 2721 2722 case KEY_HOME: 2723 case KEY_M_CTRL | KEY_PPAGE: 2724 view->skip_rows = 0; 2725 return MSG_HANDLED; 2726 2727 case KEY_END: 2728 case KEY_M_CTRL | KEY_NPAGE: 2729 view->skip_rows = view->a[0].len - 1; 2730 return MSG_HANDLED; 2731 2732 case KEY_UP: 2733 view->skip_rows--; 2734 return MSG_HANDLED; 2735 2736 case KEY_DOWN: 2737 view->skip_rows++; 2738 return MSG_HANDLED; 2739 2740 case KEY_NPAGE: 2741 view->skip_rows += view->height - 2; 2742 return MSG_HANDLED; 2743 2744 case KEY_PPAGE: 2745 view->skip_rows -= view->height - 2; 2746 return MSG_HANDLED; 2747 2748 case KEY_LEFT: 2749 view->skip_cols--; 2750 return MSG_HANDLED; 2751 2752 case KEY_RIGHT: 2753 view->skip_cols++; 2754 return MSG_HANDLED; 2755 2756 case KEY_M_CTRL | KEY_LEFT: 2757 view->skip_cols -= 8; 2758 return MSG_HANDLED; 2759 2760 case KEY_M_CTRL | KEY_RIGHT: 2761 view->skip_cols += 8; 2762 return MSG_HANDLED; 2763 2764 case XCTRL('a'): 2765 view->skip_cols = 0; 2766 return MSG_HANDLED; 2767 2768 case XCTRL('o'): 2769 view_other_cmd(); 2770 return MSG_HANDLED; 2771 2772 case 'q': 2773 case XCTRL('g'): 2774 case ESC_CHAR: 2775 view->view_quit = 1; 2776 return MSG_HANDLED; 2777 } 2778 2779 /* Key not used */ 2780 return MSG_NOT_HANDLED; 2781 } 2782 2783 2784 static cb_ret_t 2785 view_callback (Widget *w, widget_msg_t msg, int parm) 2786 { 2787 cb_ret_t i; 2788 WDiff *view = (WDiff *)w; 2789 Dlg_head *h = view->widget.parent; 2790 2791 switch (msg) { 2792 case WIDGET_INIT: 2793 view_labels(view); 2794 return MSG_HANDLED; 2795 2796 case WIDGET_DRAW: 2797 view->new_frame = 1; 2798 view_update(view); 2799 return MSG_HANDLED; 2800 2801 case WIDGET_CURSOR: 2802 return MSG_HANDLED; 2803 2804 case WIDGET_KEY: 2805 i = view_handle_key((WDiff *)view, parm); 2806 if (view->view_quit) 2807 dlg_stop(h); 2808 else { 2809 view_update(view); 2810 } 2811 return i; 2812 2813 case WIDGET_IDLE: 2814 return MSG_HANDLED; 2815 2816 case WIDGET_FOCUS: 2817 view_labels(view); 2818 return MSG_HANDLED; 2819 2820 case WIDGET_DESTROY: 2821 return MSG_HANDLED; 2822 2823 default: 2824 return default_proc(msg, parm); 2825 } 2826 } 2827 2828 2829 static void 2830 view_adjust_size (Dlg_head *h) 2831 { 2832 WDiff *view; 2833 WButtonBar *bar; 2834 2835 /* Look up the viewer and the buttonbar, we assume only two widgets here */ 2836 view = (WDiff *)find_widget_type(h, view_callback); 2837 bar = find_buttonbar(h); 2838 widget_set_size(&view->widget, 0, 0, LINES, COLS); 2839 widget_set_size((Widget *)bar, LINES - 1, 0, 1, COLS); 2840 2841 view_compute_areas(view); 2842 } 2843 2844 2845 static cb_ret_t 2846 view_dialog_callback (Dlg_head *h, dlg_msg_t msg, int parm) 2847 { 2848 switch (msg) { 2849 case DLG_RESIZE: 2850 view_adjust_size(h); 2851 return MSG_HANDLED; 2852 2853 default: 2854 return default_dlg_callback(h, msg, parm); 2855 } 2856 } 2857 2858 2859 int 2860 diff_view (const char *file1, const char *file2, const char *label1, const char *label2) 2861 { 2862 int error; 2863 WDiff *view; 2864 WButtonBar *bar; 2865 Dlg_head *view_dlg; 2866 2867 /* Create dialog and widgets, put them on the dialog */ 2868 view_dlg = 2869 create_dlg(0, 0, LINES, COLS, NULL, view_dialog_callback, 2870 "[Diff Viewer]", NULL, DLG_WANT_TAB); 2871 2872 view = g_new0(WDiff, 1); 2873 2874 init_widget(&view->widget, 0, 0, LINES, COLS, 2875 (callback_fn)view_callback, 2876 (mouse_h)view_event); 2877 2878 widget_want_cursor(view->widget, 0); 2879 2880 bar = buttonbar_new(1); 2881 2882 add_widget(view_dlg, bar); 2883 add_widget(view_dlg, view); 2884 2885 error = view_init(view, "-a", file1, file2, label1, label2, DATA_SRC_MEM); /* XXX binary diff? */ 2886 2887 /* Please note that if you add another widget, 2888 * you have to modify view_adjust_size to 2889 * be aware of it 2890 */ 2891 if (!error) { 2892 run_dlg(view_dlg); 2893 view_search(view, -1); 2894 view_fini(view); 2895 } 2896 destroy_dlg(view_dlg); 2897 2898 return error; 2899 } 2900 #endif -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/ydiff.h mc-4.7.0-pre1/src/ydiff.h
old new 1 #ifndef YDIFF_H_included 2 #define YDIFF_H_included 3 4 int diff_view (const char *file1, const char *file2, const char *label1, const char *label2); 5 6 #endif -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/zdiff.c mc-4.7.0-pre1/src/zdiff.c
old new 1 /* 2 * Copyright (c) 2008 Daniel Borca All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 19 20 #include <config.h> 21 #include <ctype.h> 22 #include <errno.h> 23 #include <fcntl.h> 24 #include <stdlib.h> 25 #include "global.h" 26 #include "tty.h" 27 #include "cmd.h" 28 #include "dialog.h" 29 #include "widget.h" 30 #include "color.h" 31 #include "help.h" 32 #include "key.h" 33 #include "layout.h" 34 #include "wtools.h" 35 #include "panel.h" /* Needed for current_panel and other_panel */ 36 #include "charsets.h" 37 #include "history.h" 38 #include "ydiff.h" 39 #include "zdiff.h" 40 41 42 #ifdef USE_DIFF_VIEW 43 44 typedef struct { 45 int len, max; 46 void *data; 47 int error; 48 int eltsize; 49 int growth; 50 } ARRAY; 51 52 #define RECURSIVE_DEPTH 100 53 54 #define SAME_FILE(st0, st1) ((st0).st_ino == (st1).st_ino) 55 56 #define ADD_CH '+' 57 #define DEL_CH '-' 58 #define CHG_CH '*' 59 #define EQU_CH ' ' 60 #define ERR_CH '!' 61 #define DIR_CH '/' 62 63 #if 1 64 #define make_tmp_path(s1, s2) _bufpath(buf, s1, s2) 65 #define make_new_path(s1, s2) _bufpath(buf, s1, s2) 66 #define make_1st_path bufpath_1st 67 #define make_2nd_path bufpath_2nd 68 #define free_tmp_path(p) 69 #define free_new_path(p) 70 #define free_1st_path(p) 71 #define free_2nd_path(p) 72 #else 73 #define make_tmp_path strpath 74 #define make_new_path strpath 75 #define make_1st_path strpath 76 #define make_2nd_path strpath 77 #define free_tmp_path(p) free(p) 78 #define free_new_path(p) free(p) 79 #define free_1st_path(p) free(p) 80 #define free_2nd_path(p) free(p) 81 #endif 82 83 typedef struct DNODE { 84 const struct DNODE *link; 85 const struct stat *st[2]; 86 } DNODE; 87 88 typedef int (*DFUNC) (void *ctx, int ch, const char *f1, const char *f2); 89 90 static int diff_file (const char *r0, const char *f0, const char *r1, const char *f1, int recursive, const DNODE *prev, DFUNC printer, void *ctx); 91 92 #define is_eq(c) ((c) == EQU_CH || (c) == DIR_CH) 93 94 typedef struct { 95 int ch; 96 char *name[2]; 97 } LNODE; 98 99 typedef struct { 100 Widget widget; 101 102 int recursive; 103 const char *dir[2]; /* filenames */ 104 ARRAY z; 105 int ndiff; /* number of hunks */ 106 107 int view_quit:1; /* Quit flag */ 108 109 int height; 110 int half1; 111 int half2; 112 int width1; 113 int width2; 114 int bias; 115 int new_frame; 116 int skip_rows; 117 int skip_cols; 118 int display_symbols; 119 int display_numbers; 120 int ord; 121 int full; 122 int last_found; 123 } WDiff; 124 125 126 #define OPTX 50 127 #define OPTY 8 128 129 static QuickWidget diffopt_widgets[] = { 130 { quick_button, 6, 10, 5, OPTY, N_("&Cancel"), 0, B_CANCEL, NULL, NULL, NULL }, 131 { quick_button, 3, 10, 5, OPTY, N_("&OK"), 0, B_ENTER, NULL, NULL, NULL }, 132 { quick_checkbox, 4, OPTX, 3, OPTY, N_("&Recursive"), 0, 0, NULL, NULL, NULL }, 133 NULL_QuickWidget 134 }; 135 136 static QuickDialog diffopt = { 137 OPTX, OPTY, -1, -1, 138 N_(" Diff Options "), "[Directory Diff Options]", 139 diffopt_widgets, 0 140 }; 141 142 #define SEARCH_DLG_WIDTH 58 143 #define SEARCH_DLG_HEIGHT 10 144 145 static const char *search_str[] = { 146 N_("&Normal") 147 }; 148 149 static QuickWidget search_widgets[] = { 150 { quick_button, 6, 10, 7, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, NULL, NULL, NULL }, 151 { quick_button, 2, 10, 7, SEARCH_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, NULL, NULL, NULL }, 152 { quick_checkbox, 33, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("&Whole words only"), 0, 0, NULL, NULL, NULL }, 153 { quick_checkbox, 33, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Backwards"), 0, 0, NULL, NULL, NULL }, 154 { quick_checkbox, 33, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, N_("case &Sensitive"), 0, 0, NULL, NULL, NULL }, 155 { quick_radio, 4, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, "", 1, 0, NULL, const_cast(char **, search_str), NULL }, 156 { quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0, NULL, NULL, "diff-search" }, 157 { quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, NULL, NULL, NULL }, 158 NULL_QuickWidget 159 }; 160 161 static QuickDialog search_input = { 162 SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, 163 N_("Search"), "[Input Line Keys]", 164 search_widgets, 0 165 }; 166 167 static const char *wholechars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; 168 169 #define error_dialog(h, s) query_dialog(h, s, D_ERROR, 1, _("&Dismiss")) 170 171 172 /* array *********************************************************************/ 173 174 175 /** 176 * Initialize array. 177 * 178 * \param a array, must be non-NULL 179 * \param eltsize element size 180 * \param growth growth constant 181 */ 182 static void 183 arr_init (ARRAY *a, int eltsize, int growth) 184 { 185 a->len = a->max = 0; 186 a->data = NULL; 187 a->error = 0; 188 a->eltsize = eltsize; 189 a->growth = growth; 190 } 191 192 193 /** 194 * Reset array length without dealocating storage. 195 * 196 * \param a array, must be non-NULL 197 */ 198 static void 199 arr_reset (ARRAY *a) 200 { 201 if (a->error) { 202 return; 203 } 204 a->len = 0; 205 } 206 207 208 /** 209 * Enlarge array. 210 * 211 * \param a array, must be non-NULL 212 * 213 * \return new element, or NULL if error 214 */ 215 static void * 216 arr_enlarge (ARRAY *a) 217 { 218 void *p; 219 if (a->error) { 220 return NULL; 221 } 222 if (a->len == a->max) { 223 int max = a->max + a->growth; 224 p = realloc(a->data, max * a->eltsize); 225 if (p == NULL) { 226 a->error = 1; 227 return NULL; 228 } 229 a->max = max; 230 a->data = p; 231 } 232 p = (void *)((char *)a->data + a->eltsize * a->len++); 233 return p; 234 } 235 236 237 /** 238 * Free array. 239 * 240 * \param a array, must be non-NULL 241 * \param func function to be called on each element 242 */ 243 static void 244 arr_free (ARRAY *a, void (*func) (void *)) 245 { 246 if (func != NULL) { 247 int i; 248 for (i = 0; i < a->len; i++) { 249 func((void *)((char *)a->data + a->eltsize * i)); 250 } 251 } 252 free(a->data); 253 arr_init(a, a->eltsize, a->growth); 254 } 255 256 257 /* diff parse ****************************************************************/ 258 259 260 /** 261 * Concatenate two strings to make a path. 262 * 263 * \param p path buffer (must be large enough) 264 * \param s1 1st component 265 * \param s2 2nd component (may be NULL) 266 * 267 * \return static path 268 * 269 * \note the user must not free this buffer 270 */ 271 static char * 272 _bufpath (char *p, const char *s1, const char *s2) 273 { 274 if (s2 == NULL) { 275 strcpy(p, s1); 276 } else { 277 int len = strlen(s1); 278 memcpy(p, s1, len); 279 p[len++] = '/'; 280 strcpy(p + len, s2); 281 } 282 return p; 283 } 284 285 286 /** 287 * Concatenate two strings to make a path. 288 * 289 * \param s1 1st component 290 * \param s2 2nd component (may be NULL) 291 * 292 * \return static path 293 * 294 * \note the user must not free this buffer 295 */ 296 static char * 297 bufpath_1st (const char *s1, const char *s2) 298 { 299 static char p[3 * PATH_MAX]; 300 return _bufpath(p, s1, s2); 301 } 302 303 304 /** 305 * Concatenate two strings to make a path. 306 * 307 * \param s1 1st component 308 * \param s2 2nd component (may be NULL) 309 * 310 * \return static path 311 * 312 * \note the user must not free this buffer 313 */ 314 static char * 315 bufpath_2nd (const char *s1, const char *s2) 316 { 317 static char p[3 * PATH_MAX]; 318 return _bufpath(p, s1, s2); 319 } 320 321 322 /** 323 * Concatenate two strings to make a path. 324 * 325 * \param s1 1st component 326 * \param s2 2nd component (may be NULL) 327 * 328 * \return allocated path 329 * 330 * \note the user must free this buffer 331 */ 332 static char * 333 strpath (const char *s1, const char *s2) 334 { 335 char *p; 336 if (s2 == NULL) { 337 p = strdup(s1); 338 } else { 339 int len = strlen(s1); 340 p = malloc(len + 1 + strlen(s2) + 1); 341 if (p != NULL) { 342 memcpy(p, s1, len); 343 p[len++] = '/'; 344 strcpy(p + len, s2); 345 } 346 } 347 return p; 348 } 349 350 351 /** 352 * Scan directory. 353 * 354 * \param a list of items to fill 355 * \param pre first component of directory 356 * \param name second component of directory 357 * 358 * \return 0 if success 359 */ 360 static int 361 scan_dir (ARRAY *a, const char *pre, const char *name) 362 { 363 char buf[2 * PATH_MAX]; /* XXX for _bufpath */ 364 char *p; 365 DIR *dir; 366 int rv = 0; 367 368 p = make_tmp_path(pre, name); 369 if (p == NULL) { 370 return -1; 371 } 372 373 dir = mc_opendir(p); 374 if (dir == NULL) { 375 free_tmp_path(p); 376 return -1; 377 } 378 379 for (;;) { 380 char **q; 381 const struct dirent *ent; 382 383 errno = 0; 384 ent = mc_readdir(dir); 385 if (ent == NULL) { 386 if (errno) { 387 rv = errno; /* XXX should we try to continue? */ 388 } 389 break; 390 } 391 392 if (ent->d_name[0] == '.' && (ent->d_name[1] == '\0' || (ent->d_name[1] == '.' && ent->d_name[2] == '\0'))) { 393 continue; 394 } 395 396 q = arr_enlarge(a); 397 if (q == NULL) { 398 rv = -1; 399 break; 400 } 401 402 *q = strdup(ent->d_name); 403 if (*q == NULL) { 404 rv = -1; 405 break; 406 } 407 } 408 409 mc_closedir(dir); 410 free_tmp_path(p); 411 412 return rv; 413 } 414 415 416 /** 417 * Comparator for qsort. 418 * 419 * \param f1 1st item 420 * \param f2 2nd item 421 * 422 * \return strcmp-like result 423 */ 424 static int 425 compar (const void *f1, const void *f2) 426 { 427 return strcmp(*(const char *const *)f1, *(const char *const *)f2); 428 } 429 430 431 /** 432 * Helper to free directory items. 433 * 434 * \param p element 435 */ 436 static void 437 dispose (void *p) 438 { 439 free(*(char **)p); 440 } 441 442 443 /** 444 * Compare two binary files. 445 * 446 * \param p0 1st filename 447 * \param p1 2nd filename 448 * \param st array of two stat structs 449 * 450 * \return 0 if files are identical, 1 if different, -1 if error 451 */ 452 static int 453 diff_binary (const char *p0, const char *p1, const struct stat st[2]) 454 { 455 int rv = 0; 456 int fd0, fd1; 457 off_t size; 458 459 if (st[0].st_size != st[1].st_size) { 460 return 1; 461 } 462 size = st[0].st_size; 463 464 fd0 = mc_open(p0, O_RDONLY | O_BINARY); 465 if (fd0 < 0) { 466 return -1; 467 } 468 fd1 = mc_open(p1, O_RDONLY | O_BINARY); 469 if (fd1 < 0) { 470 mc_close(fd0); 471 return -1; 472 } 473 474 while (size > 0) { 475 char buf0[BUFSIZ], buf1[BUFSIZ]; 476 int n0 = mc_read(fd0, buf0, sizeof(buf0)); 477 int n1 = mc_read(fd1, buf1, sizeof(buf1)); 478 if (n0 != n1) { 479 rv = 1; 480 break; 481 } 482 if (n0 <= 0) { 483 rv = -1; 484 break; 485 } 486 if (memcmp(buf0, buf1, n0)) { 487 rv = 1; 488 break; 489 } 490 size -= n0; 491 } 492 493 mc_close(fd1); 494 mc_close(fd0); 495 return rv; 496 } 497 498 499 /** 500 * Compare two subdirectories. 501 * 502 * \param r0 1st path component 503 * \param r1 2nd path component 504 * \param d common subdirectory name 505 * \param recursive max allowed depth 506 * \param prev top of stack of parent subdirectories 507 * \param printer callback 508 * \param ctx opaque object to be passed to callback 509 * 510 * \return 0 success, otherwise error 511 */ 512 static int 513 diff_dirs (const char *r0, const char *r1, const char *d, int recursive, const DNODE *prev, DFUNC printer, void *ctx) 514 { 515 char buf[2 * PATH_MAX]; /* XXX for _bufpath */ 516 ARRAY a[2]; 517 int rv = -1; 518 arr_init(&a[0], sizeof(char *), 256); 519 arr_init(&a[1], sizeof(char *), 256); 520 if (scan_dir(&a[0], r0, d) == 0 && scan_dir(&a[1], r1, d) == 0) { 521 int i = 0; 522 int j = 0; 523 char **q0 = a[0].data; 524 char **q1 = a[1].data; 525 526 rv = 0; 527 qsort(a[0].data, a[0].len, a[0].eltsize, compar); 528 qsort(a[1].data, a[1].len, a[1].eltsize, compar); 529 530 while (i < a[0].len || j < a[1].len) { 531 char *tmp = NULL; 532 char *f0 = NULL; 533 char *f1 = NULL; 534 int nameorder = (i >= a[0].len) ? 1 : (j >= a[1].len) ? -1 : compar(q0, q1); 535 if (nameorder <= 0) { 536 f0 = *q0++; 537 i++; 538 if (d != NULL) { 539 tmp = f0 = make_new_path(d, f0); 540 if (tmp == NULL) { 541 rv = -1; 542 break; 543 } 544 } 545 } 546 if (nameorder >= 0) { 547 f1 = *q1++; 548 j++; 549 if (d != NULL) { 550 if (tmp != NULL) { 551 f1 = tmp; 552 } else { 553 tmp = f1 = make_new_path(d, f1); 554 if (tmp == NULL) { 555 rv = -1; 556 break; 557 } 558 } 559 } 560 } 561 rv |= diff_file(r0, f0, r1, f1, recursive, prev, printer, ctx); 562 free_new_path(tmp); 563 } 564 } 565 arr_free(&a[1], dispose); 566 arr_free(&a[0], dispose); 567 return rv; 568 } 569 570 571 /** 572 * Compare two files. 573 * 574 * \param r0 1st path component 575 * \param f0 1st file name (should be NULL initially) 576 * \param r1 2nd path component 577 * \param f1 2nd file name (should be NULL initially) 578 * \param recursive max allowed depth 579 * \param prev top of stack of parent subdirectories (should be NULL initially) 580 * \param printer callback 581 * \param ctx opaque object to be passed to callback 582 * 583 * \return 0 success, otherwise error 584 * 585 * \note if neither f0 nor f1 is NULL, then they must be equivalent strings. 586 * \note f0 and f1 cannot be both NULL, unless prev is NULL 587 */ 588 static int 589 diff_file (const char *r0, const char *f0, const char *r1, const char *f1, int recursive, const DNODE *prev, DFUNC printer, void *ctx) 590 { 591 int rv; 592 char *p0, *p1; 593 struct stat st[2]; 594 595 if (prev != NULL) { 596 if (f0 == NULL) { 597 return printer(ctx, ADD_CH, NULL, f1); 598 } 599 if (f1 == NULL) { 600 return printer(ctx, DEL_CH, f0, NULL); 601 } 602 } 603 p0 = make_1st_path(r0, f0); 604 p1 = make_2nd_path(r1, f1); 605 if (p0 == NULL || p1 == NULL) { 606 free_2nd_path(p1); 607 free_1st_path(p0); 608 return -1; 609 } 610 611 if (mc_stat(p0, &st[0]) || mc_stat(p1, &st[1])) { 612 free_2nd_path(p1); 613 free_1st_path(p0); 614 if (prev == NULL) { 615 return -1; 616 } 617 return printer(ctx, ERR_CH, f0, f1); 618 } 619 if ((st[0].st_mode & S_IFMT) != (st[1].st_mode & S_IFMT)) { 620 free_2nd_path(p1); 621 free_1st_path(p0); 622 if (prev == NULL) { 623 return -1; 624 } 625 return printer(ctx, ERR_CH, f0, f1); 626 } 627 if (S_ISDIR(st[0].st_mode)) { 628 const DNODE *n; 629 int found = 0; 630 for (n = prev; n != NULL; n = n->link) { 631 if (n->st[0]->st_ino == st[0].st_ino) { 632 found |= 1; 633 break; 634 } 635 if (n->st[1]->st_ino == st[1].st_ino) { 636 found |= 2; 637 break; 638 } 639 } 640 free_2nd_path(p1); 641 free_1st_path(p0); 642 if (found) { 643 return printer(ctx, ERR_CH, f0, f1); 644 } 645 if (prev == NULL || recursive--) { 646 DNODE node; 647 node.link = prev; 648 node.st[0] = &st[0]; 649 node.st[1] = &st[1]; 650 return diff_dirs(r0, r1, f0, recursive, &node, printer, ctx); 651 } 652 return printer(ctx, DIR_CH, f0, f1); 653 } 654 if (SAME_FILE(st[0], st[1])) { 655 free_2nd_path(p1); 656 free_1st_path(p0); 657 return printer(ctx, EQU_CH, f0, f1); 658 } 659 rv = diff_binary(p0, p1, st); 660 free_2nd_path(p1); 661 free_1st_path(p0); 662 if (rv < 0) { 663 return printer(ctx, ERR_CH, f0, f1); 664 } 665 if (rv == 0) { 666 return printer(ctx, EQU_CH, f0, f1); 667 } 668 return printer(ctx, CHG_CH, f0, f1); 669 } 670 671 672 /* read line *****************************************************************/ 673 674 675 static void 676 cvt_mget(const char *name, char *buf, int width, int skip) 677 { 678 int i, j, len = strlen(name); 679 for (i = skip, j = 0; i < len && j < width; j++, i++) { 680 buf[j] = name[i]; 681 } 682 for (; j < width; j++) { 683 buf[j] = ' '; 684 } 685 buf[j] = '\0'; 686 } 687 688 689 /* diff printers et al *******************************************************/ 690 691 692 static void 693 free_pair (void *p) 694 { 695 LNODE *n = p; 696 if (n->name[0] != NULL) { 697 free(n->name[0]); 698 } else { 699 free(n->name[1]); 700 } 701 } 702 703 704 static int 705 printer (void *ctx, int ch, const char *f0, const char *f1) 706 { 707 ARRAY *z = ctx; 708 LNODE *n; 709 char *p0; 710 char *p1; 711 if (f0 == NULL) { 712 p0 = NULL; 713 p1 = strdup(f1); 714 } else if (f1 == NULL) { 715 p0 = strdup(f0); 716 p1 = NULL; 717 } else { 718 p0 = 719 p1 = strdup(f1); 720 } 721 if (p0 == NULL && p1 == NULL) { 722 z->error |= 2; 723 return -1; 724 } 725 n = arr_enlarge(z); 726 if (n == NULL) { 727 return -1; 728 } 729 n->ch = ch; 730 n->name[0] = p0; 731 n->name[1] = p1; 732 return 0; 733 } 734 735 736 static int 737 calc_diffs (const ARRAY *z) 738 { 739 const LNODE *p = z->data; 740 int i, ndiff = 0; 741 for (i = 0; i < z->len; i++, p++) { 742 if (!is_eq(p->ch) && (i == z->len - 1 || p->ch != (p + 1)->ch)) { 743 ndiff++; 744 } 745 } 746 return ndiff; 747 } 748 749 750 static int 751 redo_diff (WDiff *view) 752 { 753 int rv; 754 arr_init(&view->z, sizeof(LNODE), 256); 755 rv = diff_file(view->dir[0], NULL, view->dir[1], NULL, view->recursive, NULL, printer, &view->z); 756 if (rv != 0 || view->z.error) { 757 arr_free(&view->z, free_pair); 758 return -1; 759 } 760 return calc_diffs(&view->z); 761 } 762 763 764 /* stuff *********************************************************************/ 765 766 767 /** 768 * Read decimal number from string. 769 * 770 * \param[in,out] str string to parse 771 * \param[out] n extracted number 772 * 773 * \return 0 if success, otherwise non-zero 774 */ 775 static int 776 scan_deci (const char **str, int *n) 777 { 778 const char *p = *str; 779 char *q; 780 errno = 0; 781 *n = strtol(p, &q, 10); 782 if (errno || p == q) { 783 return -1; 784 } 785 *str = q; 786 return 0; 787 } 788 789 790 static int 791 get_digits (unsigned int n) 792 { 793 int d = 1; 794 while (n /= 10) { 795 d++; 796 } 797 return d; 798 } 799 800 801 static int 802 get_line_numbers (const ARRAY *a, int ord, int pos, int *linenum, int *lineofs) 803 { 804 *linenum = 0; 805 *lineofs = 0; 806 807 if (a->len) { 808 int i; 809 const LNODE *p; 810 811 if (pos >= a->len) { 812 pos = a->len - 1; 813 } 814 815 for (i = 0, p = a->data; i <= pos; i++, p++) { 816 if (p->name[ord] != NULL) { 817 (*linenum)++; 818 *lineofs = 0; 819 } else { 820 (*lineofs)++; 821 } 822 } 823 } 824 return 0; 825 } 826 827 828 static int 829 calc_nwidth (const ARRAY *a) 830 { 831 int l1, o1; 832 int l2, o2; 833 get_line_numbers(a, 0, a->len - 1, &l1, &o1); 834 get_line_numbers(a, 1, a->len - 1, &l2, &o2); 835 if (l1 < l2) { 836 l1 = l2; 837 } 838 return get_digits(l1); 839 } 840 841 842 static int 843 find_prev_hunk (const ARRAY *a, int pos) 844 { 845 if (pos > 0) { 846 const LNODE *p = a->data; 847 int ch = p[pos].ch; 848 while (pos > 0 && p[pos].ch == ch) { 849 pos--; 850 } 851 while (pos > 0 && is_eq(p[pos].ch)) { 852 pos--; 853 } 854 } 855 return pos; 856 } 857 858 859 static int 860 find_next_hunk (const ARRAY *a, int pos) 861 { 862 if (pos < a->len) { 863 const LNODE *p = a->data; 864 int ch = p[pos].ch; 865 while (pos < a->len && p[pos].ch == ch) { 866 pos++; 867 } 868 while (pos < a->len && is_eq(p[pos].ch)) { 869 pos++; 870 } 871 } 872 return pos; 873 } 874 875 876 /* view routines and callbacks ***********************************************/ 877 878 879 static void 880 view_compute_split (WDiff *view, int i) 881 { 882 view->bias += i; 883 if (view->bias < 2 - view->half1) { 884 view->bias = 2 - view->half1; 885 } 886 if (view->bias > view->half2 - 2) { 887 view->bias = view->half2 - 2; 888 } 889 } 890 891 892 static void 893 view_compute_areas (WDiff *view) 894 { 895 view->height = LINES - 2; 896 view->half1 = COLS / 2; 897 view->half2 = COLS - view->half1; 898 899 view_compute_split(view, 0); 900 } 901 902 903 static int 904 view_init (WDiff *view, int recursive, const char *dir1, const char *dir2) 905 { 906 int ndiff; 907 908 view->dir[0] = dir1; 909 view->dir[1] = dir2; 910 view->recursive = recursive; 911 912 ndiff = redo_diff(view); 913 if (ndiff < 0) { 914 return -1; 915 } 916 917 view->ndiff = ndiff; 918 919 view->view_quit = 0; 920 921 view->bias = 0; 922 view->new_frame = 1; 923 view->skip_rows = 0; 924 view->skip_cols = 0; 925 view->display_symbols = 0; 926 view->display_numbers = 0; 927 view->ord = 0; 928 view->full = 0; 929 view->last_found = -1; 930 931 view_compute_areas(view); 932 return 0; 933 } 934 935 936 static int 937 view_reinit (WDiff *view) 938 { 939 int recursive = view->recursive; 940 int ndiff = view->ndiff; 941 942 diffopt_widgets[2].value = recursive; 943 diffopt_widgets[2].result = &recursive; 944 945 if (quick_dialog(&diffopt) != B_CANCEL) { 946 view->recursive = (recursive != 0) * RECURSIVE_DEPTH; 947 arr_free(&view->z, free_pair); 948 ndiff = redo_diff(view); 949 if (ndiff >= 0) { 950 view->ndiff = ndiff; 951 } 952 } 953 return ndiff; 954 } 955 956 957 static void 958 view_fini (WDiff *view) 959 { 960 arr_free(&view->z, free_pair); 961 } 962 963 964 static int 965 view_display_file (const WDiff *view, int ord, 966 int r, int c, int height, int width) 967 { 968 int i, j, k; 969 char buf[BUFSIZ]; 970 const ARRAY *z = &view->z; 971 int skip = view->skip_cols; 972 int display_symbols = view->display_symbols; 973 int display_numbers = view->display_numbers; 974 const LNODE *p; 975 976 int nwidth = display_numbers; 977 int xwidth = display_symbols + display_numbers; 978 979 if (xwidth) { 980 if (xwidth > width && display_symbols) { 981 xwidth--; 982 display_symbols = 0; 983 } 984 if (xwidth > width && display_numbers) { 985 xwidth = width; 986 display_numbers = width; 987 } 988 989 xwidth++; 990 991 c += xwidth; 992 width -= xwidth; 993 994 if (width < 0) { 995 width = 0; 996 } 997 } 998 999 if ((int)sizeof(buf) <= width || (int)sizeof(buf) <= nwidth) { 1000 /* abnormal, but avoid buffer overflow */ 1001 return -1; 1002 } 1003 1004 for (i = view->skip_rows, j = 0, p = (LNODE *)z->data + i; i < z->len && j < height; p++, j++, i++) { 1005 int ch = p->ch; 1006 tty_setcolor(NORMAL_COLOR); 1007 if (p->name[ord]) { 1008 if (ch == DEL_CH) { 1009 ch = ADD_CH; 1010 } 1011 if (display_symbols) { 1012 tty_gotoyx(r + j, c - 2); 1013 tty_print_char(ch); 1014 } 1015 if (display_numbers) { 1016 int linenum, lineofs; 1017 get_line_numbers(&view->z, ord, i, &linenum, &lineofs); 1018 tty_gotoyx(r + j, c - xwidth); 1019 snprintf(buf, display_numbers + 1, "%*d", nwidth, linenum); 1020 tty_print_string(buf); 1021 } 1022 if (ch == ADD_CH) { 1023 tty_setcolor(DFFADD_COLOR); 1024 } 1025 if (ch == CHG_CH) { 1026 tty_setcolor(DFFCHG_COLOR); 1027 } 1028 if (ch == ERR_CH) { 1029 tty_setcolor(STALE_LINK_COLOR); 1030 } 1031 if (ch == DIR_CH) { 1032 tty_setcolor(DIRECTORY_COLOR); 1033 } 1034 if (i == view->last_found) { 1035 tty_setcolor(MARKED_SELECTED_COLOR); 1036 } 1037 cvt_mget(p->name[ord], buf, width, skip); 1038 } else { 1039 if (ch == ADD_CH) { 1040 ch = DEL_CH; 1041 } 1042 if (display_symbols) { 1043 tty_gotoyx(r + j, c - 2); 1044 tty_print_char(ch); 1045 } 1046 if (display_numbers) { 1047 tty_gotoyx(r + j, c - xwidth); 1048 memset(buf, ' ', display_numbers); 1049 buf[display_numbers] = '\0'; 1050 tty_print_nstring(buf, display_numbers); 1051 } 1052 if (ch == DEL_CH) { 1053 tty_setcolor(DFFCHD_COLOR); /* XXX perhaps this sucks? */ 1054 } 1055 if (ch == CHG_CH) { 1056 tty_setcolor(DFFCHG_COLOR); 1057 } 1058 if (ch == ERR_CH) { 1059 tty_setcolor(STALE_LINK_COLOR); 1060 } 1061 if (ch == DIR_CH) { 1062 tty_setcolor(DIRECTORY_COLOR); 1063 } 1064 memset(buf, ' ', width); 1065 buf[width] = '\0'; 1066 } 1067 tty_gotoyx(r + j, c); 1068 tty_print_nstring(buf, width); 1069 } 1070 tty_setcolor(NORMAL_COLOR); 1071 k = width; 1072 if (width < xwidth - 1) { 1073 k = xwidth - 1; 1074 } 1075 memset(buf, ' ', k); 1076 buf[k] = '\0'; 1077 for (; j < height; j++) { 1078 if (xwidth) { 1079 tty_gotoyx(r + j, c - xwidth); 1080 tty_print_nstring(buf, xwidth - 1); 1081 } 1082 tty_gotoyx(r + j, c); 1083 tty_print_nstring(buf, width); 1084 } 1085 1086 return 0; 1087 } 1088 1089 1090 static void 1091 view_status (const WDiff *view, int ord, int width, int c) 1092 { 1093 int skip_rows = view->skip_rows; 1094 int skip_cols = view->skip_cols; 1095 1096 char buf[BUFSIZ]; 1097 int filename_width; 1098 int linenum, lineofs; 1099 1100 tty_setcolor(SELECTED_COLOR); 1101 1102 tty_gotoyx(0, c); 1103 get_line_numbers(&view->z, ord, skip_rows, &linenum, &lineofs); 1104 1105 filename_width = width - 22; 1106 if (filename_width < 8) { 1107 filename_width = 8; 1108 } 1109 if (filename_width >= (int)sizeof(buf)) { 1110 /* abnormal, but avoid buffer overflow */ 1111 filename_width = sizeof(buf) - 1; 1112 } 1113 trim(strip_home_and_password(view->dir[ord]), buf, filename_width); 1114 if (ord == 0) { 1115 tty_printf("%-*s %6d+%-4d Col %-4d ", filename_width, buf, linenum, lineofs, skip_cols); 1116 } else { 1117 tty_printf("%-*s %6d+%-4d Dif %-4d ", filename_width, buf, linenum, lineofs, view->ndiff); 1118 } 1119 } 1120 1121 1122 static void 1123 view_update (WDiff *view) 1124 { 1125 int height = view->height; 1126 int width1; 1127 int width2; 1128 1129 int last = view->z.len - 1; 1130 1131 if (view->skip_rows > last) { 1132 view->skip_rows = last; 1133 } 1134 if (view->skip_rows < 0) { 1135 view->skip_rows = 0; 1136 } 1137 if (view->skip_cols < 0) { 1138 view->skip_cols = 0; 1139 } 1140 1141 if (height < 2) { 1142 return; 1143 } 1144 1145 width1 = view->half1 + view->bias; 1146 width2 = view->half2 - view->bias; 1147 if (view->full) { 1148 width1 = COLS; 1149 width2 = 0; 1150 } 1151 1152 if (view->new_frame) { 1153 Dlg_head *h = view->widget.parent; 1154 1155 int xwidth = view->display_symbols + view->display_numbers; 1156 1157 tty_setcolor(NORMAL_COLOR); 1158 if (width1 > 1) { 1159 draw_double_box(h, 1, 0, height, width1); 1160 } 1161 if (width2 > 1) { 1162 draw_double_box(h, 1, width1, height, width2); 1163 } 1164 1165 if (xwidth) { 1166 xwidth++; 1167 if (xwidth < width1 - 1) { 1168 tty_gotoyx(1, xwidth); 1169 tty_print_alt_char(ACS_TTEE); 1170 tty_gotoyx(height, xwidth); 1171 tty_print_alt_char(ACS_BTEE); 1172 tty_print_vline(2, xwidth, height - 2); 1173 } 1174 if (xwidth < width2 - 1) { 1175 tty_gotoyx(1, width1 + xwidth); 1176 tty_print_alt_char(ACS_TTEE); 1177 tty_gotoyx(height, width1 + xwidth); 1178 tty_print_alt_char(ACS_BTEE); 1179 tty_print_vline(2, width1 + xwidth, height - 2); 1180 } 1181 } 1182 1183 view->new_frame = 0; 1184 } 1185 1186 if (width1 > 2) { 1187 view_status(view, view->ord, width1, 0); 1188 view_display_file(view, view->ord, 2, 1, height - 2, width1 - 2); 1189 } 1190 if (width2 > 2) { 1191 view_status(view, view->ord ^ 1, width2, width1); 1192 view_display_file(view, view->ord ^ 1, 2, width1 + 1, height - 2, width2 - 2); 1193 } 1194 } 1195 1196 1197 static void 1198 view_redo (WDiff *view) 1199 { 1200 if (view_reinit(view) < 0) { 1201 view->view_quit = 1; 1202 } else if (view->display_numbers) { 1203 int old = view->display_numbers; 1204 view->display_numbers = calc_nwidth(&view->z); 1205 view->new_frame = (old != view->display_numbers); 1206 } 1207 } 1208 1209 1210 #define IS_WHOLE_OR_DONT_CARE() \ 1211 (!whole || ( \ 1212 (i == 0 || strchr(wholechars, haystack[i - 1]) == NULL) && \ 1213 (i + nlen == hlen || strchr(wholechars, haystack[i + nlen]) == NULL) \ 1214 )) 1215 1216 1217 static const unsigned char * 1218 memmem_dummy (const unsigned char *haystack, size_t i, size_t hlen, const unsigned char *needle, size_t nlen, int whole) 1219 { 1220 for (; i + nlen <= hlen; i++) { 1221 if (haystack[i] == needle[0]) { 1222 size_t j; 1223 for (j = 1; j < nlen; j++) { 1224 if (haystack[i + j] != needle[j]) { 1225 break; 1226 } 1227 } 1228 if (j == nlen && IS_WHOLE_OR_DONT_CARE()) { 1229 return haystack + i; 1230 } 1231 } 1232 } 1233 1234 return NULL; 1235 } 1236 1237 1238 static const unsigned char * 1239 memmem_dummy_nocase (const unsigned char *haystack, size_t i, size_t hlen, const unsigned char *needle, size_t nlen, int whole) 1240 { 1241 for (; i + nlen <= hlen; i++) { 1242 if (toupper(haystack[i]) == toupper(needle[0])) { 1243 size_t j; 1244 for (j = 1; j < nlen; j++) { 1245 if (toupper(haystack[i + j]) != toupper(needle[j])) { 1246 break; 1247 } 1248 } 1249 if (j == nlen && IS_WHOLE_OR_DONT_CARE()) { 1250 return haystack + i; 1251 } 1252 } 1253 } 1254 1255 return NULL; 1256 } 1257 1258 1259 static const unsigned char * 1260 search_string (const char *haystack, size_t xpos, const void *needle, size_t nlen, int whole, int ccase) 1261 { 1262 size_t hlen = strlen(haystack); 1263 1264 if (xpos > hlen || nlen <= 0 || haystack == NULL || needle == NULL) { 1265 return NULL; 1266 } 1267 1268 /* XXX I should use strstr */ 1269 if (ccase) { 1270 return memmem_dummy((const unsigned char *)haystack, xpos, hlen, needle, nlen, whole); 1271 } else { 1272 return memmem_dummy_nocase((const unsigned char *)haystack, xpos, hlen, needle, nlen, whole); 1273 } 1274 } 1275 1276 1277 static int 1278 view_search_string (WDiff *view, const char *needle, int ccase, int back, int whole) 1279 { 1280 size_t nlen = strlen(needle); 1281 size_t xpos = 0; 1282 1283 int ord = view->ord; 1284 const ARRAY *a = &view->z; 1285 const LNODE *p; 1286 1287 int i = view->last_found; 1288 1289 if (back) { 1290 if (i == -1) { 1291 i = view->skip_rows; 1292 } 1293 for (--i, p = (LNODE *)a->data + i; i >= 0; p--, i--) { 1294 if (p->name[ord]) { 1295 const unsigned char *q = search_string(p->name[ord], xpos, needle, nlen, whole, ccase); 1296 if (q != NULL) { 1297 return i; 1298 } 1299 } 1300 } 1301 } else { 1302 if (i == -1) { 1303 i = view->skip_rows - 1; 1304 } 1305 for (++i, p = (LNODE *)a->data + i; i < a->len; p++, i++) { 1306 if (p->name[ord]) { 1307 const unsigned char *q = search_string(p->name[ord], xpos, needle, nlen, whole, ccase); 1308 if (q != NULL) { 1309 return i; 1310 } 1311 } 1312 } 1313 } 1314 1315 return -1; 1316 } 1317 1318 1319 static void 1320 view_search (WDiff *view, int again) 1321 { 1322 /* XXX some statics here, to be remembered between runs */ 1323 static char *searchopt_text = NULL; 1324 static int searchopt_type; 1325 static int searchopt_case; 1326 static int searchopt_backwards; 1327 static int searchopt_whole; 1328 1329 static int compiled = 0; 1330 1331 if (again < 0) { 1332 g_free(searchopt_text); 1333 searchopt_text = NULL; 1334 if (compiled) { 1335 compiled = 0; 1336 /*XXX free search exp*/ 1337 } 1338 return; 1339 } 1340 1341 if (!again || searchopt_text == NULL) { 1342 char *tsearchopt_text; 1343 int tsearchopt_type = searchopt_type; 1344 int tsearchopt_case = searchopt_case; 1345 int tsearchopt_backwards = searchopt_backwards; 1346 int tsearchopt_whole = searchopt_whole; 1347 1348 search_widgets[2].result = &tsearchopt_whole; 1349 search_widgets[3].result = &tsearchopt_backwards; 1350 search_widgets[4].result = &tsearchopt_case; 1351 search_widgets[5].result = &tsearchopt_type; 1352 search_widgets[6].str_result = &tsearchopt_text; 1353 search_widgets[6].text = searchopt_text; 1354 1355 if (quick_dialog(&search_input) == B_CANCEL) { 1356 return; 1357 } 1358 if (tsearchopt_text == NULL || !*tsearchopt_text) { 1359 g_free(tsearchopt_text); 1360 return; 1361 } 1362 g_free(searchopt_text); 1363 1364 searchopt_text = tsearchopt_text; 1365 searchopt_type = tsearchopt_type; 1366 searchopt_case = tsearchopt_case; 1367 searchopt_backwards = tsearchopt_backwards; 1368 searchopt_whole = tsearchopt_whole; 1369 } 1370 1371 if (compiled) { 1372 compiled = 0; 1373 /*XXX free search exp*/ 1374 } 1375 if (0/*XXX new search exp*/) { 1376 error_dialog(_("Error"), _(" Cannot search ")); 1377 return; 1378 } 1379 compiled = 1; 1380 if (searchopt_type == 0) { 1381 view->last_found = view_search_string(view, searchopt_text, searchopt_case, searchopt_backwards, searchopt_whole); 1382 } 1383 1384 if (view->last_found == -1) { 1385 error_dialog(_("Search"), _(" Search string not found ")); 1386 } else { 1387 view->skip_rows = view->last_found; 1388 view_update(view); 1389 } 1390 } 1391 1392 1393 static void 1394 view_search_cmd (WDiff *view) 1395 { 1396 view_search(view, 0); 1397 } 1398 1399 1400 static void 1401 view_edit (WDiff *view, int ord) 1402 { 1403 const ARRAY *z = &view->z; 1404 const LNODE *p = z->data; 1405 const char *s = p[view->skip_rows].name[ord]; 1406 1407 if (s != NULL) { 1408 char buf[2 * PATH_MAX]; /* XXX for _bufpath */ 1409 s = make_tmp_path(view->dir[ord], s); 1410 if (s != NULL) { 1411 do_edit_at_line(s, 0); 1412 free_tmp_path(s); 1413 view_redo(view); 1414 view_update(view); 1415 } 1416 } 1417 } 1418 1419 1420 static void 1421 view_edit_cmd (WDiff *view) 1422 { 1423 view_edit(view, view->ord); 1424 } 1425 1426 1427 static void 1428 view_goto_cmd (WDiff *view, int ord) 1429 { 1430 static const char *title[2] = { " Goto line (left) ", " Goto line (right) " }; 1431 static char prev[256]; 1432 /* XXX some statics here, to be remembered between runs */ 1433 1434 int newline; 1435 char *input; 1436 1437 input = input_dialog(_(title[ord]), _(" Enter line: "), MC_HISTORY_ZDIFF_GOTO_LINE, prev); 1438 if (input != NULL) { 1439 const char *s = input; 1440 if (scan_deci(&s, &newline) == 0 && *s == '\0') { 1441 int i = 0; 1442 if (newline > 0) { 1443 const LNODE *p; 1444 int j = 0; 1445 for (p = view->z.data; i < view->z.len; i++, p++) { 1446 if (p->name[ord] != NULL) { 1447 j++; 1448 } 1449 if (j == newline) { 1450 break; 1451 } 1452 } 1453 } 1454 view->skip_rows = i; 1455 snprintf(prev, sizeof(prev), "%d", newline); 1456 } 1457 g_free(input); 1458 } 1459 } 1460 1461 1462 static void 1463 view_help_cmd (void) 1464 { 1465 interactive_display(NULL, "[Directory Diff Viewer]"); 1466 } 1467 1468 1469 static void 1470 view_quit_cmd (WDiff *view) 1471 { 1472 dlg_stop(view->widget.parent); 1473 } 1474 1475 1476 static void 1477 view_labels (WDiff *view) 1478 { 1479 Dlg_head *h = view->widget.parent; 1480 1481 buttonbar_set_label(h, 1, Q_("ButtonBar|Help"), view_help_cmd); 1482 1483 buttonbar_set_label_data(h, 4, Q_("ButtonBar|Edit"), (buttonbarfn)view_edit_cmd, view); 1484 buttonbar_set_label_data(h, 7, Q_("ButtonBar|Search"), (buttonbarfn)view_search_cmd, view); 1485 buttonbar_set_label_data(h, 10, Q_("ButtonBar|Quit"), (buttonbarfn)view_quit_cmd, view); 1486 } 1487 1488 1489 static int 1490 view_event (Gpm_Event *event, void *x) 1491 { 1492 WDiff *view = (WDiff *)x; 1493 int result = MOU_NORMAL; 1494 1495 /* We are not interested in the release events */ 1496 if (!(event->type & (GPM_DOWN | GPM_DRAG))) { 1497 return result; 1498 } 1499 1500 /* Wheel events */ 1501 if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN)) { 1502 view->skip_rows -= 2; 1503 view_update(view); 1504 return result; 1505 } 1506 if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN)) { 1507 view->skip_rows += 2; 1508 view_update(view); 1509 return result; 1510 } 1511 1512 return result; 1513 } 1514 1515 1516 static cb_ret_t 1517 view_handle_key (WDiff *view, int c) 1518 { 1519 c = convert_from_input_c(c); 1520 1521 /* XXX add copy/move/delete; add file masks with shell patterns */ 1522 1523 switch (c) { 1524 case 's': 1525 view->display_symbols ^= 1; 1526 view->new_frame = 1; 1527 return MSG_HANDLED; 1528 1529 case 'l': 1530 view->display_numbers ^= calc_nwidth(&view->z); 1531 view->new_frame = 1; 1532 return MSG_HANDLED; 1533 1534 case 'f': 1535 view->full ^= 1; 1536 view->new_frame = 1; 1537 return MSG_HANDLED; 1538 1539 case '=': 1540 if (!view->full) { 1541 view->bias = 0; 1542 view->new_frame = 1; 1543 } 1544 return MSG_HANDLED; 1545 1546 case '>': 1547 if (!view->full) { 1548 view_compute_split(view, 1); 1549 view->new_frame = 1; 1550 } 1551 return MSG_HANDLED; 1552 1553 case '<': 1554 if (!view->full) { 1555 view_compute_split(view, -1); 1556 view->new_frame = 1; 1557 } 1558 return MSG_HANDLED; 1559 1560 case XCTRL('u'): 1561 view->ord ^= 1; 1562 return MSG_HANDLED; 1563 1564 case XCTRL('r'): 1565 view_redo(view); 1566 return MSG_HANDLED; 1567 1568 case '\n': 1569 view_diff_cmd(view); 1570 return MSG_HANDLED; 1571 1572 case 'n': 1573 view->skip_rows = find_next_hunk(&view->z, view->skip_rows); 1574 return MSG_HANDLED; 1575 1576 case 'p': 1577 view->skip_rows = find_prev_hunk(&view->z, view->skip_rows); 1578 return MSG_HANDLED; 1579 1580 case 'g': 1581 case 'G': 1582 view_goto_cmd(view, c == 'G'); 1583 return MSG_HANDLED; 1584 1585 case KEY_BACKSPACE: 1586 view->last_found = -1; 1587 return MSG_HANDLED; 1588 1589 case KEY_F(4): 1590 view_edit(view, view->ord); 1591 return MSG_HANDLED; 1592 1593 case KEY_F(14): 1594 view_edit(view, view->ord ^ 1); 1595 return MSG_HANDLED; 1596 1597 case KEY_F(17): 1598 view_search(view, 1); 1599 return MSG_HANDLED; 1600 1601 case KEY_HOME: 1602 case KEY_M_CTRL | KEY_PPAGE: 1603 view->skip_rows = 0; 1604 return MSG_HANDLED; 1605 1606 case KEY_END: 1607 case KEY_M_CTRL | KEY_NPAGE: 1608 view->skip_rows = view->z.len - 1; 1609 return MSG_HANDLED; 1610 1611 case KEY_UP: 1612 view->skip_rows--; 1613 return MSG_HANDLED; 1614 1615 case KEY_DOWN: 1616 view->skip_rows++; 1617 return MSG_HANDLED; 1618 1619 case KEY_NPAGE: 1620 view->skip_rows += view->height - 2; 1621 return MSG_HANDLED; 1622 1623 case KEY_PPAGE: 1624 view->skip_rows -= view->height - 2; 1625 return MSG_HANDLED; 1626 1627 case KEY_LEFT: 1628 view->skip_cols--; 1629 return MSG_HANDLED; 1630 1631 case KEY_RIGHT: 1632 view->skip_cols++; 1633 return MSG_HANDLED; 1634 1635 case KEY_M_CTRL | KEY_LEFT: 1636 view->skip_cols -= 8; 1637 return MSG_HANDLED; 1638 1639 case KEY_M_CTRL | KEY_RIGHT: 1640 view->skip_cols += 8; 1641 return MSG_HANDLED; 1642 1643 case XCTRL('a'): 1644 view->skip_cols = 0; 1645 return MSG_HANDLED; 1646 1647 case XCTRL('o'): 1648 view_other_cmd(); 1649 return MSG_HANDLED; 1650 1651 case 'q': 1652 case XCTRL('g'): 1653 case ESC_CHAR: 1654 view->view_quit = 1; 1655 return MSG_HANDLED; 1656 } 1657 1658 /* Key not used */ 1659 return MSG_NOT_HANDLED; 1660 } 1661 1662 1663 static cb_ret_t 1664 view_callback (Widget *w, widget_msg_t msg, int parm) 1665 { 1666 cb_ret_t i; 1667 WDiff *view = (WDiff *)w; 1668 Dlg_head *h = view->widget.parent; 1669 1670 switch (msg) { 1671 case WIDGET_INIT: 1672 view_labels(view); 1673 return MSG_HANDLED; 1674 1675 case WIDGET_DRAW: 1676 view->new_frame = 1; 1677 view_update(view); 1678 return MSG_HANDLED; 1679 1680 case WIDGET_CURSOR: 1681 return MSG_HANDLED; 1682 1683 case WIDGET_KEY: 1684 i = view_handle_key((WDiff *)view, parm); 1685 if (view->view_quit) 1686 dlg_stop(h); 1687 else { 1688 view_update(view); 1689 } 1690 return i; 1691 1692 case WIDGET_IDLE: 1693 return MSG_HANDLED; 1694 1695 case WIDGET_FOCUS: 1696 view_labels(view); 1697 return MSG_HANDLED; 1698 1699 case WIDGET_DESTROY: 1700 return MSG_HANDLED; 1701 1702 default: 1703 return default_proc(msg, parm); 1704 } 1705 } 1706 1707 1708 static void 1709 view_adjust_size (Dlg_head *h) 1710 { 1711 WDiff *view; 1712 WButtonBar *bar; 1713 1714 /* Look up the viewer and the buttonbar, we assume only two widgets here */ 1715 view = (WDiff *)find_widget_type(h, view_callback); 1716 bar = find_buttonbar(h); 1717 widget_set_size(&view->widget, 0, 0, LINES, COLS); 1718 widget_set_size((Widget *)bar, LINES - 1, 0, 1, COLS); 1719 1720 view_compute_areas(view); 1721 } 1722 1723 1724 static cb_ret_t 1725 view_dialog_callback (Dlg_head *h, dlg_msg_t msg, int parm) 1726 { 1727 switch (msg) { 1728 case DLG_RESIZE: 1729 view_adjust_size(h); 1730 return MSG_HANDLED; 1731 1732 default: 1733 return default_dlg_callback(h, msg, parm); 1734 } 1735 } 1736 1737 1738 int 1739 zdiff_view (const char *dir1, const char *dir2) 1740 { 1741 int error; 1742 WDiff *view; 1743 WButtonBar *bar; 1744 Dlg_head *view_dlg; 1745 1746 /* Create dialog and widgets, put them on the dialog */ 1747 view_dlg = 1748 create_dlg(0, 0, LINES, COLS, NULL, view_dialog_callback, 1749 "[Directory Diff Viewer]", NULL, DLG_WANT_TAB); 1750 1751 view = g_new0(WDiff, 1); 1752 1753 init_widget(&view->widget, 0, 0, LINES, COLS, 1754 (callback_fn)view_callback, 1755 (mouse_h)view_event); 1756 1757 widget_want_cursor(view->widget, 0); 1758 1759 bar = buttonbar_new(1); 1760 1761 add_widget(view_dlg, bar); 1762 add_widget(view_dlg, view); 1763 1764 error = view_init(view, 0, dir1, dir2); 1765 1766 /* Please note that if you add another widget, 1767 * you have to modify view_adjust_size to 1768 * be aware of it 1769 */ 1770 if (!error) { 1771 run_dlg(view_dlg); 1772 view_search(view, -1); 1773 view_fini(view); 1774 } 1775 destroy_dlg(view_dlg); 1776 1777 return error; 1778 } 1779 1780 1781 #define GET_FILE_AND_STAMP(n) \ 1782 do { \ 1783 use_copy##n = 0; \ 1784 real_file##n = file##n; \ 1785 if (!vfs_file_is_local(file##n)) { \ 1786 real_file##n = mc_getlocalcopy(file##n); \ 1787 if (real_file##n != NULL) { \ 1788 use_copy##n = 1; \ 1789 if (mc_stat(real_file##n, &st##n) != 0) { \ 1790 use_copy##n = -1; \ 1791 } \ 1792 } \ 1793 } \ 1794 } while (0) 1795 #define UNGET_FILE(n) \ 1796 do { \ 1797 if (use_copy##n) { \ 1798 int changed = 0; \ 1799 if (use_copy##n > 0) { \ 1800 time_t mtime = st##n.st_mtime; \ 1801 if (mc_stat(real_file##n, &st##n) == 0) { \ 1802 changed = (mtime != st##n.st_mtime); \ 1803 } \ 1804 } \ 1805 mc_ungetlocalcopy(file##n, real_file##n, changed); \ 1806 g_free(real_file##n); \ 1807 } \ 1808 } while (0) 1809 void 1810 view_diff_cmd (void *obj) 1811 { 1812 int rv = 0; 1813 char *file0 = NULL; 1814 char *file1 = NULL; 1815 WDiff *view = obj; 1816 int is_dir0 = 0; 1817 int is_dir1 = 0; 1818 1819 if (view == NULL) { 1820 const WPanel *panel0 = current_panel; 1821 const WPanel *panel1 = other_panel; 1822 if (get_current_index()) { 1823 panel0 = other_panel; 1824 panel1 = current_panel; 1825 } 1826 file0 = concat_dir_and_file(panel0->cwd, selection(panel0)->fname); 1827 file1 = concat_dir_and_file(panel1->cwd, selection(panel1)->fname); 1828 is_dir0 = S_ISDIR(selection(panel0)->st.st_mode); 1829 is_dir1 = S_ISDIR(selection(panel1)->st.st_mode); 1830 } else { 1831 int ord = view->ord; 1832 const ARRAY *z = &view->z; 1833 const LNODE *p = (LNODE *)z->data + view->skip_rows; 1834 if (p->name[0] == NULL || p->name[1] == NULL || p->ch == ERR_CH) { 1835 return; 1836 } 1837 file0 = strpath(view->dir[ord], p->name[ord]); 1838 file1 = strpath(view->dir[ord ^ 1], p->name[ord ^ 1]); 1839 if (p->ch == DIR_CH) { 1840 is_dir0 = is_dir1 = 1; 1841 } 1842 } 1843 1844 if (rv == 0) { 1845 rv = -1; 1846 if (file0 != NULL && file1 != NULL) { 1847 if (is_dir0 && is_dir1) { 1848 rv = zdiff_view(file0, file1); 1849 } else { 1850 if (!is_dir0 && !is_dir1) { 1851 int use_copy0; 1852 int use_copy1; 1853 struct stat st0; 1854 struct stat st1; 1855 char *real_file0; 1856 char *real_file1; 1857 GET_FILE_AND_STAMP(0); 1858 GET_FILE_AND_STAMP(1); 1859 if (real_file0 != NULL && real_file1 != NULL) { 1860 rv = diff_view(real_file0, real_file1, file0, file1); 1861 } 1862 UNGET_FILE(1); 1863 UNGET_FILE(0); 1864 } 1865 } 1866 } 1867 } 1868 1869 free(file1); 1870 free(file0); 1871 1872 if (rv != 0) { 1873 message (1, MSG_ERROR, _(" Error building diff ")); 1874 } 1875 } 1876 #endif -
mc-4.7.0-pre1
diff -Naur mc-4.7.0-pre1~/src/zdiff.h mc-4.7.0-pre1/src/zdiff.h
old new 1 #ifndef ZDIFF_H_included 2 #define ZDIFF_H_included 3 4 void view_diff_cmd (void *obj); 5 6 #endif