Date: Fri, 11 Dec 2009 13:06:49 GMT From: Andrei Lavreniyuk <andy.lavr@reactor-xg.kiev.ua> To: freebsd-gnats-submit@FreeBSD.org Subject: ports/141368: [UPDATE] misc/mc: update to 4.6.2_1 Message-ID: <200912111306.nBBD6nlT039583@www.freebsd.org> Resent-Message-ID: <200912111310.nBBDA5Sk098499@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 141368 >Category: ports >Synopsis: [UPDATE] misc/mc: update to 4.6.2_1 >Confidential: no >Severity: non-critical >Priority: medium >Responsible: freebsd-ports-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Fri Dec 11 13:10:05 UTC 2009 >Closed-Date: >Last-Modified: >Originator: Andrei Lavreniyuk >Release: FreeBSD 8.0-STABLE >Organization: Technica-03, Inc. >Environment: FreeBSD datacenter.technica-03.local 8.0-STABLE FreeBSD 8.0-STABLE #0: Sat Dec 5 23:38:56 EET 2009 root@datacenter.technica-03.local:/usr/obj/usr/src/sys/SMP64 amd64 >Description: Adds new features: 1 - Diff Viewer (Ctrl-x-y) 2 - Show hidden files (Ctrl-.) *- adds new patch >How-To-Repeat: >Fix: Patch attached with submission follows: diff -ruN mc.bak/Makefile mc/Makefile --- mc.bak/Makefile 2009-12-10 21:13:00.000000000 +0200 +++ mc/Makefile 2009-12-11 14:59:16.765071101 +0200 @@ -7,6 +7,7 @@ PORTNAME= mc PORTVERSION= 4.6.2 +PORTREVISION= 1 CATEGORIES= misc shells MASTER_SITES= http://www.midnight-commander.org/downloads/ \ ${MASTER_SITE_SUNSITE} @@ -118,6 +119,11 @@ -e 's|/usr/bin/unzip|${LOCALBASE}/bin/unzip|' \ ${WRKSRC}/configure +pre-configure: + @${RM} ${WRKSRC}/configure + @${CHMOD} 755 ${WRKSRC}/autogen.sh + @cd ${WRKSRC} && ${SH} autogen.sh + post-install: @${LN} -sf mc ${PREFIX}/bin/midc diff -ruN mc.bak/files/patch-Makefile.am mc/files/patch-Makefile.am --- mc.bak/files/patch-Makefile.am 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-Makefile.am 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,11 @@ +--- src/Makefile.am.orig 2009-12-10 21:32:50.000000000 +0200 ++++ src/Makefile.am 2009-12-10 21:34:57.436062336 +0200 +@@ -64,7 +64,7 @@ + tree.c tree.h treestore.c treestore.h timefmt.h tty.c tty.h user.c \ + user.h util.c util.h utilunix.c view.c view.h vfsdummy.h widget.c \ + widget.h win.c win.h wtools.c wtools.h unixcompat.h \ +- x11conn.h x11conn.c ecs.h ecs.c ++ x11conn.h x11conn.c ecs.h ecs.c ydiff.c ydiff.h + + if CHARSET + mc_SOURCES = $(SRCS) $(CHARSET_SRC) diff -ruN mc.bak/files/patch-cmd.c mc/files/patch-cmd.c --- mc.bak/files/patch-cmd.c 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-cmd.c 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,54 @@ +--- src/cmd.c 2005-05-27 17:19:18.000000000 +0300 ++++ src/cmd.c 2007-05-05 13:11:08.000000000 +0300 +@@ -48,6 +48,7 @@ + #include "tty.h" /* LINES */ + #include "dialog.h" /* Widget */ + #include "view.h" /* mc_internal_viewer() */ ++#include "ydiff.h" /* diff_view() */ + #include "wtools.h" /* message() */ + #include "widget.h" /* push_history() */ + #include "key.h" /* application_keypad_mode() */ +@@ -840,6 +841,43 @@ + "listing mode to use this command ")); + } + } ++ ++#ifdef USE_DIFF_VIEW ++void ++diff_view_cmd (void) ++{ ++ int rv = -1; ++ char *file1; ++ char *file2; ++ ++ if (!S_ISREG(selection(current_panel)->st.st_mode) || ++ !S_ISREG(selection(other_panel)->st.st_mode)) { ++ message (1, MSG_ERROR, ++ _(" Both files must be regular files ")); ++ return; ++ } ++ ++ file1 = mhl_str_dir_plus_file(current_panel->cwd, selection(current_panel)->fname); ++ file2 = mhl_str_dir_plus_file(other_panel->cwd, selection(other_panel)->fname); ++ ++ if (file1 != NULL && file2 != NULL) { ++ /*if (get_current_index()) { ++ char *tmp = file1; ++ file1 = file2; ++ file2 = tmp; ++ }*/ ++ rv = diff_view(file1, file2); ++ } ++ ++ g_free(file2); ++ g_free(file1); ++ ++ if (rv != 0) { ++ message (1, MSG_ERROR, ++ _(" Error building diff ")); ++ } ++} ++#endif + + void + history_cmd (void) diff -ruN mc.bak/files/patch-cmd.h mc/files/patch-cmd.h --- mc.bak/files/patch-cmd.h 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-cmd.h 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,10 @@ +--- src/cmd.h 2004-08-29 19:43:04.000000000 +0300 ++++ src/cmd.h 2007-05-05 13:11:08.000000000 +0300 +@@ -35,6 +35,7 @@ + void edit_syntax_cmd (void); + void quick_chdir_cmd (void); + void compare_dirs_cmd (void); ++void diff_view_cmd (void); + void history_cmd (void); + void tree_cmd (void); + void link_cmd (void); diff -ruN mc.bak/files/patch-color.c mc/files/patch-color.c --- mc.bak/files/patch-color.c 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-color.c 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,30 @@ +--- src/color.c 2005-05-27 17:19:18.000000000 +0300 ++++ src/color.c 2007-05-05 13:12:22.000000000 +0300 +@@ -100,6 +100,13 @@ + /* error dialog colors start at 37 */ + { "errdhotnormal=", 0, 0 }, /* Error dialog normal/hot */ /* 37 */ + { "errdhotfocus=", 0, 0 }, /* Error dialog focused/hot */ ++ ++/* diff viewer colors start at 39 */ ++ { "dffadd=", 0, 0 }, /* added line */ ++ { "dffchg=", 0, 0 }, /* changed line */ ++ { "dffchh=", 0, 0 }, /* changed line (highlight) */ ++ { "dffchd=", 0, 0 }, /* changed line (deleted) */ ++ { "dffdel=", 0, 0 }, /* deleted line */ + }; + + struct color_table_s { +@@ -162,7 +169,12 @@ + "editbold=yellow,blue:" + "editmarked=black,cyan:" + "errdhotnormal=yellow,red:" +-"errdhotfocus=yellow,lightgray"; ++"errdhotfocus=yellow,lightgray:" ++"dffadd=black,green:" ++"dffchg=black,brown:" ++"dffchh=black,magenta:" ++"dffchd=lightgray,black:" ++"dffdel=lightgray,red"; + + #ifdef HAVE_SLANG + # define color_value(i) color_table [i].name diff -ruN mc.bak/files/patch-color.h mc/files/patch-color.h --- mc.bak/files/patch-color.h 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-color.h 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,16 @@ +--- src/color.h 2004-09-02 01:33:43.000000000 +0300 ++++ src/color.h 2007-05-05 13:12:52.000000000 +0300 +@@ -83,6 +83,13 @@ + #define ERROR_HOT_NORMAL IF_COLOR (37, 0) + #define ERROR_HOT_FOCUS IF_COLOR (38, 0) + ++/* Diff colors */ ++#define DFFADD_COLOR IF_COLOR(39, A_BOLD) ++#define DFFCHG_COLOR IF_COLOR(40, A_UNDERLINE) ++#define DFFCHH_COLOR IF_COLOR(41, A_UNDERLINE) ++#define DFFCHD_COLOR IF_COLOR(42, A_REVERSE) ++#define DFFDEL_COLOR IF_COLOR(43, A_REVERSE) ++ + #ifdef HAVE_SLANG + # define CTYPE const char * + #else diff -ruN mc.bak/files/patch-configure.ac mc/files/patch-configure.ac --- mc.bak/files/patch-configure.ac 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-configure.ac 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,40 @@ +--- configure.ac 2005-07-23 19:52:49.000000000 +0300 ++++ configure.ac 2007-05-05 13:11:08.000000000 +0300 +@@ -514,6 +514,22 @@ + fi + + ++dnl ++dnl Diff viewer support. ++dnl ++AC_ARG_WITH(diff, ++ [ --with-diff Enable diff viewer [[yes]]]) ++ ++if test x$with_diff != xno; then ++ AC_DEFINE(USE_DIFF_VIEW, 1, [Define to enable diff viewer]) ++ use_diff=yes ++ diff_msg="yes" ++ AC_MSG_NOTICE([using diff viewer]) ++else ++ diff_msg="no" ++fi ++ ++ + dnl Check if the OS is supported by the console saver. + cons_saver="" + case $host_os in +@@ -583,6 +599,7 @@ + fi + + AM_CONDITIONAL(USE_EDIT, [test -n "$use_edit"]) ++AM_CONDITIONAL(USE_DIFF, [test -n "$use_diff"]) + AM_CONDITIONAL(USE_VFS, [test "x$use_vfs" = xyes]) + AM_CONDITIONAL(USE_VFS_NET, [test x"$use_net_code" = xtrue]) + AM_CONDITIONAL(USE_UNDEL_FS, [test -n "$use_undelfs"]) +@@ -656,5 +673,6 @@ + X11 events support: ${textmode_x11_support} + With subshell support: ${subshell} + Internal editor: ${edit_msg} ++ Diff viewer: ${diff_msg} + Support for charset: ${charset_msg} + " diff -ruN mc.bak/files/patch-main.c mc/files/patch-main.c --- mc.bak/files/patch-main.c 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-main.c 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,30 @@ +--- src/main.c 2005-07-23 19:52:02.000000000 +0300 ++++ src/main.c 2007-05-05 13:13:29.000000000 +0300 +@@ -876,6 +876,9 @@ + {' ', N_("s&Wap panels C-u"), 'W', swap_cmd}, + {' ', N_("switch &Panels on/off C-o"), 'P', view_other_cmd}, + {' ', N_("&Compare directories C-x d"), 'C', compare_dirs_cmd}, ++#ifdef USE_DIFF_VIEW ++ {' ', N_("&View diff files C-x C-y"), 'V', diff_view_cmd}, ++#endif + {' ', N_("e&Xternal panelize C-x !"), 'X', external_panelize}, + {' ', N_("show directory s&Izes"), 'I', dirsizes_cmd}, + {' ', "", ' ', 0}, +@@ -1216,6 +1219,9 @@ + static const key_map ctl_x_map[] = { + {XCTRL ('c'), quit_cmd}, + {'d', compare_dirs_cmd}, ++#ifdef USE_DIFF_VIEW ++ {XCTRL ('y'), diff_view_cmd}, ++#endif + #ifdef USE_VFS + {'a', reselect_vfs}, + #endif /* USE_VFS */ +@@ -1882,6 +1888,7 @@ + " errdhotfocus\n" + " Menus: menu, menuhot, menusel, menuhotsel\n" + " Editor: editnormal, editbold, editmarked\n" ++ " Diff viewer: dffadd, dffchg, dffchh, dffchd, dffdel\n" + " Help: helpnormal, helpitalic, helpbold, helplink, helpslink\n" + " File types: directory, executable, link, stalelink, device, special, core\n" + "\n" "Colors:\n" diff -ruN mc.bak/files/patch-rh-hint-crash-fix mc/files/patch-rh-hint-crash-fix --- mc.bak/files/patch-rh-hint-crash-fix 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-rh-hint-crash-fix 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,14 @@ +--- src/util.c.hintchk 2008-03-27 12:39:54.000000000 +0100 ++++ src/util.c 2008-03-27 12:46:39.000000000 +0100 +@@ -995,6 +995,11 @@ load_mc_home_file (const char *filename, + if (hintfile != hintfile_base) + g_free (hintfile_base); + ++ if (!data) { ++ g_free(hintfile); ++ return NULL; ++ } ++ + if (allocated_filename) + *allocated_filename = hintfile; + else diff -ruN mc.bak/files/patch-togle_hidden_files mc/files/patch-togle_hidden_files --- mc.bak/files/patch-togle_hidden_files 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-togle_hidden_files 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,52 @@ +--- src/main.c.orig 2006-09-22 18:14:58.000000000 +0300 ++++ src/main.c 2008-09-28 22:48:43.000000000 +0300 +@@ -1018,6 +1018,13 @@ + { + show_dot_files = !show_dot_files; + update_panels (UP_RELOAD, UP_KEEPSEL); ++ do_refresh(); ++} ++ ++void toggle_horiz_vert_layout(void) { ++ horizontal_split = !horizontal_split; ++ layout_change(); ++ do_refresh(); + } + + /* +@@ -1292,6 +1299,12 @@ + /* Swap panels */ + {XCTRL ('u'), swap_cmd}, + ++ /* Toggle Hidden Files */ ++ {ALT ('.'), toggle_show_hidden}, ++ ++ /* Toggle Horizontal/Vertical layout */ ++ {ALT (','), toggle_horiz_vert_layout}, ++ + /* View output */ + {XCTRL ('o'), view_other_cmd}, + +--- src/layout.c.orig 2006-11-08 15:37:25.000000000 +0200 ++++ src/layout.c 2008-09-28 11:47:19.000000000 +0300 +@@ -488,9 +488,7 @@ + radio_widget->sel = horizontal_split; + } + +-static void +-layout_change (void) +-{ ++void layout_change(void) { + setup_panels (); + /* re-init the menu, because perhaps there was a change in the way + how the panel are split (horizontal/vertical). */ +--- src/layout.h.orig 2004-12-03 21:17:47.000000000 +0200 ++++ src/layout.h 2008-09-28 11:47:13.000000000 +0300 +@@ -8,6 +8,7 @@ + void init_curses (void); + void done_screen (void); + void setup_panels (void); ++void layout_change(void); + void destroy_panels (void); + void move_resize_panel (void); + void flag_winch (int dummy); diff -ruN mc.bak/files/patch-ydiff.c mc/files/patch-ydiff.c --- mc.bak/files/patch-ydiff.c 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-ydiff.c 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,2679 @@ +--- src/ydiff.c.orig 2009-12-10 23:44:56.944204631 +0200 ++++ src/ydiff.c 2009-12-10 23:46:06.709619876 +0200 +@@ -0,0 +1,2676 @@ ++/* ++ * Copyright (c) 2007 Daniel Borca All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++ ++#include <config.h> ++#include <ctype.h> ++#include <errno.h> ++#include <sys/stat.h> ++#include "global.h" ++#include "tty.h" ++#include "cmd.h" ++#include "dialog.h" ++#include "widget.h" ++#include "color.h" ++#include "help.h" ++#include "key.h" ++#include "wtools.h" ++#include "charsets.h" ++#include "ydiff.h" ++#include "tty.h" ++ ++ ++#ifdef USE_DIFF_VIEW ++ ++#include "main.h" ++#define tty_setcolor attrset ++#define tty_gotoyx move ++#define tty_print_char addch ++#define tty_print_string addstr ++#define tty_printf printw ++#define tty_print_alt_char(c) do { acs(); addch(c); noacs(); } while (0) ++static void ++tty_print_nstring (const char *s, int n) ++{ ++ int ch; ++ while (n-- > 0 && (ch = *s++)) { ++ addch(ch); ++ } ++} ++ ++#define FILE_READ_BUF 4096 ++#define FILE_FLAG_TEMP (1 << 0) ++ ++typedef struct { ++ int fd; ++ int pos; ++ int len; ++ char *buf; ++ int flags; ++ void *data; ++} FBUF; ++ ++#define ADD_CH '+' ++#define DEL_CH '-' ++#define CHG_CH '*' ++#define EQU_CH ' ' ++ ++typedef struct { ++ int a[2][2]; ++ int cmd; ++} DIFFCMD; ++ ++typedef struct { ++ int len, max; ++ DIFFCMD *data; ++} DIFFOPS; ++ ++typedef int (*DFUNC) (void *ctx, int ch, int line, off_t off, size_t sz, const char *str); ++ ++#define HDIFF_ENABLE 1 ++#define HDIFF_MINCTX 5 ++#define HDIFF_DEPTH 10 ++ ++typedef struct { ++ int off; ++ int len; ++} BRACKET[2]; ++ ++typedef struct { ++ int len, max; ++ BRACKET *data; ++ int error; ++} HDIFF; ++ ++typedef int PAIR[2]; ++ ++typedef struct { ++ int len, max; ++ PAIR *data; ++} LCSET; ++ ++#define TAB_SKIP(ts, pos) ((ts) - (pos) % (ts)) ++ ++typedef enum { ++ DATA_SRC_MEM = 0, ++ DATA_SRC_TMP = 1, ++ DATA_SRC_ORG = 2 ++} DSRC; ++ ++typedef struct { ++ int ch; ++ int line; ++ union { ++ off_t off; ++ size_t len; ++ } u; ++ void *p; ++} DIFFLN; ++ ++typedef struct { ++ int len, max; ++ DIFFLN *data; ++ int error; ++} DIFFCC; ++ ++typedef struct { ++ FBUF *f; ++ DIFFCC *a; ++ DSRC dsrc; ++} PRINTER_CTX; ++ ++typedef struct { ++ int quality; ++ int strip_trailing_cr; ++ int ignore_tab_expansion; ++ int ignore_space_change; ++ int ignore_all_space; ++ int ignore_case; ++} DIFFOPT; ++ ++typedef struct { ++ Widget widget; ++ ++ const char *args; /* Args passed to diff */ ++ const char *file[2]; /* filenames */ ++ FBUF *f[2]; ++ DIFFCC a[2]; ++ HDIFF **hdiff; ++ int ndiff; /* number of hunks */ ++ DSRC dsrc; /* data source: memory or temporary file */ ++ ++ int view_quit:1; /* Quit flag */ ++ ++ int height; ++ int width1; ++ int width2; ++ int new_frame; ++ int skip_rows; ++ int skip_cols; ++ int display_symbols; ++ int display_numbers; ++ int show_cr; ++ int show_hdiff; ++ int tab_size; ++ int ord; ++ ++ DIFFOPT opt; ++} WDiff; ++ ++ ++#define OPTX 50 ++#define OPTY 12 ++ ++static const char *quality_str[] = { ++ N_("&Normal"), ++ N_("&Fastest"), ++ N_("&Highest") ++}; ++ ++static QuickWidget diffopt_widgets[] = { ++ { quick_button, 6, 10, 9, OPTY, N_("&Cancel"), 0, B_CANCEL, NULL, NULL, NULL }, ++ { quick_button, 3, 10, 9, OPTY, N_("&OK"), 0, B_ENTER, NULL, NULL, NULL }, ++ { quick_radio, 34, OPTX, 4, OPTY, "", 3, 2, NULL, const_cast(char **, quality_str), NULL }, ++ { quick_checkbox, 4, OPTX, 7, OPTY, N_("strip trailing &CR"), 0, 0, NULL, NULL, NULL }, ++ { quick_checkbox, 4, OPTX, 6, OPTY, N_("ignore all &Whitespace"), 0, 0, NULL, NULL, NULL }, ++ { quick_checkbox, 4, OPTX, 5, OPTY, N_("ignore &Space change"), 0, 0, NULL, NULL, NULL }, ++ { quick_checkbox, 4, OPTX, 4, OPTY, N_("ignore tab &Expansion"), 0, 0, NULL, NULL, NULL }, ++ { quick_checkbox, 4, OPTX, 3, OPTY, N_("&Ignore case"), 0, 0, NULL, NULL, NULL }, ++ NULL_QuickWidget ++}; ++ ++static QuickDialog diffopt = { ++ OPTX, OPTY, -1, -1, ++ N_(" Diff Options "), "[Diff Options]", ++ diffopt_widgets, 0 ++}; ++ ++ ++/* buffered I/O **************************************************************/ ++ ++ ++#define FILE_DIRTY(fs) \ ++ do { \ ++ (fs)->pos = 0; \ ++ (fs)->len = 0; \ ++ } while (0) ++ ++ ++/** ++ * Try to open a temporary file. ++ * ++ * \param[out] name address of a pointer to store the temporary name ++ * ++ * \return file descriptor on success, negative on error ++ * ++ * \note the name is not altered if this function fails ++ * \note tries mc_tmpdir() and then current directory ++ */ ++static int ++open_temp (void **name) ++{ ++ int fd; ++ int len; ++ char *temp; ++ const char *pattern = "mcdiffXXXXXX"; ++ const char *env = mc_tmpdir(); ++ ++ if (env == NULL) { ++ env = ""; ++ } ++ ++ len = strlen(env); ++ temp = malloc(len + 1 + strlen(pattern) + 1); ++ if (temp == NULL) { ++ return -1; ++ } ++ ++ if (len) { ++ strcpy(temp, env); ++ if (temp[len - 1] != PATH_SEP) { ++ temp[len++] = PATH_SEP; ++ } ++ } ++ strcpy(temp + len, pattern); ++ ++ fd = mkstemp(temp); ++ if (fd < 0) { ++ if (len) { ++ strcpy(temp, pattern); ++ fd = mkstemp(temp); ++ } ++ if (fd < 0) { ++ free(temp); ++ return -1; ++ } ++ } ++ ++ *name = temp; ++ return fd; ++} ++ ++ ++/** ++ * Alocate file structure and associate file descriptor to it. ++ * ++ * \param fd file descriptor ++ * ++ * \return file structure ++ */ ++static FBUF * ++f_dopen (int fd) ++{ ++ FBUF *fs; ++ ++ if (fd < 0) { ++ return NULL; ++ } ++ ++ fs = malloc(sizeof(FBUF)); ++ if (fs == NULL) { ++ return NULL; ++ } ++ ++ fs->buf = malloc(FILE_READ_BUF); ++ if (fs->buf == NULL) { ++ free(fs); ++ return NULL; ++ } ++ ++ fs->fd = fd; ++ FILE_DIRTY(fs); ++ fs->flags = 0; ++ fs->data = NULL; ++ ++ return fs; ++} ++ ++ ++/** ++ * Free file structure without closing the file. ++ * ++ * \param fs file structure ++ * ++ * \return 0 on success, non-zero on error ++ */ ++static int ++f_free (FBUF *fs) ++{ ++ int rv = 0; ++ if (fs->flags & FILE_FLAG_TEMP) { ++ rv = unlink(fs->data); ++ free(fs->data); ++ } ++ free(fs->buf); ++ free(fs); ++ return rv; ++} ++ ++ ++/** ++ * Open a binary temporary file in R/W mode. ++ * ++ * \return file structure ++ * ++ * \note the file will be deleted when closed ++ */ ++static FBUF * ++f_temp (void) ++{ ++ int fd; ++ FBUF *fs; ++ ++ fs = f_dopen(0); ++ if (fs == NULL) { ++ return NULL; ++ } ++ ++ fd = open_temp(&fs->data); ++ if (fd < 0) { ++ f_free(fs); ++ return NULL; ++ } ++ ++ fs->fd = fd; ++ fs->flags = FILE_FLAG_TEMP; ++ return fs; ++} ++ ++ ++/** ++ * Open a binary file in specified mode. ++ * ++ * \param filename file name ++ * \param flags open mode, a combination of O_RDONLY, O_WRONLY, O_RDWR ++ * ++ * \return file structure ++ */ ++static FBUF * ++f_open (const char *filename, int flags) ++{ ++ int fd; ++ FBUF *fs; ++ ++ fs = f_dopen(0); ++ if (fs == NULL) { ++ return NULL; ++ } ++ ++ fd = open(filename, flags); ++ if (fd < 0) { ++ f_free(fs); ++ return NULL; ++ } ++ ++ fs->fd = fd; ++ return fs; ++} ++ ++ ++/** ++ * Read a line of bytes from file until newline or EOF. ++ * ++ * \param buf destination buffer ++ * \param size size of buffer ++ * \param fs file structure ++ * ++ * \return number of bytes read ++ * ++ * \note does not stop on null-byte ++ * \note buf will not be null-terminated ++ */ ++static size_t ++f_gets (char *buf, size_t size, FBUF *fs) ++{ ++ size_t j = 0; ++ ++ do { ++ int i; ++ int stop = 0; ++ ++ for (i = fs->pos; j < size && i < fs->len && !stop; i++, j++) { ++ buf[j] = fs->buf[i]; ++ if (buf[j] == '\n') { ++ stop = 1; ++ } ++ } ++ fs->pos = i; ++ ++ if (j == size || stop) { ++ break; ++ } ++ ++ fs->pos = 0; ++ fs->len = read(fs->fd, fs->buf, FILE_READ_BUF); ++ } while (fs->len > 0); ++ ++ return j; ++} ++ ++ ++/** ++ * Read one character from file. ++ * ++ * \param fs file structure ++ * ++ * \return character ++ */ ++static int ++f_getc (FBUF *fs) ++{ ++ do { ++ if (fs->pos < fs->len) { ++ return (unsigned char)fs->buf[fs->pos++]; ++ } ++ ++ fs->pos = 0; ++ fs->len = read(fs->fd, fs->buf, FILE_READ_BUF); ++ } while (fs->len > 0); ++ ++ return -1; ++} ++ ++ ++/** ++ * Seek into file. ++ * ++ * \param fs file structure ++ * \param off offset ++ * \param whence seek directive: SEEK_SET, SEEK_CUR or SEEK_END ++ * ++ * \return position in file, starting from begginning ++ * ++ * \note avoids thrashing read cache when possible ++ */ ++static off_t ++f_seek (FBUF *fs, off_t off, int whence) ++{ ++ off_t rv; ++ ++ if (fs->len && whence != SEEK_END) { ++ rv = lseek(fs->fd, 0, SEEK_CUR); ++ if (rv != -1) { ++ if (whence == SEEK_CUR) { ++ whence = SEEK_SET; ++ off += rv - fs->len + fs->pos; ++ } ++ if (off - rv >= -fs->len && off - rv <= 0) { ++ fs->pos = fs->len + off - rv; ++ return off; ++ } ++ } ++ } ++ ++ rv = lseek(fs->fd, off, whence); ++ if (rv != -1) { ++ FILE_DIRTY(fs); ++ } ++ return rv; ++} ++ ++ ++/** ++ * Seek to the beginning of file, thrashing read cache. ++ * ++ * \param fs file structure ++ * ++ * \return 0 if success, non-zero on error ++ */ ++static off_t ++f_reset (FBUF *fs) ++{ ++ off_t rv = lseek(fs->fd, 0, SEEK_SET); ++ if (rv != -1) { ++ FILE_DIRTY(fs); ++ } ++ return rv; ++} ++ ++ ++/** ++ * Write bytes to file. ++ * ++ * \param fs file structure ++ * \param buf source buffer ++ * \param size size of buffer ++ * ++ * \return number of written bytes, -1 on error ++ * ++ * \note thrashes read cache ++ */ ++static ssize_t ++f_write (FBUF *fs, const char *buf, size_t size) ++{ ++ ssize_t rv = write(fs->fd, buf, size); ++ if (rv >= 0) { ++ FILE_DIRTY(fs); ++ } ++ return rv; ++} ++ ++ ++/** ++ * Truncate file to the current position. ++ * ++ * \param fs file structure ++ * ++ * \return current file size on success, negative on error ++ * ++ * \note thrashes read cache ++ */ ++static off_t ++f_trunc (FBUF *fs) ++{ ++ off_t off = lseek(fs->fd, 0, SEEK_CUR); ++ if (off != -1) { ++ int rv = ftruncate(fs->fd, off); ++ if (rv != 0) { ++ off = -1; ++ } else { ++ FILE_DIRTY(fs); ++ } ++ } ++ return off; ++} ++ ++ ++/** ++ * Close file. ++ * ++ * \param fs file structure ++ * ++ * \return 0 on success, non-zero on error ++ * ++ * \note if this is temporary file, it is deleted ++ */ ++static int ++f_close (FBUF *fs) ++{ ++ int rv = close(fs->fd); ++ f_free(fs); ++ return rv; ++} ++ ++ ++/** ++ * Create pipe stream to process. ++ * ++ * \param cmd shell command line ++ * \param flags open mode, either O_RDONLY or O_WRONLY ++ * ++ * \return file structure ++ */ ++static FBUF * ++p_open (const char *cmd, int flags) ++{ ++ FILE *f; ++ FBUF *fs; ++ const char *type = NULL; ++ ++ if (flags == O_RDONLY) { ++ type = "r"; ++ } ++ if (flags == O_WRONLY) { ++ type = "w"; ++ } ++ ++ if (type == NULL) { ++ return NULL; ++ } ++ ++ fs = f_dopen(0); ++ if (fs == NULL) { ++ return NULL; ++ } ++ ++ f = popen(cmd, type); ++ if (f == NULL) { ++ f_free(fs); ++ return NULL; ++ } ++ ++ fs->fd = fileno(f); ++ fs->data = f; ++ return fs; ++} ++ ++ ++/** ++ * Close pipe stream. ++ * ++ * \param fs structure ++ * ++ * \return 0 on success, non-zero on error ++ */ ++static int ++p_close (FBUF *fs) ++{ ++ int rv = pclose(fs->data); ++ f_free(fs); ++ return rv; ++} ++ ++ ++/* diff parse ****************************************************************/ ++ ++ ++/** ++ * Initialize list of diff statements. ++ * ++ * \param ops list, must be non-NULL ++ */ ++static void ++dff_init (DIFFOPS *ops) ++{ ++ ops->len = ops->max = 0; ++ ops->data = NULL; ++} ++ ++ ++/** ++ * Enlarge list of diff statements. ++ * ++ * \param ops list, must be non-NULL ++ * ++ * \return new element, or NULL if error ++ */ ++static DIFFCMD * ++dff_enlarge (DIFFOPS *ops) ++{ ++ DIFFCMD *p; ++ if (ops->len == ops->max) { ++ int max = ops->max + 64; ++ p = realloc(ops->data, max * sizeof(ops->data[0])); ++ if (p == NULL) { ++ return NULL; ++ } ++ ops->max = max; ++ ops->data = p; ++ } ++ p = ops->data + ops->len++; ++ return p; ++} ++ ++ ++/** ++ * Free list of diff statements. ++ * ++ * \param ops list, must be non-NULL ++ */ ++static void ++dff_free (DIFFOPS *ops) ++{ ++ free(ops->data); ++ dff_init(ops); ++} ++ ++ ++/** ++ * Read decimal number from string. ++ * ++ * \param[in,out] str string to parse ++ * \param[out] n extracted number ++ * ++ * \return 0 if success, otherwise non-zero ++ */ ++static int ++scan_deci (const char **str, int *n) ++{ ++ const char *p = *str; ++ char *q; ++ errno = 0; ++ *n = strtol(p, &q, 10); ++ if (errno || p == q) { ++ return -1; ++ } ++ *str = q; ++ return 0; ++} ++ ++ ++/** ++ * Parse line for diff statement. ++ * ++ * \param p string to parse ++ * \param ops list of diff statements ++ * ++ * \return 0 if success, otherwise non-zero ++ */ ++static int ++scan_line (const char *p, DIFFOPS *ops) ++{ ++ DIFFCMD *op; ++ ++ int f1, f2; ++ int t1, t2; ++ int cmd; ++ ++ int range; ++ ++ /* handle the following cases: ++ * NUMaNUM[,NUM] ++ * NUM[,NUM]cNUM[,NUM] ++ * NUM[,NUM]dNUM ++ * where NUM is a positive integer ++ */ ++ ++ if (scan_deci(&p, &f1) != 0 || f1 < 0) { ++ return -1; ++ } ++ f2 = f1; ++ range = 0; ++ if (*p == ',') { ++ p++; ++ if (scan_deci(&p, &f2) != 0 || f2 < f1) { ++ return -1; ++ } ++ range = 1; ++ } ++ ++ cmd = *p++; ++ if (cmd == 'a') { ++ if (range) { ++ return -1; ++ } ++ } else if (cmd != 'c' && cmd != 'd') { ++ return -1; ++ } ++ ++ if (scan_deci(&p, &t1) != 0 || t1 < 0) { ++ return -1; ++ } ++ t2 = t1; ++ range = 0; ++ if (*p == ',') { ++ p++; ++ if (scan_deci(&p, &t2) != 0 || t2 < t1) { ++ return -1; ++ } ++ range = 1; ++ } ++ ++ if (cmd == 'd') { ++ if (range) { ++ return -1; ++ } ++ } ++ ++ op = dff_enlarge(ops); ++ if (op == NULL) { ++ return -1; ++ } ++ op->a[0][0] = f1; ++ op->a[0][1] = f2; ++ op->cmd = cmd; ++ op->a[1][0] = t1; ++ op->a[1][1] = t2; ++ return 0; ++} ++ ++ ++/** ++ * Parse diff output and extract diff statements. ++ * ++ * \param f stream to read from ++ * \param ops list of diff statements to fill ++ * ++ * \return positive number indicating number of hunks, otherwise negative ++ */ ++static int ++scan_diff (FBUF *f, DIFFOPS *ops) ++{ ++ int sz; ++ char buf[BUFSIZ]; ++ ++ while ((sz = f_gets(buf, sizeof(buf) - 1, f))) { ++ if (isdigit(buf[0])) { ++ if (buf[sz - 1] != '\n') { ++ return -1; ++ } ++ buf[sz] = '\0'; ++ if (scan_line(buf, ops) != 0) { ++ return -1; ++ } ++ continue; ++ } ++ while (buf[sz - 1] != '\n' && (sz = f_gets(buf, sizeof(buf), f))) { ++ } ++ } ++ ++ return ops->len; ++} ++ ++ ++/** ++ * Invoke diff and extract diff statements. ++ * ++ * \param args extra arguments to be passed to diff ++ * \param extra more arguments to be passed to diff ++ * \param file1 first file to compare ++ * \param file2 second file to compare ++ * \param ops list of diff statements to fill ++ * ++ * \return positive number indicating number of hunks, otherwise negative ++ */ ++static int ++dff_execute (const char *args, const char *extra, const char *file1, const char *file2, DIFFOPS *ops) ++{ ++ static const char *opt = ++ " --old-group-format='%df%(f=l?:,%dl)d%dE\n'" ++ " --new-group-format='%dea%dF%(F=L?:,%dL)\n'" ++ " --changed-group-format='%df%(f=l?:,%dl)c%dF%(F=L?:,%dL)\n'" ++ " --unchanged-group-format=''"; ++ ++ int rv; ++ FBUF *f; ++ char *cmd; ++ int code; ++ ++ cmd = malloc(14 + strlen(args) + strlen(extra) + strlen(opt) + strlen(file1) + strlen(file2)); ++ if (cmd == NULL) { ++ return -1; ++ } ++ sprintf(cmd, "diff %s %s %s \"%s\" \"%s\"", args, extra, opt, file1, file2); ++ ++ f = p_open(cmd, O_RDONLY); ++ free(cmd); ++ if (f == NULL) { ++ return -1; ++ } ++ ++ dff_init(ops); ++ rv = scan_diff(f, ops); ++ code = p_close(f); ++ ++ if (rv < 0 || code == -1 || !WIFEXITED(code) || WEXITSTATUS(code) == 2) { ++ dff_free(ops); ++ return -1; ++ } ++ ++ return rv; ++} ++ ++ ++/** ++ * Reparse and display file according to diff statements. ++ * ++ * \param ord 0 if displaying first file, 1 if displaying 2nd file ++ * \param filename file name to display ++ * \param ops list of diff statements ++ * \param printer printf-like function to be used for displaying ++ * \param ctx printer context ++ * ++ * \return 0 if success, otherwise non-zero ++ */ ++static int ++dff_reparse (int ord, const char *filename, const DIFFOPS *ops, DFUNC printer, void *ctx) ++{ ++ int i; ++ FBUF *f; ++ size_t sz; ++ char buf[BUFSIZ]; ++ int line = 0; ++ off_t off = 0; ++ const DIFFCMD *op; ++ int eff, tee; ++ int add_cmd; ++ int del_cmd; ++ ++ f = f_open(filename, O_RDONLY); ++ if (f == NULL) { ++ return -1; ++ } ++ ++ ord &= 1; ++ eff = ord; ++ tee = ord ^ 1; ++ ++ add_cmd = 'a'; ++ del_cmd = 'd'; ++ if (ord) { ++ add_cmd = 'd'; ++ del_cmd = 'a'; ++ } ++ ++#define F1 a[eff][0] ++#define F2 a[eff][1] ++#define T1 a[tee][0] ++#define T2 a[tee][1] ++ for (op = ops->data, i = 0; i < ops->len; i++, op++) { ++ int n = op->F1 - (op->cmd != add_cmd); ++ while (line < n && (sz = f_gets(buf, sizeof(buf), f))) { ++ line++; ++ printer(ctx, EQU_CH, line, off, sz, buf); ++ off += sz; ++ while (buf[sz - 1] != '\n') { ++ if (!(sz = f_gets(buf, sizeof(buf), f))) { ++ printer(ctx, 0, 0, 0, 1, "\n"); ++ break; ++ } ++ printer(ctx, 0, 0, 0, sz, buf); ++ off += sz; ++ } ++ } ++ if (line != n) { ++ goto err; ++ } ++ ++ if (op->cmd == add_cmd) { ++ n = op->T2 - op->T1 + 1; ++ while (n) { ++ printer(ctx, DEL_CH, 0, 0, 1, "\n"); ++ n--; ++ } ++ } ++ if (op->cmd == del_cmd) { ++ n = op->F2 - op->F1 + 1; ++ while (n && (sz = f_gets(buf, sizeof(buf), f))) { ++ line++; ++ printer(ctx, ADD_CH, line, off, sz, buf); ++ off += sz; ++ while (buf[sz - 1] != '\n') { ++ if (!(sz = f_gets(buf, sizeof(buf), f))) { ++ printer(ctx, 0, 0, 0, 1, "\n"); ++ break; ++ } ++ printer(ctx, 0, 0, 0, sz, buf); ++ off += sz; ++ } ++ n--; ++ } ++ if (n) { ++ goto err; ++ } ++ } ++ if (op->cmd == 'c') { ++ n = op->F2 - op->F1 + 1; ++ while (n && (sz = f_gets(buf, sizeof(buf), f))) { ++ line++; ++ printer(ctx, CHG_CH, line, off, sz, buf); ++ off += sz; ++ while (buf[sz - 1] != '\n') { ++ if (!(sz = f_gets(buf, sizeof(buf), f))) { ++ printer(ctx, 0, 0, 0, 1, "\n"); ++ break; ++ } ++ printer(ctx, 0, 0, 0, sz, buf); ++ off += sz; ++ } ++ n--; ++ } ++ if (n) { ++ goto err; ++ } ++ n = op->T2 - op->T1 - (op->F2 - op->F1); ++ while (n > 0) { ++ printer(ctx, CHG_CH, 0, 0, 1, "\n"); ++ n--; ++ } ++ } ++ } ++#undef T2 ++#undef T1 ++#undef F2 ++#undef F1 ++ ++ while ((sz = f_gets(buf, sizeof(buf), f))) { ++ line++; ++ printer(ctx, EQU_CH, line, off, sz, buf); ++ off += sz; ++ while (buf[sz - 1] != '\n') { ++ if (!(sz = f_gets(buf, sizeof(buf), f))) { ++ printer(ctx, 0, 0, 0, 1, "\n"); ++ break; ++ } ++ printer(ctx, 0, 0, 0, sz, buf); ++ off += sz; ++ } ++ } ++ ++ f_close(f); ++ return 0; ++ ++ err: ++ f_close(f); ++ return -1; ++} ++ ++ ++/* horizontal diff ***********************************************************/ ++ ++ ++/** ++ * Initialize list of longest common substring offsets. ++ * ++ * \param set list, must be non-NULL ++ */ ++static void ++lcs_init (LCSET *set) ++{ ++ set->len = set->max = 0; ++ set->data = NULL; ++} ++ ++ ++/** ++ * Reset list of longest common substring offsets. ++ * ++ * \param set list, must be non-NULL ++ * ++ * \note does nto deallocate storage ++ */ ++static void ++lcs_reset (LCSET *set) ++{ ++ set->len = 0; ++} ++ ++ ++/** ++ * Enlarge list of longest common substring offsets. ++ * ++ * \param set list, must be non-NULL ++ * ++ * \return new element, or NULL if error ++ */ ++static PAIR * ++lcs_enlarge (LCSET *set) ++{ ++ PAIR *p; ++ if (set->len == set->max) { ++ int max = set->max + 4; ++ p = realloc(set->data, max * sizeof(set->data[0])); ++ if (p == NULL) { ++ return NULL; ++ } ++ set->max = max; ++ set->data = p; ++ } ++ p = set->data + set->len++; ++ return p; ++} ++ ++ ++/** ++ * Free list of longest common substring offsets. ++ * ++ * \param set list, must be non-NULL ++ */ ++static void ++lcs_free (LCSET *set) ++{ ++ free(set->data); ++ lcs_init(set); ++} ++ ++ ++/** ++ * Initialize list of horizontal diff ranges. ++ * ++ * \param set list, must be non-NULL ++ */ ++static void ++hdiff_init (HDIFF *set) ++{ ++ set->len = set->max = 0; ++ set->data = NULL; ++ set->error = 0; ++} ++ ++ ++/** ++ * Enlarge list of horizontal diff ranges. ++ * ++ * \param set list, must be non-NULL ++ * ++ * \return new element, or NULL if error ++ */ ++static BRACKET * ++hdiff_enlarge (HDIFF *set) ++{ ++ BRACKET *p; ++ if (set->error) { ++ return NULL; ++ } ++ if (set->len == set->max) { ++ int max = set->max + 4; ++ p = realloc(set->data, max * sizeof(set->data[0])); ++ if (p == NULL) { ++ return NULL; ++ } ++ set->max = max; ++ set->data = p; ++ } ++ p = set->data + set->len++; ++ return p; ++} ++ ++ ++/** ++ * Free list of horizontal diff ranges. ++ * ++ * \param set list, must be non-NULL ++ */ ++static void ++hdiff_free (HDIFF *set) ++{ ++ free(set->data); ++ hdiff_init(set); ++} ++ ++ ++/** ++ * Longest common substring. ++ * ++ * \param s first string ++ * \param m length of first string ++ * \param t second string ++ * \param n length of second string ++ * \param ret list of offsets for longest common substrings inside each string ++ * \param min minimum length of common substrings ++ * ++ * \return 0 if success, nonzero otherwise ++ */ ++static int ++lcsubstr (const char *s, int m, const char *t, int n, LCSET *ret, int min) ++{ ++ int i, j; ++ ++ int *Lprev, *Lcurr; ++ ++ int z = 0; ++ ++ lcs_init(ret); ++ ++ if (m < min || n < min) { ++ /* XXX early culling */ ++ return 0; ++ } ++ ++ Lprev = calloc(n + 1, sizeof(int)); ++ if (Lprev == NULL) { ++ goto err_0; ++ } ++ Lcurr = calloc(n + 1, sizeof(int)); ++ if (Lcurr == NULL) { ++ goto err_1; ++ } ++ ++ for (i = 0; i < m; i++) { ++ int *L = Lprev; ++ Lprev = Lcurr; ++ Lcurr = L; ++#ifdef USE_MEMSET_IN_LCS ++ memset(Lcurr, 0, (n + 1) * sizeof(int)); ++#endif ++ for (j = 0; j < n; j++) { ++#ifndef USE_MEMSET_IN_LCS ++ Lcurr[j + 1] = 0; ++#endif ++ if (s[i] == t[j]) { ++ int v = Lprev[j] + 1; ++ Lcurr[j + 1] = v; ++ if (z < v) { ++ z = v; ++ lcs_reset(ret); ++ } ++ if (z == v && z >= min) { ++ int off0 = i - z + 1; ++ int off1 = j - z + 1; ++ int k; ++ PAIR *p; ++ for (p = ret->data, k = 0; k < ret->len; k++, p++) { ++ if ((*p)[0] == off0) { ++ break; ++ } ++ if ((*p)[1] == off1) { ++ break; ++ } ++ } ++ if (k == ret->len) { ++ p = lcs_enlarge(ret); ++ if (p == NULL) { ++ goto err_2; ++ } ++ (*p)[0] = off0; ++ (*p)[1] = off1; ++ } ++ } ++ } ++ } ++ } ++ ++ free(Lcurr); ++ free(Lprev); ++ return z; ++ ++ err_2: ++ free(Lcurr); ++ err_1: ++ free(Lprev); ++ err_0: ++ lcs_free(ret); ++ return -1; ++} ++ ++ ++/** ++ * Scan recursively for common substrings and build ranges. ++ * ++ * \param s first string ++ * \param t second string ++ * \param bracket current limits for both of the strings ++ * \param min minimum length of common substrings ++ * \param hdiff list of horizontal diff ranges to fill ++ * \param depth recursion depth ++ * ++ * \return 0 if success, nonzero otherwise ++ */ ++static int ++hdiff_multi (const char *s, const char *t, const BRACKET bracket, int min, HDIFF *hdiff, unsigned int depth) ++{ ++ BRACKET *p; ++ ++ if (depth--) { ++ LCSET ret; ++ BRACKET b; ++ int len = lcsubstr(s + bracket[0].off, bracket[0].len, ++ t + bracket[1].off, bracket[1].len, &ret, min); ++ if (ret.len) { ++ int k = 0; ++ ++ b[0].off = bracket[0].off; ++ b[0].len = ret.data[k][0]; ++ b[1].off = bracket[1].off; ++ b[1].len = ret.data[k][1]; ++ hdiff_multi(s, t, b, min, hdiff, depth); ++ ++ for (k = 0; k < ret.len - 1; k++) { ++ b[0].off = bracket[0].off + ret.data[k][0] + len; ++ b[0].len = ret.data[k + 1][0] - ret.data[k][0] - len; ++ b[1].off = bracket[1].off + ret.data[k][1] + len; ++ b[1].len = ret.data[k + 1][1] - ret.data[k][1] - len; ++ hdiff_multi(s, t, b, min, hdiff, depth); ++ } ++ ++ b[0].off = bracket[0].off + ret.data[k][0] + len; ++ b[0].len = bracket[0].len - ret.data[k][0] - len; ++ b[1].off = bracket[1].off + ret.data[k][1] + len; ++ b[1].len = bracket[1].len - ret.data[k][1] - len; ++ hdiff_multi(s, t, b, min, hdiff, depth); ++ ++ lcs_free(&ret); ++ return 0; ++ } ++ } ++ ++ p = hdiff_enlarge(hdiff); ++ if (p == NULL) { ++ return -1; ++ } ++ (*p)[0].off = bracket[0].off; ++ (*p)[0].len = bracket[0].len; ++ (*p)[1].off = bracket[1].off; ++ (*p)[1].len = bracket[1].len; ++ ++ return 0; ++} ++ ++ ++/** ++ * Build list of horizontal diff ranges. ++ * ++ * \param s first string ++ * \param m length of first string ++ * \param t second string ++ * \param n length of second string ++ * \param min minimum length of common substrings ++ * \param hdiff list of horizontal diff ranges to fill ++ * \param depth recursion depth ++ * ++ * \return 0 if success, nonzero otherwise ++ */ ++static int ++hdiff_scan (const char *s, int m, const char *t, int n, int min, HDIFF *hdiff, unsigned int depth) ++{ ++ int i; ++ BRACKET b; ++ ++ /* dumbscan (single horizontal diff) -- does not compress whitespace */ ++ ++ for (i = 0; i < m && i < n && s[i] == t[i]; i++) { ++ } ++ for (; m > i && n > i && s[m - 1] == t[n - 1]; m--, n--) { ++ } ++ b[0].off = i; ++ b[0].len = m - i; ++ b[1].off = i; ++ b[1].len = n - i; ++ ++ /* smartscan (multiple horizontal diff) */ ++ ++ hdiff_init(hdiff); ++ hdiff_multi(s, t, b, min, hdiff, depth); ++ if (hdiff->error) { ++ hdiff_free(hdiff); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++ ++/* read line *****************************************************************/ ++ ++ ++/** ++ * Check if character is inside horizontal diff limits. ++ * ++ * \param k rank of character inside line ++ * \param hdiff horizontal diff structure ++ * \param ord 0 if reading from first file, 1 if reading from 2nd file ++ * ++ * \return TRUE if inside hdiff limits, FALSE otherwise ++ */ ++static int ++is_inside (int k, HDIFF *hdiff, int ord) ++{ ++ int i; ++ BRACKET *b; ++ for (b = hdiff->data, i = 0; i < hdiff->len; i++, b++) { ++ int start = (*b)[ord].off; ++ int end = start + (*b)[ord].len; ++ if (k >= start && k < end) { ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++ ++/** ++ * Copy `src' to `dst' expanding tabs. ++ * ++ * \param dst destination buffer ++ * \param src source buffer ++ * \param srcsize size of src buffer ++ * \param base virtual base of this string, needed to calculate tabs ++ * \param ts tab size ++ * ++ * \return new virtual base ++ * ++ * \note The procedure returns when all bytes are consumed from `src' ++ */ ++static int ++cvt_cpy (char *dst, const char *src, size_t srcsize, int base, int ts) ++{ ++ int i; ++ for (i = 0; srcsize; i++, src++, dst++, srcsize--) { ++ *dst = *src; ++ if (*src == '\t') { ++ int j = TAB_SKIP(ts, i + base); ++ i += j - 1; ++ while (j-- > 0) { ++ *dst++ = ' '; ++ } ++ dst--; ++ } ++ } ++ return i + base; ++} ++ ++ ++/** ++ * Copy `src' to `dst' expanding tabs. ++ * ++ * \param dst destination buffer ++ * \param dstsize size of dst buffer ++ * \param[in,out] _src source buffer ++ * \param srcsize size of src buffer ++ * \param base virtual base of this string, needed to calculate tabs ++ * \param ts tab size ++ * ++ * \return new virtual base ++ * ++ * \note The procedure returns when all bytes are consumed from `src' ++ * or `dstsize' bytes are written to `dst' ++ * \note Upon return, `src' points to the first unwritten character in source ++ */ ++static int ++cvt_ncpy (char *dst, int dstsize, const char **_src, size_t srcsize, int base, int ts) ++{ ++ int i; ++ const char *src = *_src; ++ for (i = 0; i < dstsize && srcsize; i++, src++, dst++, srcsize--) { ++ *dst = *src; ++ if (*src == '\t') { ++ int j = TAB_SKIP(ts, i + base); ++ if (j > dstsize - i) { ++ j = dstsize - i; ++ } ++ i += j - 1; ++ while (j-- > 0) { ++ *dst++ = ' '; ++ } ++ dst--; ++ } ++ } ++ *_src = src; ++ return i + base; ++} ++ ++ ++/** ++ * Read line from memory, converting tabs to spaces and padding with spaces. ++ * ++ * \param src buffer to read from ++ * \param srcsize size of src buffer ++ * \param dst buffer to read to ++ * \param dstsize size of dst buffer, excluding trailing null ++ * \param skip number of characters to skip ++ * \param ts tab size ++ * \param show_cr show trailing carriage return as ^M ++ * ++ * \return negative on error, otherwise number of bytes except padding ++ */ ++static int ++cvt_mget (const char *src, size_t srcsize, char *dst, int dstsize, int skip, int ts, int show_cr) ++{ ++ int sz = 0; ++ if (src != NULL) { ++ int i; ++ char *tmp = dst; ++ const int base = 0; ++ for (i = 0; dstsize && srcsize && *src != '\n'; i++, src++, srcsize--) { ++ if (*src == '\t') { ++ int j = TAB_SKIP(ts, i + base); ++ i += j - 1; ++ while (j-- > 0) { ++ if (skip) { ++ skip--; ++ } else if (dstsize) { ++ dstsize--; ++ *dst++ = ' '; ++ } ++ } ++ } else if (src[0] == '\r' && (srcsize == 1 || src[1] == '\n')) { ++ if (!skip && show_cr) { ++ if (dstsize > 1) { ++ dstsize -= 2; ++ *dst++ = '^'; ++ *dst++ = 'M'; ++ } else { ++ dstsize--; ++ *dst++ = '.'; ++ } ++ } ++ break; ++ } else { ++ if (skip) { ++ skip--; ++ } else { ++ dstsize--; ++ *dst++ = is_printable(*src) ? *src : '.'; ++ } ++ } ++ } ++ sz = dst - tmp; ++ } ++ while (dstsize) { ++ dstsize--; ++ *dst++ = ' '; ++ } ++ *dst = '\0'; ++ return sz; ++} ++ ++ ++/** ++ * Read line from memory and build attribute array. ++ * ++ * \param src buffer to read from ++ * \param srcsize size of src buffer ++ * \param dst buffer to read to ++ * \param dstsize size of dst buffer, excluding trailing null ++ * \param skip number of characters to skip ++ * \param ts tab size ++ * \param show_cr show trailing carriage return as ^M ++ * \param hdiff horizontal diff structure ++ * \param ord 0 if reading from first file, 1 if reading from 2nd file ++ * \param att buffer of attributes ++ * ++ * \return negative on error, otherwise number of bytes except padding ++ */ ++static int ++cvt_mgeta (const char *src, size_t srcsize, char *dst, int dstsize, int skip, int ts, int show_cr, HDIFF *hdiff, int ord, char *att) ++{ ++ int sz = 0; ++ if (src != NULL) { ++ int i, k; ++ char *tmp = dst; ++ const int base = 0; ++ for (i = 0, k = 0; dstsize && srcsize && *src != '\n'; i++, k++, src++, srcsize--) { ++ if (*src == '\t') { ++ int j = TAB_SKIP(ts, i + base); ++ i += j - 1; ++ while (j-- > 0) { ++ if (skip) { ++ skip--; ++ } else if (dstsize) { ++ dstsize--; ++ *att++ = is_inside(k, hdiff, ord); ++ *dst++ = ' '; ++ } ++ } ++ } else if (src[0] == '\r' && (srcsize == 1 || src[1] == '\n')) { ++ if (!skip && show_cr) { ++ if (dstsize > 1) { ++ dstsize -= 2; ++ *att++ = is_inside(k, hdiff, ord); ++ *dst++ = '^'; ++ *att++ = is_inside(k, hdiff, ord); ++ *dst++ = 'M'; ++ } else { ++ dstsize--; ++ *att++ = is_inside(k, hdiff, ord); ++ *dst++ = '.'; ++ } ++ } ++ break; ++ } else { ++ if (skip) { ++ skip--; ++ } else { ++ dstsize--; ++ *att++ = is_inside(k, hdiff, ord); ++ *dst++ = is_printable(*src) ? *src : '.'; ++ } ++ } ++ } ++ sz = dst - tmp; ++ } ++ while (dstsize) { ++ dstsize--; ++ *att++ = 0; ++ *dst++ = ' '; ++ } ++ *dst = '\0'; ++ return sz; ++} ++ ++ ++/** ++ * Read line from file, converting tabs to spaces and padding with spaces. ++ * ++ * \param f file stream to read from ++ * \param off offset of line inside file ++ * \param dst buffer to read to ++ * \param dstsize size of dst buffer, excluding trailing null ++ * \param skip number of characters to skip ++ * \param ts tab size ++ * \param show_cr show trailing carriage return as ^M ++ * ++ * \return negative on error, otherwise number of bytes except padding ++ */ ++static int ++cvt_fget (FBUF *f, off_t off, char *dst, int dstsize, int skip, int ts, int show_cr) ++{ ++ int base = 0; ++ int old_base = base; ++ const int amount = dstsize; ++ ++ int useful; ++ int offset; ++ ++ ssize_t i; ++ size_t sz; ++ ++ int lastch = '\0'; ++ ++ const char *q = NULL; ++ char tmp[BUFSIZ]; /* XXX capacity must be >= max{dstsize + 1, amount} */ ++ char cvt[BUFSIZ]; /* XXX capacity must be >= MAX_TAB_WIDTH * amount */ ++ ++ if ((int)sizeof(tmp) < amount || (int)sizeof(tmp) <= dstsize || (int)sizeof(cvt) < 8 * amount) { ++ /* abnormal, but avoid buffer overflow */ ++ memset(dst, ' ', dstsize); ++ dst[dstsize] = '\0'; ++ return 0; ++ } ++ ++ f_seek(f, off, SEEK_SET); ++ ++ while (skip > base) { ++ old_base = base; ++ if (!(sz = f_gets(tmp, amount, f))) { ++ break; ++ } ++ base = cvt_cpy(cvt, tmp, sz, old_base, ts); ++ if (cvt[base - old_base - 1] == '\n') { ++ q = &cvt[base - old_base - 1]; ++ base = old_base + q - cvt + 1; ++ break; ++ } ++ } ++ ++ useful = base - skip; ++ offset = skip - old_base; ++ ++ if (useful < 0) { ++ memset(dst, ' ', dstsize); ++ dst[dstsize] = '\0'; ++ return 0; ++ } ++ ++ if (useful <= dstsize) { ++ if (useful) { ++ memmove(dst, cvt + offset, useful); ++ } ++ if (q == NULL && (sz = f_gets(tmp, dstsize - useful + 1, f))) { ++ const char *ptr = tmp; ++ useful += cvt_ncpy(dst + useful, dstsize - useful, &ptr, sz, base, ts) - base; ++ if (ptr < tmp + sz) { ++ lastch = *ptr; ++ } ++ } ++ sz = useful; ++ } else { ++ memmove(dst, cvt + offset, dstsize); ++ sz = dstsize; ++ lastch = cvt[offset + dstsize]; ++ } ++ ++ dst[sz] = lastch; ++ for (i = 0; i < sz && dst[i] != '\n'; i++) { ++ if (dst[i] == '\r' && dst[i + 1] == '\n') { ++ if (show_cr) { ++ if (i + 1 < dstsize) { ++ dst[i++] = '^'; ++ dst[i++] = 'M'; ++ } else { ++ dst[i++] = '.'; ++ } ++ } ++ break; ++ } else if (!is_printable(dst[i])) { ++ dst[i] = '.'; ++ } ++ } ++ for (; i < dstsize; i++) { ++ dst[i] = ' '; ++ } ++ dst[i] = '\0'; ++ return sz; ++} ++ ++ ++/* diff printers et al *******************************************************/ ++ ++ ++/** ++ * Initialize diff cache. ++ * ++ * \param a cache, must be non-NULL ++ */ ++static void ++cc_init (DIFFCC *a) ++{ ++ a->len = a->max = 0; ++ a->data = NULL; ++ a->error = 0; ++} ++ ++ ++/** ++ * Enlarge diff cache. ++ * ++ * \param a cache, must be non-NULL ++ * ++ * \return new element, or NULL if error ++ */ ++static DIFFLN * ++cc_enlarge (DIFFCC *a) ++{ ++ DIFFLN *p; ++ if (a->error) { ++ return NULL; ++ } ++ if (a->len == a->max) { ++ int max = a->max + 256; ++ p = realloc(a->data, max * sizeof(a->data[0])); ++ if (p == NULL) { ++ a->error = -1; ++ return NULL; ++ } ++ a->max = max; ++ a->data = p; ++ } ++ p = a->data + a->len++; ++ p->p = NULL; ++ return p; ++} ++ ++ ++/** ++ * Free diff cache. ++ * ++ * \param a cache, must be non-NULL ++ */ ++static void ++cc_free (DIFFCC *a) ++{ ++ int i; ++ DIFFLN *p; ++ for (p = a->data, i = 0; i < a->len; i++, p++) { ++ if (p->p) { ++ free(p->p); ++ } ++ } ++ free(a->data); ++ cc_init(a); ++} ++ ++ ++static int ++printer (void *ctx, int ch, int line, off_t off, size_t sz, const char *str) ++{ ++ DIFFLN *p; ++ DIFFCC *a = ((PRINTER_CTX *)ctx)->a; ++ DSRC dsrc = ((PRINTER_CTX *)ctx)->dsrc; ++ if (a->error) { ++ return -1; ++ } ++ if (ch) { ++ p = cc_enlarge(a); ++ if (p == NULL) { ++ return -1; ++ } ++ p->ch = ch; ++ p->line = line; ++ p->u.off = off; ++ if (dsrc == DATA_SRC_MEM && line) { ++ if (sz && str[sz - 1] == '\n') { ++ sz--; ++ } ++ if (sz) { ++ p->p = malloc(sz); ++ if (p->p == NULL) { ++ a->error = 1; ++ return -1; ++ } ++ memcpy(p->p, str, sz); ++ } ++ p->u.len = sz; ++ } ++ } else if (dsrc == DATA_SRC_MEM) { ++ if (!a->len) { ++ a->error = 1; ++ return -1; ++ } ++ p = a->data + a->len - 1; ++ if (sz && str[sz - 1] == '\n') { ++ sz--; ++ } ++ if (sz) { ++ size_t new_size = p->u.len + sz; ++ char *q = realloc(p->p, new_size); ++ if (q == NULL) { ++ a->error = 1; ++ return -1; ++ } ++ memcpy(q + p->u.len, str, sz); ++ p->p = q; ++ } ++ p->u.len += sz; ++ } ++ if (dsrc == DATA_SRC_TMP && (line || !ch)) { ++ FBUF *f = ((PRINTER_CTX *)ctx)->f; ++ f_write(f, str, sz); ++ } ++ return 0; ++} ++ ++ ++static int ++redo_diff (WDiff *view) ++{ ++ FBUF *const *f = view->f; ++ DIFFCC *a = view->a; ++ ++ PRINTER_CTX ctx; ++ DIFFOPS ops; ++ int ndiff; ++ int rv; ++ ++ char extra[256]; ++ ++ extra[0] = '\0'; ++ if (view->opt.quality == 2) { ++ strcat(extra, " -d"); ++ } ++ if (view->opt.quality == 1) { ++ strcat(extra, " --speed-large-files"); ++ } ++ if (view->opt.strip_trailing_cr) { ++ strcat(extra, " --strip-trailing-cr"); ++ } ++ if (view->opt.ignore_tab_expansion) { ++ strcat(extra, " -E"); ++ } ++ if (view->opt.ignore_space_change) { ++ strcat(extra, " -b"); ++ } ++ if (view->opt.ignore_all_space) { ++ strcat(extra, " -w"); ++ } ++ if (view->opt.ignore_case) { ++ strcat(extra, " -i"); ++ } ++ ++ if (view->dsrc != DATA_SRC_MEM) { ++ f_reset(f[0]); ++ f_reset(f[1]); ++ } ++ ++ ndiff = dff_execute(view->args, extra, view->file[0], view->file[1], &ops); ++ if (ndiff < 0) { ++ return -1; ++ } ++ ++ ctx.dsrc = view->dsrc; ++ ++ rv = 0; ++ ++ cc_init(&a[0]); ++ ctx.a = &a[0]; ++ ctx.f = f[0]; ++ rv |= dff_reparse(0, view->file[0], &ops, printer, &ctx); ++ ++ cc_init(&a[1]); ++ ctx.a = &a[1]; ++ ctx.f = f[1]; ++ rv |= dff_reparse(1, view->file[1], &ops, printer, &ctx); ++ ++ dff_free(&ops); ++ ++ if (rv || a[0].error || a[1].error || a[0].len != a[1].len) { ++ cc_free(&a[0]); ++ cc_free(&a[1]); ++ return -1; ++ } ++ ++ if (view->dsrc == DATA_SRC_TMP) { ++ f_trunc(f[0]); ++ f_trunc(f[1]); ++ } ++ ++ if (view->dsrc == DATA_SRC_MEM && HDIFF_ENABLE) { ++ view->hdiff = malloc(a[0].len * sizeof(HDIFF *)); ++ if (view->hdiff != NULL) { ++ int i; ++ const DIFFLN *p; ++ const DIFFLN *q; ++ for (p = a[0].data, q = a[1].data, i = 0; i < a[0].len; i++, q++, p++) { ++ HDIFF *h = NULL; ++ if (p->line && q->line && p->ch == CHG_CH) { ++ h = malloc(sizeof(HDIFF)); ++ if (h != NULL) { ++ int rv = hdiff_scan(p->p, p->u.len, q->p, q->u.len, HDIFF_MINCTX, h, HDIFF_DEPTH); ++ if (rv != 0) { ++ free(h); ++ h = NULL; ++ } ++ } ++ } ++ view->hdiff[i] = h; ++ } ++ } ++ } ++ ++ return ndiff; ++} ++ ++ ++static void ++destroy_hdiff (WDiff *view) ++{ ++ if (view->hdiff != NULL) { ++ int i; ++ int len = view->a[0].len; ++ for (i = 0; i < len; i++) { ++ HDIFF *h = view->hdiff[i]; ++ if (h != NULL) { ++ hdiff_free(h); ++ free(h); ++ } ++ } ++ free(view->hdiff); ++ view->hdiff = NULL; ++ } ++} ++ ++ ++/* stuff *********************************************************************/ ++ ++ ++static int ++get_digits (unsigned int n) ++{ ++ int d = 1; ++ while (n /= 10) { ++ d++; ++ } ++ return d; ++} ++ ++ ++static int ++get_line_numbers (const DIFFCC *a, int pos, int *linenum, int *lineofs) ++{ ++ const DIFFLN *p; ++ ++ *linenum = 0; ++ *lineofs = 0; ++ ++ if (a->len) { ++ if (pos >= a->len) { ++ pos = a->len - 1; ++ } ++ ++ p = a->data + pos; ++ ++ if (!p->line) { ++ int n; ++ for (n = pos; n > 0; n--) { ++ p--; ++ if (p->line) { ++ break; ++ } ++ } ++ *lineofs = pos - n + 1; ++ } ++ ++ *linenum = p->line; ++ } ++ return 0; ++} ++ ++ ++static int ++calc_nwidth (const DIFFCC *const a) ++{ ++ int l1, o1; ++ int l2, o2; ++ get_line_numbers(&a[0], a[0].len - 1, &l1, &o1); ++ get_line_numbers(&a[1], a[1].len - 1, &l2, &o2); ++ if (l1 < l2) { ++ l1 = l2; ++ } ++ return get_digits(l1); ++} ++ ++ ++static int ++find_prev_hunk (const DIFFCC *a, int pos) ++{ ++#if 1 ++ while (pos > 0 && a->data[pos].ch != EQU_CH) { ++ pos--; ++ } ++ while (pos > 0 && a->data[pos].ch == EQU_CH) { ++ pos--; ++ } ++#else ++ while (pos > 0 && a->data[pos - 1].ch == EQU_CH) { ++ pos--; ++ } ++ while (pos > 0 && a->data[pos - 1].ch != EQU_CH) { ++ pos--; ++ } ++#endif ++ ++ return pos; ++} ++ ++ ++static int ++find_next_hunk (const DIFFCC *a, int pos) ++{ ++ while (pos < a->len && a->data[pos].ch != EQU_CH) { ++ pos++; ++ } ++ while (pos < a->len && a->data[pos].ch == EQU_CH) { ++ pos++; ++ } ++ ++ return pos; ++} ++ ++ ++/* view routines and callbacks ***********************************************/ ++ ++ ++static void ++view_compute_areas (WDiff *view) ++{ ++ view->height = LINES - 2; ++ view->width1 = COLS / 2; ++ view->width2 = COLS / 2 + (COLS & 1); ++} ++ ++ ++static int ++view_init (WDiff *view, const char *args, const char *file1, const char *file2, DSRC dsrc) ++{ ++ int ndiff; ++ FBUF *f[2]; ++ ++ f[0] = NULL; ++ f[1] = NULL; ++ ++ if (dsrc == DATA_SRC_TMP) { ++ f[0] = f_temp(); ++ if (f[0] == NULL) { ++ goto err_2; ++ } ++ f[1] = f_temp(); ++ if (f[1] == NULL) { ++ f_close(f[0]); ++ goto err_2; ++ } ++ } ++ if (dsrc == DATA_SRC_ORG) { ++ f[0] = f_open(file1, O_RDONLY); ++ if (f[0] == NULL) { ++ goto err_2; ++ } ++ f[1] = f_open(file2, O_RDONLY); ++ if (f[1] == NULL) { ++ f_close(f[0]); ++ goto err_2; ++ } ++ } ++ ++ view->args = args; ++ view->file[0] = file1; ++ view->file[1] = file2; ++ view->f[0] = f[0]; ++ view->f[1] = f[1]; ++ view->hdiff = NULL; ++ view->dsrc = dsrc; ++ ++ ndiff = redo_diff(view); ++ if (ndiff < 0) { ++ goto err_3; ++ } ++ ++ view->ndiff = ndiff; ++ ++ view->view_quit = 0; ++ ++ view->new_frame = 1; ++ view->skip_rows = 0; ++ view->skip_cols = 0; ++ view->display_symbols = 0; ++ view->display_numbers = 0; ++ view->show_cr = 1; ++ view->show_hdiff = 1; ++ view->tab_size = 8; ++ view->ord = 0; ++ ++ view->opt.quality = 0; ++ view->opt.strip_trailing_cr = 0; ++ view->opt.ignore_tab_expansion = 0; ++ view->opt.ignore_space_change = 0; ++ view->opt.ignore_all_space = 0; ++ view->opt.ignore_case = 0; ++ ++ view_compute_areas(view); ++ return 0; ++ ++ err_3: ++ if (dsrc != DATA_SRC_MEM) { ++ f_close(f[1]); ++ f_close(f[0]); ++ } ++ err_2: ++ return -1; ++} ++ ++ ++static int ++view_reinit (WDiff *view) ++{ ++ int ndiff = view->ndiff; ++ ++ diffopt_widgets[2].value = view->opt.quality; ++ diffopt_widgets[2].result = &view->opt.quality; ++ diffopt_widgets[3].result = &view->opt.strip_trailing_cr; ++ diffopt_widgets[4].result = &view->opt.ignore_all_space; ++ diffopt_widgets[5].result = &view->opt.ignore_space_change; ++ diffopt_widgets[6].result = &view->opt.ignore_tab_expansion; ++ diffopt_widgets[7].result = &view->opt.ignore_case; ++ ++ if (quick_dialog(&diffopt) != B_CANCEL) { ++ destroy_hdiff(view); ++ cc_free(&view->a[1]); ++ cc_free(&view->a[0]); ++ ndiff = redo_diff(view); ++ if (ndiff >= 0) { ++ view->ndiff = ndiff; ++ } ++ } ++ return ndiff; ++} ++ ++ ++static void ++view_fini (WDiff *view) ++{ ++ if (view->dsrc != DATA_SRC_MEM) { ++ f_close(view->f[1]); ++ f_close(view->f[0]); ++ } ++ ++ destroy_hdiff(view); ++ cc_free(&view->a[1]); ++ cc_free(&view->a[0]); ++} ++ ++ ++static int ++view_display_file (const WDiff *view, int ord, ++ int r, int c, int height, int width) ++{ ++ int i, j; ++ char buf[BUFSIZ]; ++ FBUF *f = view->f[ord]; ++ const DIFFCC *a = &view->a[ord]; ++ int skip = view->skip_cols; ++ int display_symbols = view->display_symbols; ++ int display_numbers = view->display_numbers; ++ int show_cr = view->show_cr; ++ int tab_size = view->tab_size; ++ const DIFFLN *p; ++ ++ int nwidth = display_numbers; ++ int xwidth = display_symbols + display_numbers; ++ ++ if (xwidth) { ++ if (xwidth > width && display_symbols) { ++ xwidth--; ++ display_symbols = 0; ++ } ++ if (xwidth > width && display_numbers) { ++ xwidth = width; ++ display_numbers = width; ++ } ++ ++ xwidth++; ++ ++ c += xwidth; ++ width -= xwidth; ++ ++ if (width < 0) { ++ width = 0; ++ } ++ } ++ ++ if ((int)sizeof(buf) <= width || (int)sizeof(buf) <= nwidth) { ++ /* abnormal, but avoid buffer overflow */ ++ return -1; ++ } ++ ++ for (i = view->skip_rows, j = 0, p = a->data + i; i < a->len && j < height; p++, j++, i++) { ++ int ch = p->ch; ++ tty_setcolor(NORMAL_COLOR); ++ if (display_symbols) { ++ tty_gotoyx(r + j, c - 2); ++ tty_print_char(ch); ++ } ++ if (p->line) { ++ if (display_numbers) { ++ tty_gotoyx(r + j, c - xwidth); ++ snprintf(buf, display_numbers + 1, "%*d", nwidth, p->line); ++ tty_print_string(buf); ++ } ++ if (f == NULL) { ++ if (view->show_hdiff) { ++ int k; ++ if (view->hdiff != NULL && view->hdiff[i] != NULL) { ++ char att[BUFSIZ]; ++ cvt_mgeta(p->p, p->u.len, buf, width, skip, tab_size, show_cr, view->hdiff[i], ord, att); ++ tty_gotoyx(r + j, c); ++ for (k = 0; k < width; k++) { ++ tty_setcolor(att[k] ? DFFCHH_COLOR : DFFCHG_COLOR); ++ tty_print_char(buf[k]); ++ } ++ continue; ++ } else if (ch == CHG_CH) { ++ int sz = cvt_mget(p->p, p->u.len, buf, width, skip, tab_size, show_cr); ++ tty_gotoyx(r + j, c); ++ tty_setcolor(DFFCHH_COLOR); ++ for (k = 0; k < sz; k++) { ++ tty_print_char(buf[k]); ++ } ++ tty_setcolor(DFFCHG_COLOR); ++ for (; k < width; k++) { ++ tty_print_char(buf[k]); ++ } ++ continue; ++ } ++ } ++ cvt_mget(p->p, p->u.len, buf, width, skip, tab_size, show_cr); ++ } else { ++ cvt_fget(f, p->u.off, buf, width, skip, tab_size, show_cr); ++ } ++ if (ch == ADD_CH) { ++ tty_setcolor(DFFADD_COLOR); ++ } ++ if (ch == CHG_CH) { ++ tty_setcolor(DFFCHG_COLOR); ++ } ++ } else { ++ if (display_numbers) { ++ tty_gotoyx(r + j, c - xwidth); ++ memset(buf, ' ', display_numbers); ++ buf[display_numbers] = '\0'; ++ tty_print_nstring(buf, display_numbers); ++ } ++ memset(buf, ' ', width); ++ buf[width] = '\0'; ++ if (ch == DEL_CH) { ++ tty_setcolor(DFFDEL_COLOR); ++ } ++ if (ch == CHG_CH) { ++ tty_setcolor(DFFCHD_COLOR); ++ } ++ } ++ tty_gotoyx(r + j, c); ++ tty_print_nstring(buf, width); ++ } ++ tty_setcolor(NORMAL_COLOR); ++ if (width < xwidth - 1) { ++ width = xwidth - 1; ++ } ++ memset(buf, ' ', width); ++ buf[width] = '\0'; ++ for (; j < height; j++) { ++ if (xwidth) { ++ tty_gotoyx(r + j, c - xwidth); ++ tty_print_nstring(buf, xwidth - 1); ++ } ++ tty_gotoyx(r + j, c); ++ tty_print_nstring(buf, width); ++ } ++ ++ return 0; ++} ++ ++ ++static void ++view_status (const WDiff *view) ++{ ++ int ord = view->ord; ++ int skip_rows = view->skip_rows; ++ int skip_cols = view->skip_cols; ++ int width1 = view->width1; ++ int width2 = view->width2; ++ ++ char buf[BUFSIZ]; ++ int filename_width; ++ int linenum, lineofs; ++ ++ tty_setcolor(SELECTED_COLOR); ++ ++ tty_gotoyx(0, 0); ++ get_line_numbers(&view->a[ord], skip_rows, &linenum, &lineofs); ++ ++ filename_width = width1 - 22; ++ if (filename_width < 8) { ++ filename_width = 8; ++ } ++ if (filename_width >= (int)sizeof(buf)) { ++ /* abnormal, but avoid buffer overflow */ ++ filename_width = sizeof(buf) - 1; ++ } ++ trim(strip_home_and_password(view->file[ord]), buf, filename_width); ++ tty_printf("%-*s %6d+%-4d Col %-4d ", filename_width, buf, linenum, lineofs, skip_cols); ++ ++ ord ^= 1; ++ ++ tty_gotoyx(0, width1); ++ get_line_numbers(&view->a[ord], skip_rows, &linenum, &lineofs); ++ ++ filename_width = width2 - 22; ++ if (filename_width < 8) { ++ filename_width = 8; ++ } ++ if (filename_width >= (int)sizeof(buf)) { ++ /* abnormal, but avoid buffer overflow */ ++ filename_width = sizeof(buf) - 1; ++ } ++ trim(strip_home_and_password(view->file[ord]), buf, filename_width); ++ tty_printf("%-*s %6d+%-4d Dif %-4d ", filename_width, buf, linenum, lineofs, view->ndiff); ++} ++ ++ ++static void ++view_update (WDiff *view) ++{ ++ int height = view->height; ++ int width1 = view->width1; ++ int width2 = view->width2; ++ ++ int last = view->a[0].len - 1; ++ ++ if (view->skip_rows > last) { ++ view->skip_rows = last; ++ } ++ if (view->skip_rows < 0) { ++ view->skip_rows = 0; ++ } ++ if (view->skip_cols < 0) { ++ view->skip_cols = 0; ++ } ++ ++ if (height < 2 || width1 < 2 || width2 < 2) { ++ return; ++ } ++ ++ if (view->new_frame) { ++ Dlg_head *h = view->widget.parent; ++ ++ int xwidth = view->display_symbols + view->display_numbers; ++ ++ tty_setcolor(NORMAL_COLOR); ++ draw_box(h, 1, 0, height, width1); ++ draw_box(h, 1, width1, height, width2); ++ ++ if (xwidth) { ++ xwidth++; ++ if (xwidth < width1 - 1) { ++ tty_gotoyx(1, xwidth); ++ tty_print_alt_char('w'/*ACS_TTEE*/); ++ tty_gotoyx(height, xwidth); ++ tty_print_alt_char('v'/*ACS_BTEE*/); ++ tty_print_vline(2, xwidth, height - 2); ++ } ++ if (xwidth < width2 - 1) { ++ tty_gotoyx(1, width1 + xwidth); ++ tty_print_alt_char('w'/*ACS_TTEE*/); ++ tty_gotoyx(height, width1 + xwidth); ++ tty_print_alt_char('v'/*ACS_BTEE*/); ++ tty_print_vline(2, width1 + xwidth, height - 2); ++ } ++ } ++ ++ view->new_frame = 0; ++ } ++ ++ view_status(view); ++ ++ view_display_file(view, view->ord, 2, 1, height - 2, width1 - 2); ++ view_display_file(view, view->ord ^ 1, 2, width1 + 1, height - 2, width2 - 2); ++} ++ ++ ++static void ++view_help_cmd (void) ++{ ++ interactive_display(NULL, "[Diff Viewer]"); ++} ++ ++ ++static void ++view_quit_cmd (WDiff *view) ++{ ++ dlg_stop(view->widget.parent); ++} ++ ++ ++static void ++view_labels (WDiff *view) ++{ ++ Dlg_head *h = view->widget.parent; ++ ++ buttonbar_set_label(h, 1, _("Help"), view_help_cmd); ++ ++ buttonbar_set_label_data(h, 10, _("Quit"), (buttonbarfn)view_quit_cmd, view); ++} ++ ++ ++static int ++view_event (Gpm_Event *event, void *x) ++{ ++ WDiff *view = (WDiff *)x; ++ int result = MOU_NORMAL; ++ ++ /* We are not interested in the release events */ ++ if (!(event->type & (GPM_DOWN | GPM_DRAG))) { ++ return result; ++ } ++ ++ /* Wheel events */ ++ if ((event->buttons & GPM_B_UP) && (event->type & GPM_DOWN)) { ++ view->skip_rows -= 2; ++ view_update(view); ++ return result; ++ } ++ if ((event->buttons & GPM_B_DOWN) && (event->type & GPM_DOWN)) { ++ view->skip_rows += 2; ++ view_update(view); ++ return result; ++ } ++ ++ return result; ++} ++ ++ ++static cb_ret_t ++view_handle_key (WDiff *view, int c) ++{ ++ c = convert_from_input_c(c); ++ ++ switch (c) { ++ case 's': ++ view->display_symbols ^= 1; ++ view->new_frame = 1; ++ return MSG_HANDLED; ++ ++ case 'l': ++ view->display_numbers ^= calc_nwidth(view->a); ++ view->new_frame = 1; ++ return MSG_HANDLED; ++ ++ case 'c': ++ view->show_cr ^= 1; ++ return MSG_HANDLED; ++ ++ case 'h': ++ view->show_hdiff ^= 1; ++ return MSG_HANDLED; ++ ++ case '2': ++ case '3': ++ case '4': ++ case '8': ++ view->tab_size = c - '0'; ++ return MSG_HANDLED; ++ ++ case XCTRL('u'): { ++ view->ord ^= 1; ++ return MSG_HANDLED; ++ } ++ ++ case XCTRL('r'): ++ if (view_reinit(view) < 0) { ++ view->view_quit = 1; ++ } else if (view->display_numbers) { ++ int old = view->display_numbers; ++ view->display_numbers = calc_nwidth(view->a); ++ view->new_frame = (old != view->display_numbers); ++ } ++ return MSG_HANDLED; ++ ++ case 'n': ++ view->skip_rows = find_next_hunk(&view->a[0], view->skip_rows); ++ return MSG_HANDLED; ++ ++ case 'p': ++ view->skip_rows = find_prev_hunk(&view->a[0], view->skip_rows); ++ return MSG_HANDLED; ++ ++ case KEY_HOME: ++ case KEY_M_CTRL | KEY_PPAGE: ++ view->skip_rows = 0; ++ return MSG_HANDLED; ++ ++ case KEY_END: ++ case KEY_M_CTRL | KEY_NPAGE: ++ view->skip_rows = view->a[0].len - 1; ++ return MSG_HANDLED; ++ ++ case KEY_UP: ++ view->skip_rows--; ++ return MSG_HANDLED; ++ ++ case KEY_DOWN: ++ view->skip_rows++; ++ return MSG_HANDLED; ++ ++ case KEY_NPAGE: ++ view->skip_rows += view->height - 2; ++ return MSG_HANDLED; ++ ++ case KEY_PPAGE: ++ view->skip_rows -= view->height - 2; ++ return MSG_HANDLED; ++ ++ case KEY_LEFT: ++ view->skip_cols--; ++ return MSG_HANDLED; ++ ++ case KEY_RIGHT: ++ view->skip_cols++; ++ return MSG_HANDLED; ++ ++ case KEY_M_CTRL | KEY_LEFT: ++ view->skip_cols -= 8; ++ return MSG_HANDLED; ++ ++ case KEY_M_CTRL | KEY_RIGHT: ++ view->skip_cols += 8; ++ return MSG_HANDLED; ++ ++ case XCTRL('a'): ++ view->skip_cols = 0; ++ return MSG_HANDLED; ++ ++ case XCTRL('o'): ++ view_other_cmd(); ++ return MSG_HANDLED; ++ ++ case 'q': ++ case XCTRL('g'): ++ case ESC_CHAR: ++ view->view_quit = 1; ++ return MSG_HANDLED; ++ } ++ ++ /* Key not used */ ++ return MSG_NOT_HANDLED; ++} ++ ++ ++static cb_ret_t ++view_callback (Widget *w, widget_msg_t msg, int parm) ++{ ++ cb_ret_t i; ++ WDiff *view = (WDiff *)w; ++ Dlg_head *h = view->widget.parent; ++ ++ switch (msg) { ++ case WIDGET_INIT: ++ view_labels(view); ++ return MSG_HANDLED; ++ ++ case WIDGET_DRAW: ++ view->new_frame = 1; ++ view_update(view); ++ return MSG_HANDLED; ++ ++ case WIDGET_CURSOR: ++ return MSG_HANDLED; ++ ++ case WIDGET_KEY: ++ i = view_handle_key((WDiff *)view, parm); ++ if (view->view_quit) ++ dlg_stop(h); ++ else { ++ view_update(view); ++ } ++ return i; ++ ++ case WIDGET_IDLE: ++ return MSG_HANDLED; ++ ++ case WIDGET_FOCUS: ++ view_labels(view); ++ return MSG_HANDLED; ++ ++ case WIDGET_DESTROY: ++ return MSG_HANDLED; ++ ++ default: ++ return default_proc(msg, parm); ++ } ++} ++ ++ ++static void ++view_adjust_size (Dlg_head *h) ++{ ++ WDiff *view; ++ WButtonBar *bar; ++ ++ /* Look up the viewer and the buttonbar, we assume only two widgets here */ ++ view = (WDiff *)find_widget_type(h, view_callback); ++ bar = find_buttonbar(h); ++ widget_set_size(&view->widget, 0, 0, LINES, COLS); ++ widget_set_size((Widget *)bar, LINES - 1, 0, 1, COLS); ++ ++ view_compute_areas(view); ++} ++ ++ ++static cb_ret_t ++view_dialog_callback (Dlg_head *h, dlg_msg_t msg, int parm) ++{ ++ switch (msg) { ++ case DLG_RESIZE: ++ view_adjust_size(h); ++ return MSG_HANDLED; ++ ++ default: ++ return default_dlg_callback(h, msg, parm); ++ } ++} ++ ++ ++int ++diff_view (const char *file1, const char *file2) ++{ ++ int error; ++ WDiff *view; ++ WButtonBar *bar; ++ Dlg_head *view_dlg; ++ ++ /* Create dialog and widgets, put them on the dialog */ ++ view_dlg = ++ create_dlg(0, 0, LINES, COLS, NULL, view_dialog_callback, ++ "[Diff Viewer]", NULL, DLG_WANT_TAB); ++ ++ view = g_new0(WDiff, 1); ++ ++ init_widget(&view->widget, 0, 0, LINES, COLS, ++ (callback_fn)view_callback, ++ (mouse_h)view_event); ++ ++ widget_want_cursor(view->widget, 0); ++ ++ bar = buttonbar_new(1); ++ ++ add_widget(view_dlg, bar); ++ add_widget(view_dlg, view); ++ ++ error = view_init(view, "-a", file1, file2, DATA_SRC_MEM); ++ ++ /* Please note that if you add another widget, ++ * you have to modify view_adjust_size to ++ * be aware of it ++ */ ++ if (!error) { ++ run_dlg(view_dlg); ++ view_fini(view); ++ } ++ destroy_dlg(view_dlg); ++ ++ return error; ++} ++#endif diff -ruN mc.bak/files/patch-ydiff.h mc/files/patch-ydiff.h --- mc.bak/files/patch-ydiff.h 1970-01-01 03:00:00.000000000 +0300 +++ mc/files/patch-ydiff.h 2009-12-11 00:04:06.000000000 +0200 @@ -0,0 +1,9 @@ +--- src/ydiff.h 1970-01-01 02:00:00.000000000 +0200 ++++ src/ydiff.h 2007-05-05 13:11:08.000000000 +0300 +@@ -0,0 +1,6 @@ ++#ifndef YDIFF_H_included ++#define YDIFF_H_included ++ ++int diff_view (const char *file1, const char *file2); ++ ++#endif >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200912111306.nBBD6nlT039583>