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  
    739739/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */ 
    740740#undef TIME_WITH_SYS_TIME 
    741741 
     742/* Define to enable diff viewer */ 
     743#undef USE_DIFF_VIEW 
     744 
    742745/* Define to enable undelete support on ext2 */ 
    743746#undef USE_EXT2FSLIB 
    744747 
  • mc-4.7.0-pre1

    diff -Naur mc-4.7.0-pre1~/configure mc-4.7.0-pre1/configure
    old new  
    801801USE_VFS_NET_TRUE 
    802802USE_VFS_FALSE 
    803803USE_VFS_TRUE 
     804USE_DIFF_FALSE 
     805USE_DIFF_TRUE 
    804806USE_EDIT_FALSE 
    805807USE_EDIT_TRUE 
    806808subdirs 
     
    10951097with_slang_includes 
    10961098with_slang_libs 
    10971099with_edit 
     1100with_diff 
    10981101enable_background 
    10991102enable_charset 
    11001103' 
     
    18251828  --with-slang-libs=[DIR] set path to SLANG library [default=/usr/lib]; may 
    18261829                          sense only if --with-screen=slang 
    18271830  --with-edit              Enable internal editor [yes] 
     1831  --with-diff              Enable diff viewer [yes] 
    18281832 
    18291833Some influential environment variables: 
    18301834  CC          C compiler command 
     
    4074340747fi 
    4074440748 
    4074540749 
     40750 
     40751# Check whether --with-diff was given. 
     40752if test "${with_diff+set}" = set; then 
     40753  withval=$with_diff; 
     40754fi 
     40755 
     40756 
     40757if test x$with_diff != xno; then 
     40758 
     40759cat >>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;} 
     40767else 
     40768        diff_msg="no" 
     40769fi 
     40770 
     40771 
    4074640772cons_saver="" 
    4074740773case $host_os in 
    4074840774linux*) 
     
    4082040846  USE_EDIT_FALSE= 
    4082140847fi 
    4082240848 
     40849 if test -n "$use_diff"; then 
     40850  USE_DIFF_TRUE= 
     40851  USE_DIFF_FALSE='#' 
     40852else 
     40853  USE_DIFF_TRUE='#' 
     40854  USE_DIFF_FALSE= 
     40855fi 
     40856 
    4082340857 if test "x$use_vfs" = xyes; then 
    4082440858  USE_VFS_TRUE= 
    4082540859  USE_VFS_FALSE='#' 
     
    4128541319Usually this means the macro was only invoked conditionally." >&2;} 
    4128641320   { (exit 1); exit 1; }; } 
    4128741321fi 
     41322if test -z "${USE_DIFF_TRUE}" && test -z "${USE_DIFF_FALSE}"; then 
     41323  { { $as_echo "$as_me:$LINENO: error: conditional \"USE_DIFF\" was never defined. 
     41324Usually this means the macro was only invoked conditionally." >&5 
     41325$as_echo "$as_me: error: conditional \"USE_DIFF\" was never defined. 
     41326Usually this means the macro was only invoked conditionally." >&2;} 
     41327   { (exit 1); exit 1; }; } 
     41328fi 
    4128841329if test -z "${USE_VFS_TRUE}" && test -z "${USE_VFS_FALSE}"; then 
    4128941330  { { $as_echo "$as_me:$LINENO: error: conditional \"USE_VFS\" was never defined. 
    4129041331Usually this means the macro was only invoked conditionally." >&5 
     
    4395843999  X11 events support:         ${textmode_x11_support} 
    4395944000  With subshell support:      ${subshell} 
    4396044001  Internal editor:            ${edit_msg} 
     44002  Diff viewer:                ${diff_msg} 
    4396144003  Support for charset:        ${charset_msg} 
    4396244004  Search type:                ${SEARCH_TYPE} 
    4396344005" 
  • configure.ac

    diff -Naur mc-4.7.0-pre1~/configure.ac mc-4.7.0-pre1/configure.ac
    old new  
    475475fi 
    476476 
    477477 
     478dnl 
     479dnl Diff viewer support. 
     480dnl 
     481AC_ARG_WITH(diff, 
     482        [  --with-diff              Enable diff viewer [[yes]]]) 
     483 
     484if 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]) 
     489else 
     490        diff_msg="no" 
     491fi 
     492 
     493 
    478494dnl Check if the OS is supported by the console saver. 
    479495cons_saver="" 
    480496case $host_os in 
     
    533549fi 
    534550 
    535551AM_CONDITIONAL(USE_EDIT, [test -n "$use_edit"]) 
     552AM_CONDITIONAL(USE_DIFF, [test -n "$use_diff"]) 
    536553AM_CONDITIONAL(USE_VFS, [test "x$use_vfs" = xyes]) 
    537554AM_CONDITIONAL(USE_VFS_NET, [test x"$use_net_code" = xtrue]) 
    538555AM_CONDITIONAL(USE_UNDEL_FS, [test -n "$use_undelfs"]) 
     
    628645  X11 events support:         ${textmode_x11_support} 
    629646  With subshell support:      ${subshell} 
    630647  Internal editor:            ${edit_msg} 
     648  Diff viewer:                ${diff_msg} 
    631649  Support for charset:        ${charset_msg} 
    632650  Search type:                ${SEARCH_TYPE} 
    633651" 
  • 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  
    26422642is disabled, the dialog pops up only if you press 
    26432643.B Alt-Tab 
    26442644for the second time, for the first time MC just beeps. 
     2645.\"NODE "Diff Viewer" 
     2646.SH "Diff Viewer" 
     2647Side-by-side diff output. 
     2648.PP 
     2649.B C-l 
     2650Refresh the screen. 
     2651.PP 
     2652.B C-o 
     2653Switch to the subshell and show the command screen. 
     2654.PP 
     2655.B C-u 
     2656Swap the contents of the two panels. 
     2657.PP 
     2658.B C-r 
     2659Redo diff. 
     2660.I BUG: 
     2661does not re-read files from 
     2662.\"LINK2" 
     2663VFS\&. 
     2664.\"Virtual File System" 
     2665.PP 
     2666.B f 
     2667Display each file full size (use 
     2668.I C-u 
     2669to switch between files). 
     2670.PP 
     2671.BR < ", " = ", " > 
     2672Alter panel split. 
     2673.PP 
     2674.B s 
     2675Display diff symbols (useful in 
     2676.I nocolor 
     2677mode). 
     2678.PP 
     2679.B l 
     2680Display line numbers in each panel. 
     2681.PP 
     2682.B c 
     2683Hide carriage returns. 
     2684.PP 
     2685.B h 
     2686Toggle horizontal diff (if available). 
     2687.PP 
     2688.BR 2 ", " 3 ", " 4 ", " 8 
     2689Set tabstop at desired number of spaces. 
     2690.PP 
     2691.B n 
     2692Go to next hunk. 
     2693.PP 
     2694.B p 
     2695Go to previous hunk. 
     2696.PP 
     2697.BR g ", " G 
     2698Go to line number in left, respectively right panel. 
     2699.PP 
     2700.BR backspace 
     2701Forget last search. 
     2702.PP 
     2703.BR F4 ", " F14 
     2704Edit left, respectively right file. 
     2705.PP 
     2706.B F7 
     2707Search in left file. 
     2708.I BUG: 
     2709incomplete: 
     2710regexp/scanf style is not implemented; 
     2711performs search only at line level. 
     2712.PP 
     2713.B F17 
     2714Start search if there was no previous search expression else find next match. 
     2715.I BUG: 
     2716see above 
     2717.PP 
     2718.BR home ", " C-prev-page 
     2719Go to first line. 
     2720.PP 
     2721.BR end ", " C-next-page 
     2722Go to last line. 
     2723.PP 
     2724.B up 
     2725Move one line up. 
     2726.PP 
     2727.B down 
     2728Move one line down. 
     2729.PP 
     2730.B prev-page 
     2731Move one page up. 
     2732.PP 
     2733.B next-page 
     2734Move one page down. 
     2735.PP 
     2736.B left 
     2737Move one column left. 
     2738.PP 
     2739.B right 
     2740Move one column right. 
     2741.PP 
     2742.B C-left 
     2743Move 8 columns left. 
     2744.PP 
     2745.B C-right 
     2746Move 8 columns right. 
     2747.PP 
     2748.B C-a 
     2749Move to the first column. 
     2750.PP 
     2751.BR q ", " C-g ", " escape 
     2752Quit. 
     2753.\"NODE "Diff Options" 
     2754.SH "Diff Options" 
     2755.PP 
     2756.B Ignore case 
     2757Ignore changes in case; consider upper- and lower-case letters equivalent. 
     2758.PP 
     2759.B ignore tab Expansion 
     2760Ignore the distinction between tabs and spaces on input.  A tab is considered 
     2761to be equivalent to the number of spaces to the next tab stop.  `diff' assumes 
     2762that tab stops are set every 8 print columns. 
     2763.PP 
     2764.B ignore Space change 
     2765This option is stronger.  Ignore white space at line end, and consider all 
     2766other sequences of one or more white space characters to be equivalent. 
     2767.PP 
     2768.B ignore all Whitespace 
     2769This option is stronger still.  Ignore difference even if one line has 
     2770white space where the other line has none.  "White space" characters include 
     2771tab, newline, vertical tab, form feed, carriage return, and space; some 
     2772locales may define additional characters to be white space. 
     2773.PP 
     2774.B strip trailing CR 
     2775Treat input lines that end in carriage return followed by newline as if 
     2776they end in plain newline.  This can be useful when comparing text that is 
     2777imperfectly imported from many personal computer operating systems.  This 
     2778option does not affect the way the files are displayed, only how lines are 
     2779read, which in turn affects how they are compared. 
     2780.PP 
     2781.B Normal 
     2782Run `diff' with default performance settings. 
     2783.PP 
     2784.B Fastest 
     2785When the files you are comparing are large and have small groups of changes 
     2786scattered throughout them, you can use this option to make a different 
     2787modification to the algorithm that `diff' uses.  If the input files have a 
     2788constant small density of changes, this option speeds up the comparisons 
     2789without changing the output.  If not, `diff' might produce a larger set of 
     2790differences; however, the output will still be correct. 
     2791.PP 
     2792.B Minimal 
     2793Tell `diff' to use a modified algorithm that sometimes produces a smaller set 
     2794of differences.  It can also cause `diff' to run more slowly than usual. 
     2795.\"NODE "Directory Diff Viewer" 
     2796.SH "Directory Diff Viewer" 
     2797Side-by-side directory diff. 
     2798.PP 
     2799.B C-l 
     2800Refresh the screen. 
     2801.PP 
     2802.B C-o 
     2803Switch to the subshell and show the command screen. 
     2804.PP 
     2805.B C-u 
     2806Swap the contents of the two panels. 
     2807.PP 
     2808.B C-r 
     2809Redo diff. 
     2810.PP 
     2811.B f 
     2812Display each file full size (use 
     2813.I C-u 
     2814to switch between files). 
     2815.PP 
     2816.BR < ", " = ", " > 
     2817Alter panel split. 
     2818.PP 
     2819.B s 
     2820Display diff symbols (useful in 
     2821.I nocolor 
     2822mode). 
     2823.PP 
     2824.B l 
     2825Display line numbers in each panel. 
     2826.PP 
     2827.B enter 
     2828Diff items. 
     2829.PP 
     2830.B n 
     2831Go to next hunk. 
     2832.PP 
     2833.B p 
     2834Go to previous hunk. 
     2835.PP 
     2836.BR g ", " G 
     2837Go to line number in left, respectively right panel. 
     2838.PP 
     2839.BR backspace 
     2840Forget last search. 
     2841.PP 
     2842.BR F4 ", " F14 
     2843Edit left, respectively right file. 
     2844.PP 
     2845.B F7 
     2846Search in left panel. 
     2847.I BUG: 
     2848incomplete: 
     2849regexp/scanf style is not implemented. 
     2850.PP 
     2851.B F17 
     2852Start search if there was no previous search expression else find next match. 
     2853.I BUG: 
     2854see above 
     2855.PP 
     2856.BR home ", " C-prev-page 
     2857Go to first line. 
     2858.PP 
     2859.BR end ", " C-next-page 
     2860Go to last line. 
     2861.PP 
     2862.B up 
     2863Move one line up. 
     2864.PP 
     2865.B down 
     2866Move one line down. 
     2867.PP 
     2868.B prev-page 
     2869Move one page up. 
     2870.PP 
     2871.B next-page 
     2872Move one page down. 
     2873.PP 
     2874.B left 
     2875Move one column left. 
     2876.PP 
     2877.B right 
     2878Move one column right. 
     2879.PP 
     2880.B C-left 
     2881Move 8 columns left. 
     2882.PP 
     2883.B C-right 
     2884Move 8 columns right. 
     2885.PP 
     2886.B C-a 
     2887Move to the first column. 
     2888.PP 
     2889.BR q ", " C-g ", " escape 
     2890Quit. 
     2891.\"NODE "Directory Diff Options" 
     2892.SH "Directory Diff Options" 
     2893.PP 
     2894.B Recursive 
     2895Diff subdirectories recursively. 
    26452896.\"NODE "Virtual File System" 
    26462897.SH "Virtual File System" 
    26472898The 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  
    6565        tree.c tree.h treestore.c treestore.h timefmt.h tty.c tty.h user.c      \ 
    6666        user.h util.c util.h utilunix.c view.c view.h vfsdummy.h widget.c       \ 
    6767        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 \ 
    6969        strutil.h strutil.c strutilascii.c strutil8bit.c strutilutf8.c \ 
    7070        search/search.h strescape.c strescape.h 
    7171 
  • src/Makefile.in

    diff -Naur mc-4.7.0-pre1~/src/Makefile.in mc-4.7.0-pre1/src/Makefile.in
    old new  
    120120        timefmt.h tty.c tty.h user.c user.h util.c util.h utilunix.c \ 
    121121        view.c view.h vfsdummy.h widget.c widget.h win.c win.h \ 
    122122        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 
    126127am__objects_1 = achown.$(OBJEXT) background.$(OBJEXT) boxes.$(OBJEXT) \ 
    127128        chmod.$(OBJEXT) chown.$(OBJEXT) cmd.$(OBJEXT) color.$(OBJEXT) \ 
    128129        command.$(OBJEXT) complete.$(OBJEXT) cons.handler.$(OBJEXT) \ 
     
    141142        treestore.$(OBJEXT) tty.$(OBJEXT) user.$(OBJEXT) \ 
    142143        util.$(OBJEXT) utilunix.$(OBJEXT) view.$(OBJEXT) \ 
    143144        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) 
    147149am__objects_2 = charsets.$(OBJEXT) selcodepage.$(OBJEXT) 
    148150@CHARSET_FALSE@am_mc_OBJECTS = $(am__objects_1) 
    149151@CHARSET_TRUE@am_mc_OBJECTS = $(am__objects_1) $(am__objects_2) 
     
    420422        tree.c tree.h treestore.c treestore.h timefmt.h tty.c tty.h user.c      \ 
    421423        user.h util.c util.h utilunix.c view.c view.h vfsdummy.h widget.c       \ 
    422424        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 \ 
    424426        strutil.h strutil.c strutilascii.c strutil8bit.c strutilutf8.c \ 
    425427        search/search.h strescape.c strescape.h 
    426428 
     
    623625@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/win.Po@am__quote@ 
    624626@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wtools.Po@am__quote@ 
    625627@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@ 
    626630 
    627631.c.o: 
    628632@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  
    5757#include "tty.h"                /* LINES */ 
    5858#include "dialog.h"             /* Widget */ 
    5959#include "view.h"               /* mc_internal_viewer() */ 
     60#include "zdiff.h"              /* view_diff_cmd() */ 
    6061#include "wtools.h"             /* message() */ 
    6162#include "widget.h"             /* push_history() */ 
    6263#include "key.h"                /* application_keypad_mode() */ 
     
    833834                   "listing mode to use this command ")); 
    834835    } 
    835836} 
     837  
     838#ifdef USE_DIFF_VIEW 
     839void 
     840diff_view_cmd (void) 
     841{ 
     842    view_diff_cmd(NULL); 
     843} 
     844#endif 
    836845 
    837846void 
    838847history_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  
    4141void edit_mc_menu_cmd (void); 
    4242void quick_chdir_cmd (void); 
    4343void compare_dirs_cmd (void); 
     44void diff_view_cmd (void); 
    4445void history_cmd (void); 
    4546void tree_cmd (void); 
    4647void 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  
    110110/* error dialog colors start at 39 */ 
    111111    { "errdhotnormal=",  0, 0 }, /* Error dialog normal/hot */ /* 39 */ 
    112112    { "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 */ 
    113120}; 
    114121 
    115122struct color_table_s { 
     
    262269        "editwhitespace=brightblue,blue:" 
    263270        "editlinestate=white,cyan:" 
    264271        "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); 
    266281 
    267282    extern char *command_line_colors; 
    268283 
  • 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  
    9393#define ERROR_HOT_NORMAL   IF_COLOR (39, 0) 
    9494#define ERROR_HOT_FOCUS    IF_COLOR (40, 0) 
    9595 
     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 
    96103#ifdef HAVE_SLANG 
    97104#   define CTYPE const char * 
    98105#else 
  • src/history.h

    diff -Naur mc-4.7.0-pre1~/src/history.h mc-4.7.0-pre1/src/history.h
    old new  
    3838 
    3939#define MC_HISTORY_HOTLIST_ADD          "mc.hotlist.add" 
    4040 
     41#define MC_HISTORY_YDIFF_GOTO_LINE      "mc.ydiff.goto-line" 
     42#define MC_HISTORY_ZDIFF_GOTO_LINE      "mc.zdiff.goto-line" 
     43 
    4144#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  
    917917    {' ', N_("s&Wap panels          C-u"), NULL_HOTKEY, swap_cmd}, 
    918918    {' ', N_("switch &Panels on/off C-o"), NULL_HOTKEY, view_other_cmd}, 
    919919    {' ', 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 
    920923    {' ', N_("e&Xternal panelize    C-x !"), NULL_HOTKEY, external_panelize}, 
    921924    {' ', N_("show directory s&Izes"), NULL_HOTKEY, dirsizes_cmd}, 
    922925    {' ', "", NULL_HOTKEY, 0}, 
     
    12621265static const key_map ctl_x_map[] = { 
    12631266    {XCTRL ('c'), quit_cmd}, 
    12641267    {'d', compare_dirs_cmd}, 
     1268#ifdef USE_DIFF_VIEW 
     1269    {XCTRL ('y'), diff_view_cmd}, 
     1270#endif 
    12651271#ifdef USE_VFS 
    12661272    {'a', reselect_vfs}, 
    12671273#endif                          /* USE_VFS */ 
     
    20092015            "                 errdhotfocus\n" 
    20102016            "   Menus:        menu, menuhot, menusel, menuhotsel\n" 
    20112017            "   Editor:       editnormal, editbold, editmarked, editwhitespace,\n" 
    2012             "                 editlinestate\n"), stdout); 
     2018            "                 editlinestate\n" 
     2019            "   Diff viewer:  dffadd, dffchg, dffchh, dffchd, dffdel\n"), stdout); 
    20132020    fputs (_ 
    20142021           ( 
    20152022            "   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  
    2929#define ACS_LLCORNER SLSMG_LLCORN_CHAR 
    3030#define ACS_URCORNER SLSMG_URCORN_CHAR 
    3131#define ACS_LRCORNER SLSMG_LRCORN_CHAR 
     32#define ACS_TTEE SLSMG_UTEE_CHAR 
     33#define ACS_BTEE SLSMG_DTEE_CHAR 
    3234 
    3335#define acs()   SLsmg_set_char_set(1) 
    3436#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  
    171171} 
    172172 
    173173extern void 
     174tty_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 
     183extern void 
    174184tty_print_one_hline(void) 
    175185{ 
    176186    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  
    6060extern void tty_print_char(int); 
    6161extern void tty_print_alt_char(int); 
    6262extern void tty_print_string(const char *); 
     63extern void tty_print_nstring(const char *s, int n); 
    6364extern void tty_print_one_vline(void); 
    6465extern void tty_print_one_hline(void); 
    6566extern 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 
     42typedef 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 
     53typedef 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 
     67typedef struct { 
     68    int a[2][2]; 
     69    int cmd; 
     70} DIFFCMD; 
     71 
     72typedef 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 
     78typedef struct { 
     79    int off; 
     80    int len; 
     81} BRACKET[2]; 
     82 
     83typedef int PAIR[2]; 
     84 
     85#define TAB_SKIP(ts, pos)       ((ts) - (pos) % (ts)) 
     86 
     87typedef enum { 
     88    DATA_SRC_MEM = 0, 
     89    DATA_SRC_TMP = 1, 
     90    DATA_SRC_ORG = 2 
     91} DSRC; 
     92 
     93typedef 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 
     103typedef struct { 
     104    FBUF *f; 
     105    ARRAY *a; 
     106    DSRC dsrc; 
     107} PRINTER_CTX; 
     108 
     109typedef 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 
     155static const char *quality_str[] = { 
     156    N_("&Normal"), 
     157    N_("&Fastest"), 
     158    N_("&Minimal") 
     159}; 
     160 
     161static 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 
     173static 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 
     182static const char *search_str[] = { 
     183    N_("&Normal") 
     184}; 
     185 
     186static 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 
     198static QuickDialog search_input = { 
     199    SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, 
     200    N_("Search"), "[Input Line Keys]", 
     201    search_widgets, 0 
     202}; 
     203 
     204static 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 */ 
     219static void 
     220arr_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 */ 
     235static void 
     236arr_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 */ 
     252static void * 
     253arr_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 */ 
     280static void 
     281arr_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 */ 
     314static int 
     315open_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 */ 
     365static FBUF * 
     366f_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 */ 
     401static int 
     402f_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 */ 
     422static FBUF * 
     423f_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 */ 
     453static FBUF * 
     454f_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 */ 
     487static size_t 
     488f_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 */ 
     523static int 
     524f_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 */ 
     550static off_t 
     551f_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 */ 
     584static off_t 
     585f_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 */ 
     606static ssize_t 
     607f_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 */ 
     626static off_t 
     627f_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 */ 
     651static int 
     652f_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 */ 
     668static FBUF * 
     669p_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 */ 
     710static int 
     711p_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 */ 
     730static int 
     731scan_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 */ 
     753static int 
     754scan_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 */ 
     833static int 
     834scan_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 */ 
     869static int 
     870dff_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 */ 
     919static int 
     920dff_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 */ 
     1068static int 
     1069lcsubstr (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 */ 
     1163static int 
     1164hdiff_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 */ 
     1228static int 
     1229hdiff_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 */ 
     1270static int 
     1271is_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 */ 
     1299static int 
     1300cvt_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 */ 
     1334static int 
     1335cvt_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 */ 
     1371static int 
     1372cvt_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 */ 
     1439static int 
     1440cvt_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 */ 
     1510static int 
     1511cvt_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 
     1605static void 
     1606cc_free_elt (void *elt) 
     1607{ 
     1608    DIFFLN *p = elt; 
     1609    if (p->p) { 
     1610        free(p->p); 
     1611    } 
     1612} 
     1613 
     1614 
     1615static int 
     1616printer (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 
     1676static int 
     1677redo_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 
     1776static void 
     1777destroy_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 
     1798static int 
     1799get_digits (unsigned int n) 
     1800{ 
     1801    int d = 1; 
     1802    while (n /= 10) { 
     1803        d++; 
     1804    } 
     1805    return d; 
     1806} 
     1807 
     1808 
     1809static int 
     1810get_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 
     1841static int 
     1842calc_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 
     1855static int 
     1856find_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 
     1878static int 
     1879find_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 
     1895static void 
     1896view_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 
     1908static void 
     1909view_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 
     1919static int 
     1920view_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 
     2003static int 
     2004view_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 
     2029static void 
     2030view_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 
     2043static int 
     2044view_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 
     2165static void 
     2166view_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 
     2197static void 
     2198view_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 
     2272static void 
     2273view_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 
     2292static const unsigned char * 
     2293memmem_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 
     2313static const unsigned char * 
     2314memmem_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 
     2334static const unsigned char * 
     2335memmem_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 
     2355static const unsigned char * 
     2356memmem_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 
     2376static const unsigned char * 
     2377search_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 
     2395static int 
     2396view_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 
     2433static void 
     2434view_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 
     2512static void 
     2513view_search_cmd (WDiff *view) 
     2514{ 
     2515    view_search(view, 0); 
     2516} 
     2517 
     2518 
     2519static void 
     2520view_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 
     2536static void 
     2537view_edit_cmd (WDiff *view) 
     2538{ 
     2539    view_edit(view, view->ord); 
     2540} 
     2541 
     2542 
     2543static void 
     2544view_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 
     2574static void 
     2575view_help_cmd (void) 
     2576{ 
     2577    interactive_display(NULL, "[Diff Viewer]"); 
     2578} 
     2579 
     2580 
     2581static void 
     2582view_quit_cmd (WDiff *view) 
     2583{ 
     2584    dlg_stop(view->widget.parent); 
     2585} 
     2586 
     2587 
     2588static void 
     2589view_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 
     2601static int 
     2602view_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 
     2628static cb_ret_t 
     2629view_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 
     2784static cb_ret_t 
     2785view_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 
     2829static void 
     2830view_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 
     2845static cb_ret_t 
     2846view_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 
     2859int 
     2860diff_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 
     4int 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 
     44typedef 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 
     83typedef struct DNODE { 
     84    const struct DNODE *link; 
     85    const struct stat *st[2]; 
     86} DNODE; 
     87 
     88typedef int (*DFUNC) (void *ctx, int ch, const char *f1, const char *f2); 
     89 
     90static 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 
     94typedef struct { 
     95    int ch; 
     96    char *name[2]; 
     97} LNODE; 
     98 
     99typedef 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 
     129static 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 
     136static 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 
     145static const char *search_str[] = { 
     146    N_("&Normal") 
     147}; 
     148 
     149static 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 
     161static QuickDialog search_input = { 
     162    SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, 
     163    N_("Search"), "[Input Line Keys]", 
     164    search_widgets, 0 
     165}; 
     166 
     167static 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 */ 
     182static void 
     183arr_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 */ 
     198static void 
     199arr_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 */ 
     215static void * 
     216arr_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 */ 
     243static void 
     244arr_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 */ 
     271static 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 */ 
     296static char * 
     297bufpath_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 */ 
     314static char * 
     315bufpath_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 */ 
     332static char * 
     333strpath (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 */ 
     360static int 
     361scan_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 */ 
     424static int 
     425compar (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 */ 
     436static void 
     437dispose (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 */ 
     452static int 
     453diff_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 */ 
     512static int 
     513diff_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 */ 
     588static int 
     589diff_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 
     675static void 
     676cvt_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 
     692static void 
     693free_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 
     704static int 
     705printer (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 
     736static int 
     737calc_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 
     750static int 
     751redo_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 */ 
     775static int 
     776scan_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 
     790static int 
     791get_digits (unsigned int n) 
     792{ 
     793    int d = 1; 
     794    while (n /= 10) { 
     795        d++; 
     796    } 
     797    return d; 
     798} 
     799 
     800 
     801static int 
     802get_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 
     828static int 
     829calc_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 
     842static int 
     843find_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 
     859static int 
     860find_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 
     879static void 
     880view_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 
     892static void 
     893view_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 
     903static int 
     904view_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 
     936static int 
     937view_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 
     957static void 
     958view_fini (WDiff *view) 
     959{ 
     960    arr_free(&view->z, free_pair); 
     961} 
     962 
     963 
     964static int 
     965view_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 
     1090static void 
     1091view_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 
     1122static void 
     1123view_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 
     1197static void 
     1198view_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 
     1217static const unsigned char * 
     1218memmem_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 
     1238static const unsigned char * 
     1239memmem_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 
     1259static const unsigned char * 
     1260search_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 
     1277static int 
     1278view_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 
     1319static void 
     1320view_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 
     1393static void 
     1394view_search_cmd (WDiff *view) 
     1395{ 
     1396    view_search(view, 0); 
     1397} 
     1398 
     1399 
     1400static void 
     1401view_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 
     1420static void 
     1421view_edit_cmd (WDiff *view) 
     1422{ 
     1423    view_edit(view, view->ord); 
     1424} 
     1425 
     1426 
     1427static void 
     1428view_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 
     1462static void 
     1463view_help_cmd (void) 
     1464{ 
     1465    interactive_display(NULL, "[Directory Diff Viewer]"); 
     1466} 
     1467 
     1468 
     1469static void 
     1470view_quit_cmd (WDiff *view) 
     1471{ 
     1472    dlg_stop(view->widget.parent); 
     1473} 
     1474 
     1475 
     1476static void 
     1477view_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 
     1489static int 
     1490view_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 
     1516static cb_ret_t 
     1517view_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 
     1663static cb_ret_t 
     1664view_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 
     1708static void 
     1709view_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 
     1724static cb_ret_t 
     1725view_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 
     1738int 
     1739zdiff_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) 
     1809void 
     1810view_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 
     4void view_diff_cmd (void *obj); 
     5 
     6#endif