ports/141368: [UPDATE] misc/mc: update to 4.6.2_1
Andrei Lavreniyuk
andy.lavr at reactor-xg.kiev.ua
Fri Dec 11 13:10:06 UTC 2009
>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 at 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:
More information about the freebsd-ports-bugs
mailing list