From 5dbb548479824a25cbde3f977140c332e666e5ca Mon Sep 17 00:00:00 2001 From: Slava Zanko Date: Fri, 11 Apr 2014 16:16:04 +0300 Subject: [PATCH] Added events for diffviewer Signed-off-by: Slava Zanko --- src/diffviewer/Makefile.am | 9 + src/diffviewer/charset.c | 105 ++ src/diffviewer/charset.h | 22 + src/diffviewer/events.c | 683 ++++++++ src/diffviewer/execute.c | 643 +++++++ src/diffviewer/execute.h | 45 + src/diffviewer/internal.h | 56 +- src/diffviewer/options.c | 183 ++ src/diffviewer/options.h | 26 + src/diffviewer/search.c | 32 +- src/diffviewer/tools.c | 2002 ++++++++++++++++++++++ src/diffviewer/ydiff.c | 3259 +++--------------------------------- src/diffviewer/ydiff.h | 26 +- src/filemanager/cmd.c | 11 +- src/filemanager/midnight.c | 17 +- src/filemanager/midnight.h | 2 +- src/main.c | 9 +- 17 files changed, 4031 insertions(+), 3099 deletions(-) create mode 100644 src/diffviewer/charset.c create mode 100644 src/diffviewer/charset.h create mode 100644 src/diffviewer/events.c create mode 100644 src/diffviewer/execute.c create mode 100644 src/diffviewer/execute.h create mode 100644 src/diffviewer/options.c create mode 100644 src/diffviewer/options.h create mode 100644 src/diffviewer/tools.c diff --git a/src/diffviewer/Makefile.am b/src/diffviewer/Makefile.am index 58e8e5b04..ecb97da8d 100644 --- a/src/diffviewer/Makefile.am +++ b/src/diffviewer/Makefile.am @@ -1,10 +1,19 @@ noinst_LTLIBRARIES = libdiffviewer.la libdiffviewer_la_SOURCES = \ + events.c \ + execute.c execute.h \ internal.h \ + options.c options.h \ search.c \ + tools.c \ ydiff.c ydiff.h +if CHARSET +libdiffviewer_la_SOURCES += charset.c charset.h +endif + + AM_CPPFLAGS = -I$(top_srcdir) $(GLIB_CFLAGS) $(PCRE_CPPFLAGS) libdiffviewer_la_LIBADD = ../../lib/libmc.la diff --git a/src/diffviewer/charset.c b/src/diffviewer/charset.c new file mode 100644 index 000000000..a4323f48b --- /dev/null +++ b/src/diffviewer/charset.c @@ -0,0 +1,105 @@ +/* + Search functions for diffviewer. + + Copyright (C) 2010-2014 + Free Software Foundation, Inc. + + Written by: + Slava Zanko , 2010. + Andrew Borodin , 2012 + + This file is part of the Midnight Commander. + + The Midnight Commander 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 3 of the License, + or (at your option) any later version. + + The Midnight Commander 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, see . + */ + +#include + +#include + +#include "lib/global.h" +#include "lib/strutil.h" +#include "lib/tty/key.h" +#include "lib/widget.h" +#include "lib/charsets.h" + +#include "src/history.h" +#include "src/selcodepage.h" + +#include "internal.h" +#include "charset.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +mc_diffviewer_set_codeset (WDiff * dview) +{ + const char *encoding_id = NULL; + + dview->utf8 = TRUE; + encoding_id = + get_codepage_id (mc_global.source_codepage >= + 0 ? mc_global.source_codepage : mc_global.display_codepage); + if (encoding_id != NULL) + { + GIConv conv; + + conv = str_crt_conv_from (encoding_id); + if (conv != INVALID_CONV) + { + if (dview->converter != str_cnv_from_term) + str_close_conv (dview->converter); + dview->converter = conv; + } + dview->utf8 = (gboolean) str_isutf8 (encoding_id); + } +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +gboolean +mc_diffviewer_cmd_select_encoding_show_dialog (const gchar * event_group_name, + const gchar * event_name, gpointer init_data, + gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + if (do_select_codepage ()) + mc_diffviewer_set_codeset (dview); + mc_diffviewer_reread (dview); + tty_touch_screen (); + repaint_screen (); + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/diffviewer/charset.h b/src/diffviewer/charset.h new file mode 100644 index 000000000..cf2f983a9 --- /dev/null +++ b/src/diffviewer/charset.h @@ -0,0 +1,22 @@ +#ifndef MC__DIFFVIEW_CHARSET_H +#define MC__DIFFVIEW_CHARSET_H + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +gboolean mc_diffviewer_cmd_select_encoding_show_dialog (const gchar * event_group_name, + const gchar * event_name, + gpointer init_data, gpointer data); + +void mc_diffviewer_set_codeset (WDiff * dview); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__DIFFVIEW_CHARSET_H */ diff --git a/src/diffviewer/events.c b/src/diffviewer/events.c new file mode 100644 index 000000000..f025b71c4 --- /dev/null +++ b/src/diffviewer/events.c @@ -0,0 +1,683 @@ +/* + Events diffviewer. + + Copyright (C) 2010-2014 + Free Software Foundation, Inc. + + Written by: + Slava Zanko , 2010. + Andrew Borodin , 2012 + + This file is part of the Midnight Commander. + + The Midnight Commander 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 3 of the License, + or (at your option) any later version. + + The Midnight Commander 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, see . + */ + +#include + +#include +#include + +#include "lib/global.h" +#include "lib/event.h" +#include "lib/strutil.h" +#include "lib/vfs/vfs.h" /* mc_opendir, mc_readdir, mc_closedir, */ +#include "lib/tty/key.h" +#include "lib/widget.h" + +#include "src/filemanager/cmd.h" /* edit_file_at_line(), view_other_cmd() */ +#include "src/history.h" +#include "src/setup.h" + +#include "internal.h" +#include "options.h" + +#ifdef HAVE_CHARSET +#include "charset.h" +#endif + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +typedef enum +{ + MC_DVIEW_SPLIT_EQUAL = 1, + MC_DVIEW_SPLIT_FULL, + MC_DVIEW_SPLIT_MORE, + MC_DVIEW_SPLIT_LESS, +} mc_diffviewer_split_type_t; + +/*** file scope type declarations ****************************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_show_symbols (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + dview->display_symbols ^= 1; + dview->new_frame = 1; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_show_numbers (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + dview->display_numbers ^= mc_diffviewer_calc_nwidth ((const GArray ** const) dview->a); + dview->new_frame = 1; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_split_views (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + mc_diffviewer_split_type_t split_type; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + if (init_data == NULL) + return TRUE; + + split_type = (mc_diffviewer_split_type_t) init_data; + + if (split_type == MC_DVIEW_SPLIT_FULL) + { + dview->full ^= 1; + dview->new_frame = 1; + } + else if (!dview->full) + { + switch (split_type) + { + case MC_DVIEW_SPLIT_EQUAL: + dview->bias = 0; + break; + case MC_DVIEW_SPLIT_MORE: + mc_diffviewer_compute_split (dview, 1); + break; + case MC_DVIEW_SPLIT_LESS: + mc_diffviewer_compute_split (dview, -1); + break; + default: + return TRUE; + } + dview->new_frame = 1; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_set_tab_size (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + + if (init_data != NULL) + dview->tab_size = (int) (intptr_t) init_data; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_swap (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + dview->ord ^= 1; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_redo (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + if (dview->display_numbers) + { + int old; + + old = dview->display_numbers; + dview->display_numbers = mc_diffviewer_calc_nwidth ((const GArray **) dview->a); + dview->new_frame = (old != dview->display_numbers); + } + mc_diffviewer_reread (dview); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_hunk_next (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + const GArray *a; + diff_place_t diff_place; + + (void) event_group_name; + (void) event_name; + + diff_place = (diff_place_t) init_data; + a = dview->a[diff_place]; + + while ((size_t) dview->skip_rows < a->len + && ((DIFFLN *) & g_array_index (a, DIFFLN, dview->skip_rows))->ch != EQU_CH) + dview->skip_rows++; + while ((size_t) dview->skip_rows < a->len + && ((DIFFLN *) & g_array_index (a, DIFFLN, dview->skip_rows))->ch == EQU_CH) + dview->skip_rows++; + + dview->search.last_accessed_num_line = dview->skip_rows; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_hunk_prev (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + const GArray *a; + diff_place_t diff_place; + + (void) event_group_name; + (void) event_name; + + diff_place = (diff_place_t) init_data; + a = dview->a[diff_place]; + + while (dview->skip_rows > 0 + && ((DIFFLN *) & g_array_index (a, DIFFLN, dview->skip_rows))->ch != EQU_CH) + dview->skip_rows--; + while (dview->skip_rows > 0 + && ((DIFFLN *) & g_array_index (a, DIFFLN, dview->skip_rows))->ch == EQU_CH) + dview->skip_rows--; + while (dview->skip_rows > 0 + && ((DIFFLN *) & g_array_index (a, DIFFLN, dview->skip_rows))->ch != EQU_CH) + dview->skip_rows--; + if (dview->skip_rows > 0 && (size_t) dview->skip_rows < a->len) + dview->skip_rows++; + + dview->search.last_accessed_num_line = dview->skip_rows; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_goto_line (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + diff_place_t ord = (diff_place_t) init_data; + /* *INDENT-OFF* */ + static const char *title[2] = { + N_("Goto line (left)"), + N_("Goto line (right)") + }; + /* *INDENT-ON* */ + static char prev[256]; + /* XXX some statics here, to be remembered between runs */ + + int newline; + char *input; + + (void) event_group_name; + (void) event_name; + + input = + input_dialog (_(title[ord]), _("Enter line:"), MC_HISTORY_YDIFF_GOTO_LINE, prev, + INPUT_COMPLETE_NONE); + if (input != NULL) + { + const char *s = input; + + if (mc_diffviewer_scan_deci (&s, &newline) == 0 && *s == '\0') + { + size_t i = 0; + + if (newline > 0) + { + for (; i < dview->a[ord]->len; i++) + { + const DIFFLN *p; + + p = &g_array_index (dview->a[ord], DIFFLN, i); + if (p->line == newline) + break; + } + } + dview->skip_rows = dview->search.last_accessed_num_line = (ssize_t) i; + g_snprintf (prev, sizeof (prev), "%d", newline); + } + g_free (input); + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_edit (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + diff_place_t diff_place = (diff_place_t) init_data; + + WDialog *h; + gboolean h_modal; + int linenum, lineofs; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + switch (diff_place) + { + case DIFF_CURRENT: + diff_place = dview->ord; + break; + case DIFF_OTHER: + diff_place = dview->ord ^ 1; + break; + case DIFF_LEFT: + case DIFF_RIGHT: + break; + default: + return FALSE; + } + + if (dview->dsrc == DATA_SRC_TMP) + { + error_dialog (_("Edit"), _("Edit is disabled")); + return TRUE; + } + + h = WIDGET (dview)->owner; + h_modal = h->modal; + + mc_diffviewer_get_line_numbers (dview->a[diff_place], dview->skip_rows, &linenum, &lineofs); + h->modal = TRUE; /* not allow edit file in several editors */ + { + vfs_path_t *tmp_vpath; + + tmp_vpath = vfs_path_from_str (dview->file[diff_place]); + edit_file_at_line (tmp_vpath, use_internal_edit != 0, linenum); + vfs_path_free (tmp_vpath); + } + h->modal = h_modal; + mc_event_raise (MCEVENT_GROUP_DIFFVIEWER, "redo", dview); + mc_diffviewer_update (dview); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_goto_top (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + dview->skip_rows = dview->search.last_accessed_num_line = 0; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_goto_bottom (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + dview->skip_rows = dview->search.last_accessed_num_line = dview->a[DIFF_LEFT]->len - 1; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_goto_up (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + if (dview->skip_rows > 0) + { + dview->skip_rows--; + dview->search.last_accessed_num_line = dview->skip_rows; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_goto_down (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + dview->skip_rows++; + dview->search.last_accessed_num_line = dview->skip_rows; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_goto_page_up (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + if (dview->height > 2) + { + dview->skip_rows -= dview->height - 2; + dview->search.last_accessed_num_line = dview->skip_rows; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_goto_page_down (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + if (dview->height > 2) + { + dview->skip_rows += dview->height - 2; + dview->search.last_accessed_num_line = dview->skip_rows; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_goto_left (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + dview->skip_cols -= (int) (intptr_t) init_data; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_goto_right (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + dview->skip_cols += (int) (intptr_t) init_data; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_goto_start_of_line (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + dview->skip_cols = 0; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_quit (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + dview->view_quit = 1; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +static gboolean +mc_diffviewer_cmd_save_changes (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + gboolean res; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + if (dview->merged[DIFF_LEFT]) + { + res = mc_util_unlink_backup_if_possible (dview->file[DIFF_LEFT], "~~~"); + dview->merged[DIFF_LEFT] = !res; + } + if (dview->merged[DIFF_RIGHT]) + { + res = mc_util_unlink_backup_if_possible (dview->file[DIFF_RIGHT], "~~~"); + dview->merged[DIFF_RIGHT] = !res; + } + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +mc_diffviewer_init_events (GError ** error) +{ + /* *INDENT-OFF* */ + event_init_group_t core_group_events[] = + { + {"run", mc_diffviewer_cmd_run, NULL}, + {"show_symbols", mc_diffviewer_cmd_show_symbols, NULL}, + {"show_numbers", mc_diffviewer_cmd_show_numbers, NULL}, + {"split_full", mc_diffviewer_cmd_split_views, (void *) MC_DVIEW_SPLIT_FULL}, + {"split_equal", mc_diffviewer_cmd_split_views, (void *) MC_DVIEW_SPLIT_EQUAL}, + {"split_more", mc_diffviewer_cmd_split_views, (void *) MC_DVIEW_SPLIT_MORE}, + {"split_less", mc_diffviewer_cmd_split_views, (void *) MC_DVIEW_SPLIT_LESS}, + {"tab_size_2", mc_diffviewer_cmd_set_tab_size, (void *) 2}, + {"tab_size_3", mc_diffviewer_cmd_set_tab_size, (void *) 3}, + {"tab_size_4", mc_diffviewer_cmd_set_tab_size, (void *) 4}, + {"tab_size_8", mc_diffviewer_cmd_set_tab_size, (void *) 8}, + {"swap", mc_diffviewer_cmd_swap, NULL}, + {"redo", mc_diffviewer_cmd_redo, NULL}, + {"hunk_next", mc_diffviewer_cmd_hunk_next, (void *) DIFF_LEFT}, + {"hunk_prev", mc_diffviewer_cmd_hunk_prev, (void *) DIFF_LEFT}, + {"goto_line", mc_diffviewer_cmd_goto_line, (void *) DIFF_RIGHT}, + + {"edit_current", mc_diffviewer_cmd_edit, (void *) DIFF_CURRENT}, + {"edit_other", mc_diffviewer_cmd_edit, (void *) DIFF_OTHER}, + {"edit_left", mc_diffviewer_cmd_edit, (void *) DIFF_LEFT}, + {"edit_right", mc_diffviewer_cmd_edit, (void *) DIFF_RIGHT}, + + {"merge_from_left_to_right", mc_diffviewer_cmd_merge, (void *) DIFF_LEFT}, + {"merge_from_right_to_left", mc_diffviewer_cmd_merge, (void *) DIFF_RIGHT}, + {"merge_from_current_to_other", mc_diffviewer_cmd_merge, (void *) DIFF_CURRENT}, + {"merge_from_other_to_current", mc_diffviewer_cmd_merge, (void *) DIFF_OTHER}, + {"search", mc_diffviewer_cmd_search, NULL}, + {"continue_search", mc_diffviewer_cmd_continue_search, NULL}, + {"goto_top", mc_diffviewer_cmd_goto_top, NULL}, + {"goto_bottom", mc_diffviewer_cmd_goto_bottom, NULL}, + {"goto_up", mc_diffviewer_cmd_goto_up, NULL}, + {"goto_down", mc_diffviewer_cmd_goto_down, NULL}, + {"goto_page_up", mc_diffviewer_cmd_goto_page_up, NULL}, + {"goto_page_down", mc_diffviewer_cmd_goto_page_down, NULL}, + {"goto_left", mc_diffviewer_cmd_goto_left, (void *) 1}, + {"goto_left_quick", mc_diffviewer_cmd_goto_left, (void *) 8}, + {"goto_right", mc_diffviewer_cmd_goto_right, (void *) 1}, + {"goto_right_quick", mc_diffviewer_cmd_goto_right, (void *) 8}, + {"goto_start_of_line", mc_diffviewer_cmd_goto_start_of_line, (void *) 8}, + {"quit", mc_diffviewer_cmd_quit, (void *) 8}, + {"save_changes", mc_diffviewer_cmd_save_changes, (void *) 8}, + + {"options_show_dialog", mc_diffviewer_cmd_options_show_dialog, NULL}, + {"options_save", mc_diffviewer_cmd_options_save, NULL}, + {"options_load", mc_diffviewer_cmd_options_load, NULL}, +#ifdef HAVE_CHARSET + {"select_encoding_show_dialog", mc_diffviewer_cmd_select_encoding_show_dialog, NULL}, +#endif /* HAVE_CHARSET */ + + {NULL, NULL, NULL} + }; + /* *INDENT-ON* */ + + /* *INDENT-OFF* */ + event_init_t standard_events[] = + { + {MCEVENT_GROUP_DIFFVIEWER, core_group_events}, + {NULL, NULL} + }; + /* *INDENT-ON* */ + + mc_event_mass_add (standard_events, error); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/diffviewer/execute.c b/src/diffviewer/execute.c new file mode 100644 index 000000000..99f6ecb43 --- /dev/null +++ b/src/diffviewer/execute.c @@ -0,0 +1,643 @@ +/* + Search functions for diffviewer. + + Copyright (C) 2010-2014 + Free Software Foundation, Inc. + + Written by: + Slava Zanko , 2010. + Andrew Borodin , 2012 + + This file is part of the Midnight Commander. + + The Midnight Commander 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 3 of the License, + or (at your option) any later version. + + The Midnight Commander 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, see . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "lib/global.h" +#include "lib/strescape.h" /* strutils_glob_escape() */ +#include "lib/vfs/vfs.h" /* mc_opendir, mc_readdir, mc_closedir, */ +#include "lib/widget.h" +#include "lib/util.h" + +#include "execute.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define FILE_READ_BUF 4096 +#define FILE_FLAG_TEMP (1 << 0) + +#define FILE_DIRTY(fs) \ +do \ +{ \ + (fs)->pos = 0; \ + (fs)->len = 0; \ +} \ +while (0) + +/*** file scope type declarations ****************************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/** + * 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); + g_free (fs->data); + } + g_free (fs->buf); + g_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"; + else if (flags == O_WRONLY) + type = "w"; + + if (type == NULL) + return NULL; + + fs = mc_diffviewer_file_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; +} + +/* --------------------------------------------------------------------------------------------- */ +/* diff parse *************************************************************** */ +/** + * 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, GArray * 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 (mc_diffviewer_scan_deci (&p, &f1) != 0 || f1 < 0) + return -1; + + f2 = f1; + range = 0; + if (*p == ',') + { + p++; + if (mc_diffviewer_scan_deci (&p, &f2) != 0 || f2 < f1) + return -1; + + range = 1; + } + + cmd = *p++; + if (cmd == 'a') + { + if (range != 0) + return -1; + } + else if (cmd != 'c' && cmd != 'd') + return -1; + + if (mc_diffviewer_scan_deci (&p, &t1) != 0 || t1 < 0) + return -1; + + t2 = t1; + range = 0; + if (*p == ',') + { + p++; + if (mc_diffviewer_scan_deci (&p, &t2) != 0 || t2 < t1) + return -1; + + range = 1; + } + + if (cmd == 'd' && range != 0) + 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; + g_array_append_val (ops, op); + 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, GArray * ops) +{ + int sz; + char buf[BUFSIZ]; + + while ((sz = mc_diffviewer_file_gets (buf, sizeof (buf) - 1, f)) != 0) + { + 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 = mc_diffviewer_file_gets (buf, sizeof (buf), f)) != 0) + ; + } + + return ops->len; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Close pipe stream. + * + * @param fs structure + * @return 0 on success, non-zero on error + */ + +static int +p_close (FBUF * fs) +{ + int rv = -1; + + if (fs != NULL) + { + rv = pclose (fs->data); + f_free (fs); + } + + return rv; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/** + * 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 + */ + +int +mc_diffviewer_execute (const char *args, const char *extra, const char *file1, const char *file2, + GArray * 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; + char *file1_esc, *file2_esc; + + /* escape potential $ to avoid shell variable substitutions in popen() */ + file1_esc = strutils_shell_escape (file1); + file2_esc = strutils_shell_escape (file2); + cmd = g_strdup_printf ("diff %s %s %s %s %s", args, extra, opt, file1_esc, file2_esc); + g_free (file1_esc); + g_free (file2_esc); + + if (cmd == NULL) + return -1; + + f = p_open (cmd, O_RDONLY); + g_free (cmd); + + if (f == NULL) + return -1; + + rv = scan_diff (f, ops); + code = p_close (f); + + if (rv < 0 || code == -1 || !WIFEXITED (code) || WEXITSTATUS (code) == 2) + rv = -1; + + return rv; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Close file. + * @note if this is temporary file, it is deleted + * + * @param fs file structure + * @return 0 on success, non-zero on error + */ + +int +mc_diffviewer_file_close (FBUF * fs) +{ + int rv = -1; + + if (fs != NULL) + { + rv = close (fs->fd); + f_free (fs); + } + + return rv; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Read decimal number from string. + * + * @param[in,out] str string to parse + * @param[out] n extracted number + * @return 0 if success, otherwise non-zero + */ + +int +mc_diffviewer_scan_deci (const char **str, int *n) +{ + const char *p = *str; + char *q; + + errno = 0; + *n = strtol (p, &q, 10); + if (errno != 0 || p == q) + return -1; + *str = q; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Seek into file. + * @note avoids thrashing read cache when possible + * + * @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 + */ + +off_t +mc_diffviewer_file_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; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Read a line of bytes from file until newline or EOF. + * @note does not stop on null-byte + * @note buf will not be null-terminated + * + * @param buf destination buffer + * @param size size of buffer + * @param fs file structure + * + * @return number of bytes read + */ + +size_t +mc_diffviewer_file_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; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Seek to the beginning of file, thrashing read cache. + * + * @param fs file structure + * + * @return 0 if success, non-zero on error + */ + +off_t +mc_diffviewer_file_reset (FBUF * fs) +{ + off_t rv; + + rv = lseek (fs->fd, 0, SEEK_SET); + if (rv != -1) + FILE_DIRTY (fs); + return rv; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * 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 + */ + +FBUF * +mc_diffviewer_file_open (const char *filename, int flags) +{ + int fd; + FBUF *fs; + + fs = mc_diffviewer_file_dopen (0); + if (fs == NULL) + return NULL; + + fd = open (filename, flags); + if (fd < 0) + { + f_free (fs); + return NULL; + } + + fs->fd = fd; + return fs; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Write bytes to file. + * @note thrashes read cache + * + * @param fs file structure + * @param buf source buffer + * @param size size of buffer + * + * @return number of written bytes, -1 on error + */ + +ssize_t +mc_diffviewer_file_write (FBUF * fs, const char *buf, size_t size) +{ + ssize_t rv; + + rv = write (fs->fd, buf, size); + if (rv >= 0) + FILE_DIRTY (fs); + return rv; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Truncate file to the current position. + * @note thrashes read cache + * + * @param fs file structure + * + * @return current file size on success, negative on error + */ + +off_t +mc_diffviewer_file_trunc (FBUF * fs) +{ + off_t off; + + off = lseek (fs->fd, 0, SEEK_CUR); + if (off != -1) + { + int rv; + + rv = ftruncate (fs->fd, off); + if (rv != 0) + off = -1; + else + FILE_DIRTY (fs); + } + return off; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Alocate file structure and associate file descriptor to it. + * + * @param fd file descriptor + * @return file structure + */ + +FBUF * +mc_diffviewer_file_dopen (int fd) +{ + FBUF *fs; + + if (fd < 0) + return NULL; + + fs = g_try_malloc (sizeof (FBUF)); + if (fs == NULL) + return NULL; + + fs->buf = g_try_malloc (FILE_READ_BUF); + if (fs->buf == NULL) + { + g_free (fs); + return NULL; + } + + fs->fd = fd; + FILE_DIRTY (fs); + fs->flags = 0; + fs->data = NULL; + + return fs; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Try to open a temporary file. + * @note the name is not altered if this function fails + * + * @param[out] name address of a pointer to store the temporary name + * @return file descriptor on success, negative on error + */ + +static int +open_temp (void **name) +{ + int fd; + vfs_path_t *diff_file_name = NULL; + + fd = mc_mkstemps (&diff_file_name, "mcdiff", NULL); + if (fd == -1) + { + message (D_ERROR, MSG_ERROR, + _("Cannot create temporary diff file\n%s"), unix_error_string (errno)); + return -1; + } + *name = g_strdup (vfs_path_as_str (diff_file_name)); + vfs_path_free (diff_file_name); + return fd; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Open a binary temporary file in R/W mode. + * @note the file will be deleted when closed + * + * @return file structure + */ + +FBUF * +mc_diffviewer_file_temp (void) +{ + int fd; + FBUF *fs; + + fs = mc_diffviewer_file_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; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/diffviewer/execute.h b/src/diffviewer/execute.h new file mode 100644 index 000000000..19e40b059 --- /dev/null +++ b/src/diffviewer/execute.h @@ -0,0 +1,45 @@ +#ifndef MC__DIFFVIEW_EXECUTE_H +#define MC__DIFFVIEW_EXECUTE_H + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +typedef struct +{ + int fd; + int pos; + int len; + char *buf; + int flags; + void *data; +} FBUF; + +typedef struct +{ + int a[2][2]; + int cmd; +} DIFFCMD; + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +int mc_diffviewer_scan_deci (const char **str, int *n); +int mc_diffviewer_file_close (FBUF * fs); +int mc_diffviewer_execute (const char *args, const char *extra, const char *file1, + const char *file2, GArray * ops); +off_t mc_diffviewer_file_seek (FBUF * fs, off_t off, int whence); +size_t mc_diffviewer_file_gets (char *buf, size_t size, FBUF * fs); +off_t mc_diffviewer_file_reset (FBUF * fs); +FBUF *mc_diffviewer_file_open (const char *filename, int flags); +ssize_t mc_diffviewer_file_write (FBUF * fs, const char *buf, size_t size); +off_t mc_diffviewer_file_trunc (FBUF * fs); +FBUF *mc_diffviewer_file_dopen (int fd); +FBUF *mc_diffviewer_file_temp (void); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__DIFFVIEW_EXECUTE_H */ diff --git a/src/diffviewer/internal.h b/src/diffviewer/internal.h index 41f0de5f5..2a3120991 100644 --- a/src/diffviewer/internal.h +++ b/src/diffviewer/internal.h @@ -7,6 +7,8 @@ #include "lib/tty/color.h" #include "lib/widget.h" +#include "execute.h" + /*** typedefs(not structures) and defined constants **********************************************/ typedef int (*DFUNC) (void *ctx, int ch, int line, off_t off, size_t sz, const char *str); @@ -14,6 +16,11 @@ typedef int PAIR[2]; #define error_dialog(h, s) query_dialog(h, s, D_ERROR, 1, _("&Dismiss")) +#define ADD_CH '+' +#define DEL_CH '-' +#define CHG_CH '*' +#define EQU_CH ' ' + /*** enums ***************************************************************************************/ typedef enum @@ -27,7 +34,9 @@ typedef enum { DIFF_LEFT = 0, DIFF_RIGHT = 1, - DIFF_COUNT = 2 + DIFF_COUNT = 2, + DIFF_CURRENT = 3, + DIFF_OTHER = 4 } diff_place_t; typedef enum @@ -40,23 +49,6 @@ typedef enum /*** structures declarations (and typedefs of structures)*****************************************/ -typedef struct -{ - int fd; - int pos; - int len; - char *buf; - int flags; - void *data; -} FBUF; - -typedef struct -{ - int a[2][2]; - int cmd; -} DIFFCMD; - - typedef struct { int off; @@ -146,8 +138,32 @@ typedef struct WDiff /*** declarations of public functions ************************************************************/ +void mc_diffviewer_init_events (GError ** error); + /* search.c */ -void dview_search_cmd (WDiff * dview); -void dview_continue_search_cmd (WDiff * dview); +gboolean mc_diffviewer_cmd_search (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data); +gboolean mc_diffviewer_cmd_continue_search (const gchar * event_group_name, + const gchar * event_name, gpointer init_data, + gpointer data); + + +int mc_diffviewer_calc_nwidth (const GArray ** const a); +void mc_diffviewer_compute_split (WDiff * dview, int i); +int mc_diffviewer_get_line_numbers (const GArray * a, size_t pos, int *linenum, int *lineofs); +void mc_diffviewer_reread (WDiff * dview); +void mc_diffviewer_update (WDiff * dview); + +void mc_diffviewer_destroy_hdiff (WDiff * dview); +void mc_diffviewer_deinit (WDiff * dview); + +int mc_diffviewer_redo_diff (WDiff * dview); +gboolean mc_diffviewer_cmd_merge (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data); + +gboolean mc_diffviewer_cmd_run (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data); + +/*** inline functions ****************************************************************************/ #endif /* MC__DIFFVIEW_INTERNAL_H */ diff --git a/src/diffviewer/options.c b/src/diffviewer/options.c new file mode 100644 index 000000000..b720a2fd2 --- /dev/null +++ b/src/diffviewer/options.c @@ -0,0 +1,183 @@ +/* + Search functions for diffviewer. + + Copyright (C) 2010-2014 + Free Software Foundation, Inc. + + Written by: + Slava Zanko , 2010. + Andrew Borodin , 2012 + + This file is part of the Midnight Commander. + + The Midnight Commander 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 3 of the License, + or (at your option) any later version. + + The Midnight Commander 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, see . + */ + +#include + +#include + +#include "lib/global.h" +#include "lib/strutil.h" +#include "lib/tty/key.h" +#include "lib/widget.h" + +#include "src/history.h" + +#include "internal.h" +#include "options.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +gboolean +mc_diffviewer_cmd_options_save (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + mc_config_set_bool (mc_main_config, "DiffView", "show_symbols", + dview->display_symbols != 0 ? TRUE : FALSE); + mc_config_set_bool (mc_main_config, "DiffView", "show_numbers", + dview->display_numbers != 0 ? TRUE : FALSE); + mc_config_set_int (mc_main_config, "DiffView", "tab_size", dview->tab_size); + + mc_config_set_int (mc_main_config, "DiffView", "diff_quality", dview->opt.quality); + + mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_tws", + dview->opt.strip_trailing_cr); + mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_all_space", + dview->opt.ignore_all_space); + mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_space_change", + dview->opt.ignore_space_change); + mc_config_set_bool (mc_main_config, "DiffView", "diff_tab_expansion", + dview->opt.ignore_tab_expansion); + mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_case", dview->opt.ignore_case); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +gboolean +mc_diffviewer_cmd_options_load (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + gboolean show_numbers, show_symbols; + int tab_size; + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + show_symbols = mc_config_get_bool (mc_main_config, "DiffView", "show_symbols", FALSE); + if (show_symbols) + dview->display_symbols = 1; + show_numbers = mc_config_get_bool (mc_main_config, "DiffView", "show_numbers", FALSE); + if (show_numbers) + dview->display_numbers = mc_diffviewer_calc_nwidth ((const GArray ** const) dview->a); + tab_size = mc_config_get_int (mc_main_config, "DiffView", "tab_size", 8); + if (tab_size > 0 && tab_size < 9) + dview->tab_size = tab_size; + else + dview->tab_size = 8; + + dview->opt.quality = mc_config_get_int (mc_main_config, "DiffView", "diff_quality", 0); + + dview->opt.strip_trailing_cr = + mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_tws", FALSE); + dview->opt.ignore_all_space = + mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_all_space", FALSE); + dview->opt.ignore_space_change = + mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_space_change", FALSE); + dview->opt.ignore_tab_expansion = + mc_config_get_bool (mc_main_config, "DiffView", "diff_tab_expansion", FALSE); + dview->opt.ignore_case = + mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_case", FALSE); + + dview->new_frame = 1; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +gboolean +mc_diffviewer_cmd_options_show_dialog (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + + const char *quality_str[] = { + N_("No&rmal"), + N_("&Fastest (Assume large files)"), + N_("&Minimal (Find a smaller set of change)") + }; + + quick_widget_t quick_widgets[] = { + /* *INDENT-OFF* */ + QUICK_START_GROUPBOX (N_("Diff algorithm")), + QUICK_RADIO (3, (const char **) quality_str, (int *) &dview->opt.quality, NULL), + QUICK_STOP_GROUPBOX, + QUICK_START_GROUPBOX (N_("Diff extra options")), + QUICK_CHECKBOX (N_("&Ignore case"), &dview->opt.ignore_case, NULL), + QUICK_CHECKBOX (N_("Ignore tab &expansion"), &dview->opt.ignore_tab_expansion, NULL), + QUICK_CHECKBOX (N_("Ignore &space change"), &dview->opt.ignore_space_change, NULL), + QUICK_CHECKBOX (N_("Ignore all &whitespace"), &dview->opt.ignore_all_space, NULL), + QUICK_CHECKBOX (N_("Strip &trailing carriage return"), &dview->opt.strip_trailing_cr, + NULL), + QUICK_STOP_GROUPBOX, + QUICK_BUTTONS_OK_CANCEL, + QUICK_END + /* *INDENT-ON* */ + }; + + quick_dialog_t qdlg = { + -1, -1, 56, + N_("Diff Options"), "[Diff Options]", + quick_widgets, NULL, NULL + }; + + (void) event_group_name; + (void) event_name; + (void) init_data; + + if (quick_dialog (&qdlg) != B_CANCEL) + mc_diffviewer_reread (dview); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/diffviewer/options.h b/src/diffviewer/options.h new file mode 100644 index 000000000..3e68ef545 --- /dev/null +++ b/src/diffviewer/options.h @@ -0,0 +1,26 @@ +#ifndef MC__DIFFVIEW_OPTIONS_H +#define MC__DIFFVIEW_OPTIONS_H + +/*** typedefs(not structures) and defined constants **********************************************/ + +/*** enums ***************************************************************************************/ + +/*** structures declarations (and typedefs of structures)*****************************************/ + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +gboolean mc_diffviewer_cmd_options_save (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data); + +gboolean mc_diffviewer_cmd_options_load (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data); + +gboolean mc_diffviewer_cmd_options_show_dialog (const gchar * event_group_name, + const gchar * event_name, gpointer init_data, + gpointer data); + +/*** inline functions ****************************************************************************/ + +#endif /* MC__DIFFVIEW_OPTIONS_H */ diff --git a/src/diffviewer/search.c b/src/diffviewer/search.c index 81c826880..1afbaa75b 100644 --- a/src/diffviewer/search.c +++ b/src/diffviewer/search.c @@ -236,18 +236,26 @@ mcdiffview_do_search (WDiff * dview) /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ +/* event callback */ -void -dview_search_cmd (WDiff * dview) +gboolean +mc_diffviewer_cmd_search (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) { + WDiff *dview = (WDiff *) data; + + (void) event_group_name; + (void) event_name; + (void) init_data; + if (dview->dsrc != DATA_SRC_MEM) { error_dialog (_("Search"), _("Search is disabled")); - return; + return FALSE; } if (!mcdiffview_dialog_search (dview)) - return; + return TRUE; mc_search_free (dview->search.handle); #ifdef HAVE_CHARSET @@ -257,7 +265,7 @@ dview_search_cmd (WDiff * dview) #endif if (dview->search.handle == NULL) - return; + return TRUE; dview->search.handle->search_type = mcdiffview_search_options.type; #ifdef HAVE_CHARSET @@ -267,19 +275,27 @@ dview_search_cmd (WDiff * dview) dview->search.handle->whole_words = mcdiffview_search_options.whole_words; mcdiffview_do_search (dview); + + return TRUE; } /* --------------------------------------------------------------------------------------------- */ +/* event callback */ -void -dview_continue_search_cmd (WDiff * dview) +gboolean +mc_diffviewer_cmd_continue_search (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) { + WDiff *dview = (WDiff *) data; + if (dview->dsrc != DATA_SRC_MEM) error_dialog (_("Search"), _("Search is disabled")); else if (dview->search.handle == NULL) - dview_search_cmd (dview); + return mc_diffviewer_cmd_search (event_group_name, event_name, init_data, data); else mcdiffview_do_search (dview); + + return TRUE; } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/diffviewer/tools.c b/src/diffviewer/tools.c new file mode 100644 index 000000000..ee4b5d96a --- /dev/null +++ b/src/diffviewer/tools.c @@ -0,0 +1,2002 @@ +/* + Search functions for diffviewer. + + Copyright (C) 2010-2015 + Free Software Foundation, Inc. + + Written by: + Slava Zanko , 2010, 2015. + Andrew Borodin , 2012, 2015 + + This file is part of the Midnight Commander. + + The Midnight Commander 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 3 of the License, + or (at your option) any later version. + + The Midnight Commander 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, see . + */ + +#include + +#include +#include + +#include "lib/global.h" +#include "lib/event.h" +#ifdef HAVE_CHARSET +#include "lib/charsets.h" +#endif +#include "lib/skin.h" /* EDITOR_NORMAL_COLOR */ +#include "lib/strutil.h" +#include "lib/tty/tty.h" +#include "lib/tty/color.h" +#include "lib/util.h" + +#include "internal.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define g_array_foreach(a, TP, cbf) \ +do { \ + size_t g_array_foreach_i;\ + \ + for (g_array_foreach_i = 0; g_array_foreach_i < a->len; g_array_foreach_i++) \ + { \ + TP *g_array_foreach_var; \ + \ + g_array_foreach_var = &g_array_index (a, TP, g_array_foreach_i); \ + (*cbf) (g_array_foreach_var); \ + } \ +} while (0) + +#define HDIFF_ENABLE 1 +#define HDIFF_MINCTX 5 +#define HDIFF_DEPTH 10 + +/*** file scope type declarations ****************************************************************/ + +/*** file scope variables ************************************************************************/ + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static inline int +TAB_SKIP (int ts, int pos) +{ + if (ts > 0 && ts < 9) + return ts - pos % ts; + else + return 8 - pos % 8; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +get_digits (unsigned int n) +{ + int d = 1; + + while (n /= 10) + d++; + return d; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +cc_free_elt (void *elt) +{ + DIFFLN *p = elt; + + if (p != NULL) + g_free (p->p); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +dview_status (const WDiff * dview, diff_place_t ord, int width, int c) +{ + const char *buf; + int filename_width; + int linenum, lineofs; + vfs_path_t *vpath; + char *path; + + tty_setcolor (STATUSBAR_COLOR); + + tty_gotoyx (0, c); + mc_diffviewer_get_line_numbers (dview->a[ord], dview->skip_rows, &linenum, &lineofs); + + filename_width = width - 24; + if (filename_width < 8) + filename_width = 8; + + vpath = vfs_path_from_str (dview->label[ord]); + path = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD); + vfs_path_free (vpath); + buf = str_term_trim (path, filename_width); + if (ord == DIFF_LEFT) + tty_printf ("%s%-*s %6d+%-4d Col %-4d ", dview->merged[ord] ? "* " : " ", filename_width, + buf, linenum, lineofs, dview->skip_cols); + else + tty_printf ("%s%-*s %6d+%-4d Dif %-4d ", dview->merged[ord] ? "* " : " ", filename_width, + buf, linenum, lineofs, dview->ndiff); + g_free (path); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Check if character is inside horizontal diff limits. + * + * @param k rank of character inside line + * @param hdiff horizontal diff structure + * @param ord DIFF_LEFT if reading from first file, DIFF_RIGHT if reading from 2nd file + * + * @return TRUE if inside hdiff limits, FALSE otherwise + */ + +static gboolean +is_inside (int k, GArray * hdiff, diff_place_t ord) +{ + size_t i; + BRACKET *b; + + for (i = 0; i < hdiff->len; i++) + { + int start, end; + + b = &g_array_index (hdiff, BRACKET, i); + start = (*b)[ord].off; + end = start + (*b)[ord].len; + if (k >= start && k < end) + return TRUE; + } + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ +#ifdef HAVE_CHARSET +/** + * Get utf multibyte char from string + * + * @param char * str, int * char_length, gboolean * result + * @return int as utf character or 0 and result == FALSE if fail + */ + +static int +dview_get_utf (char *str, int *char_length, gboolean * result) +{ + int res = -1; + gunichar ch; + int ch_len = 0; + + *result = TRUE; + + if (str == NULL) + { + *result = FALSE; + return 0; + } + + res = g_utf8_get_char_validated (str, -1); + + if (res < 0) + ch = *str; + else + { + gchar *next_ch; + ch = res; + + /* Calculate UTF-8 char length */ + next_ch = g_utf8_next_char (str); + ch_len = next_ch - str; + } + *char_length = ch_len; + + return ch; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +dview_str_utf8_offset_to_pos (const char *text, size_t length) +{ + ptrdiff_t result; + + if (text == NULL || text[0] == '\0') + return length; + + if (g_utf8_validate (text, -1, NULL)) + result = g_utf8_offset_to_pointer (text, length) - text; + else + { + gunichar uni; + char *tmpbuf, *buffer; + + buffer = tmpbuf = g_strdup (text); + while (tmpbuf[0] != '\0') + { + uni = g_utf8_get_char_validated (tmpbuf, -1); + if ((uni != (gunichar) (-1)) && (uni != (gunichar) (-2))) + tmpbuf = g_utf8_next_char (tmpbuf); + else + { + tmpbuf[0] = '.'; + tmpbuf++; + } + } + result = g_utf8_offset_to_pointer (tmpbuf, length) - tmpbuf; + g_free (buffer); + } + return max (length, (size_t) result); +} +#endif /*HAVE_CHARSET */ + +/* --------------------------------------------------------------------------------------------- */ +/** + * 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 != 0 && srcsize != 0 && *src != '\n'; i++, src++, srcsize--) + { + if (*src == '\t') + { + int j; + + j = TAB_SKIP (ts, i + base); + i += j - 1; + while (j-- > 0) + { + if (skip > 0) + skip--; + else if (dstsize != 0) + { + dstsize--; + *dst++ = ' '; + } + } + } + else if (src[0] == '\r' && (srcsize == 1 || src[1] == '\n')) + { + if (skip == 0 && show_cr) + { + if (dstsize > 1) + { + dstsize -= 2; + *dst++ = '^'; + *dst++ = 'M'; + } + else + { + dstsize--; + *dst++ = '.'; + } + } + break; + } + else if (skip > 0) + { +#ifdef HAVE_CHARSET + gboolean res; + int ch_len = 1; + + (void) dview_get_utf ((char *) src, &ch_len, &res); + + if (ch_len > 1) + skip += ch_len - 1; +#endif + skip--; + } + else + { + dstsize--; + *dst++ = *src; + } + } + sz = dst - tmp; + } + while (dstsize != 0) + { + 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 DIFF_LEFT if reading from first file, DIFF_RIGHT 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, + GArray * hdiff, diff_place_t 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 != 0 && srcsize != 0 && *src != '\n'; i++, k++, src++, srcsize--) + { + if (*src == '\t') + { + int j; + + j = TAB_SKIP (ts, i + base); + i += j - 1; + while (j-- > 0) + { + if (skip != 0) + skip--; + else if (dstsize != 0) + { + dstsize--; + *att++ = is_inside (k, hdiff, ord); + *dst++ = ' '; + } + } + } + else if (src[0] == '\r' && (srcsize == 1 || src[1] == '\n')) + { + if (skip == 0 && 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 != 0) + { +#ifdef HAVE_CHARSET + gboolean res; + int ch_len = 1; + + (void) dview_get_utf ((char *) src, &ch_len, &res); + if (ch_len > 1) + skip += ch_len - 1; +#endif + skip--; + } + else + { + dstsize--; + *att++ = is_inside (k, hdiff, ord); + *dst++ = *src; + } + } + sz = dst - tmp; + } + while (dstsize != 0) + { + dstsize--; + *att++ = '\0'; + *dst++ = ' '; + } + *dst = '\0'; + return sz; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Get one char (byte) from string + * + * @param char * str, gboolean * result + * @return int as character or 0 and result == FALSE if fail + */ + +static int +dview_get_byte (char *str, gboolean * result) +{ + if (str == NULL) + { + *result = FALSE; + return 0; + } + *result = TRUE; + return (unsigned char) *str; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Copy 'src' to 'dst' expanding tabs. + * @note The procedure returns when all bytes are consumed from 'src' + * + * @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 + */ + +static int +cvt_cpy (char *dst, const char *src, size_t srcsize, int base, int ts) +{ + int i; + + for (i = 0; srcsize != 0; i++, src++, dst++, srcsize--) + { + *dst = *src; + if (*src == '\t') + { + int j; + + 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 != 0; i++, src++, dst++, srcsize--) + { + *dst = *src; + if (*src == '\t') + { + int j; + + 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 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, size_t dstsize, int skip, int ts, int show_cr) +{ + int base = 0; + int old_base = base; + size_t amount = dstsize; + size_t useful, offset; + size_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 (sizeof (tmp) < amount || sizeof (tmp) <= dstsize || sizeof (cvt) < 8 * amount) + { + /* abnormal, but avoid buffer overflow */ + memset (dst, ' ', dstsize); + dst[dstsize] = '\0'; + return 0; + } + + mc_diffviewer_file_seek (f, off, SEEK_SET); + + while (skip > base) + { + old_base = base; + sz = mc_diffviewer_file_gets (tmp, amount, f); + if (sz == 0) + 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; + } + } + + if (base < skip) + { + memset (dst, ' ', dstsize); + dst[dstsize] = '\0'; + return 0; + } + + useful = base - skip; + offset = skip - old_base; + + if (useful <= dstsize) + { + if (useful != 0) + memmove (dst, cvt + offset, useful); + + if (q == NULL) + { + sz = mc_diffviewer_file_gets (tmp, dstsize - useful + 1, f); + if (sz != 0) + { + 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; + } + } + + for (; i < dstsize; i++) + dst[i] = ' '; + dst[i] = '\0'; + return sz; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +dview_display_file (const WDiff * dview, diff_place_t ord, int r, int c, int height, int width) +{ + size_t i, k; + int j; + char buf[BUFSIZ]; + FBUF *f = dview->f[ord]; + int skip = dview->skip_cols; + int display_symbols = dview->display_symbols; + int display_numbers = dview->display_numbers; + int show_cr = dview->show_cr; + int tab_size = 8; + const DIFFLN *p; + int nwidth = display_numbers; + int xwidth; + + xwidth = display_symbols + display_numbers; + if (dview->tab_size > 0 && dview->tab_size < 9) + tab_size = dview->tab_size; + + if (xwidth != 0) + { + 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 = dview->skip_rows, j = 0; i < dview->a[ord]->len && j < height; j++, i++) + { + int ch, next_ch, col; + size_t cnt; + + p = (DIFFLN *) & g_array_index (dview->a[ord], DIFFLN, i); + ch = p->ch; + tty_setcolor (NORMAL_COLOR); + if (display_symbols) + { + tty_gotoyx (r + j, c - 2); + tty_print_char (ch); + } + if (p->line != 0) + { + if (display_numbers) + { + tty_gotoyx (r + j, c - xwidth); + g_snprintf (buf, display_numbers + 1, "%*d", nwidth, p->line); + tty_print_string (str_fit_to_term (buf, nwidth, J_LEFT_FIT)); + } + if (ch == ADD_CH) + tty_setcolor (DFF_ADD_COLOR); + if (ch == CHG_CH) + tty_setcolor (DFF_CHG_COLOR); + if (f == NULL) + { + if (i == (size_t) dview->search.last_found_line) + tty_setcolor (MARKED_SELECTED_COLOR); + else if (dview->hdiff != NULL && g_ptr_array_index (dview->hdiff, i) != NULL) + { + char att[BUFSIZ]; + +#ifdef HAVE_CHARSET + if (dview->utf8) + k = dview_str_utf8_offset_to_pos (p->p, width); + else +#endif /* HAVE_CHARSET */ + k = width; + + cvt_mgeta (p->p, p->u.len, buf, k, skip, tab_size, show_cr, + g_ptr_array_index (dview->hdiff, i), ord, att); + tty_gotoyx (r + j, c); + col = 0; + + for (cnt = 0; cnt < strlen (buf) && col < width; cnt++) + { + gboolean ch_res; + +#ifdef HAVE_CHARSET + if (dview->utf8) + { + int ch_len; + + next_ch = dview_get_utf (buf + cnt, &ch_len, &ch_res); + if (ch_len > 1) + cnt += ch_len - 1; + + if (!g_unichar_isprint (next_ch)) + next_ch = '.'; + } + else +#endif /* HAVE_CHARSET */ + next_ch = dview_get_byte (buf + cnt, &ch_res); + + if (ch_res) + { + tty_setcolor (att[cnt] ? DFF_CHH_COLOR : DFF_CHG_COLOR); +#ifdef HAVE_CHARSET + if (mc_global.utf8_display) + { + if (!dview->utf8) + { + next_ch = + convert_from_8bit_to_utf_c ((unsigned char) next_ch, + dview->converter); + } + } + else if (dview->utf8) + next_ch = convert_from_utf_to_current_c (next_ch, dview->converter); + else + next_ch = convert_to_display_c (next_ch); +#endif /* HAVE_CHARSET */ + tty_print_anychar (next_ch); + col++; + } + } + continue; + } + + if (ch == CHG_CH) + tty_setcolor (DFF_CHH_COLOR); + +#ifdef HAVE_CHARSET + if (dview->utf8) + k = dview_str_utf8_offset_to_pos (p->p, width); + else +#endif /* HAVE_CHARSET */ + k = width; + cvt_mget (p->p, p->u.len, buf, k, skip, tab_size, show_cr); + } + else + cvt_fget (f, p->u.off, buf, width, skip, tab_size, show_cr); + } + else + { + if (display_numbers) + { + tty_gotoyx (r + j, c - xwidth); + memset (buf, ' ', display_numbers); + buf[display_numbers] = '\0'; + tty_print_string (buf); + } + if (ch == DEL_CH) + tty_setcolor (DFF_DEL_COLOR); + if (ch == CHG_CH) + tty_setcolor (DFF_CHD_COLOR); + memset (buf, ' ', width); + buf[width] = '\0'; + } + tty_gotoyx (r + j, c); + /* tty_print_nstring (buf, width); */ + col = 0; + for (cnt = 0; cnt < strlen (buf) && col < width; cnt++) + { + gboolean ch_res; + +#ifdef HAVE_CHARSET + if (dview->utf8) + { + int ch_len; + + next_ch = dview_get_utf (buf + cnt, &ch_len, &ch_res); + if (ch_len > 1) + cnt += ch_len - 1; + if (!g_unichar_isprint (next_ch)) + next_ch = '.'; + } + else +#endif /* HAVE_CHARSET */ + next_ch = dview_get_byte (buf + cnt, &ch_res); + if (ch_res) + { +#ifdef HAVE_CHARSET + if (mc_global.utf8_display) + { + if (!dview->utf8) + { + next_ch = + convert_from_8bit_to_utf_c ((unsigned char) next_ch, dview->converter); + } + } + else if (dview->utf8) + next_ch = convert_from_utf_to_current_c (next_ch, dview->converter); + else + next_ch = convert_to_display_c (next_ch); +#endif + + tty_print_anychar (next_ch); + col++; + } + } + } + tty_setcolor (NORMAL_COLOR); + k = width; + if (width < xwidth - 1) + k = xwidth - 1; + memset (buf, ' ', k); + buf[k] = '\0'; + for (; j < height; j++) + { + if (xwidth != 0) + { + tty_gotoyx (r + j, c - xwidth); + /* tty_print_nstring (buf, xwidth - 1); */ + tty_print_string (str_fit_to_term (buf, xwidth - 1, J_LEFT_FIT)); + } + tty_gotoyx (r + j, c); + /* tty_print_nstring (buf, width); */ + tty_print_string (str_fit_to_term (buf, width, J_LEFT_FIT)); + } + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Reparse and display file according to diff statements. + * + * @param ord DIFF_LEFT if 1nd file is displayed , DIFF_RIGHT if 2nd file is displayed. + * @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 (diff_place_t ord, const char *filename, const GArray * ops, DFUNC printer, void *ctx) +{ + size_t i; + FBUF *f; + size_t sz; + char buf[BUFSIZ]; + int line = 0; + off_t off = 0; + const DIFFCMD *op; + diff_place_t eff; + int add_cmd; + int del_cmd; + + f = mc_diffviewer_file_open (filename, O_RDONLY); + if (f == NULL) + return -1; + + ord &= 1; + eff = ord; + + add_cmd = 'a'; + del_cmd = 'd'; + if (ord != 0) + { + add_cmd = 'd'; + del_cmd = 'a'; + } +#define F1 a[eff][0] +#define F2 a[eff][1] +#define T1 a[ ord^1 ][0] +#define T2 a[ ord^1 ][1] + for (i = 0; i < ops->len; i++) + { + int n; + + op = &g_array_index (ops, DIFFCMD, i); + n = op->F1 - (op->cmd != add_cmd); + + while (line < n && (sz = mc_diffviewer_file_gets (buf, sizeof (buf), f)) != 0) + { + line++; + printer (ctx, EQU_CH, line, off, sz, buf); + off += sz; + while (buf[sz - 1] != '\n') + { + sz = mc_diffviewer_file_gets (buf, sizeof (buf), f); + if (sz == 0) + { + 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 != 0) + { + printer (ctx, DEL_CH, 0, 0, 1, "\n"); + n--; + } + } + + if (op->cmd == del_cmd) + { + n = op->F2 - op->F1 + 1; + while (n != 0 && (sz = mc_diffviewer_file_gets (buf, sizeof (buf), f)) != 0) + { + line++; + printer (ctx, ADD_CH, line, off, sz, buf); + off += sz; + while (buf[sz - 1] != '\n') + { + sz = mc_diffviewer_file_gets (buf, sizeof (buf), f); + if (sz == 0) + { + printer (ctx, 0, 0, 0, 1, "\n"); + break; + } + printer (ctx, 0, 0, 0, sz, buf); + off += sz; + } + n--; + } + + if (n != 0) + goto err; + } + + if (op->cmd == 'c') + { + n = op->F2 - op->F1 + 1; + while (n != 0 && (sz = mc_diffviewer_file_gets (buf, sizeof (buf), f)) != 0) + { + line++; + printer (ctx, CHG_CH, line, off, sz, buf); + off += sz; + while (buf[sz - 1] != '\n') + { + sz = mc_diffviewer_file_gets (buf, sizeof (buf), f); + if (sz == 0) + { + printer (ctx, 0, 0, 0, 1, "\n"); + break; + } + printer (ctx, 0, 0, 0, sz, buf); + off += sz; + } + n--; + } + + if (n != 0) + 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 = mc_diffviewer_file_gets (buf, sizeof (buf), f)) != 0) + { + line++; + printer (ctx, EQU_CH, line, off, sz, buf); + off += sz; + while (buf[sz - 1] != '\n') + { + sz = mc_diffviewer_file_gets (buf, sizeof (buf), f); + if (sz == 0) + { + printer (ctx, 0, 0, 0, 1, "\n"); + break; + } + printer (ctx, 0, 0, 0, sz, buf); + off += sz; + } + } + + mc_diffviewer_file_close (f); + return 0; + + err: + mc_diffviewer_file_close (f); + return -1; +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +printer (void *ctx, int ch, int line, off_t off, size_t sz, const char *str) +{ + GArray *a = ((PRINTER_CTX *) ctx)->a; + DSRC dsrc = ((PRINTER_CTX *) ctx)->dsrc; + + if (ch != 0) + { + DIFFLN p; + + p.p = NULL; + p.ch = ch; + p.line = line; + p.u.off = off; + if (dsrc == DATA_SRC_MEM && line != 0) + { + if (sz != 0 && str[sz - 1] == '\n') + sz--; + if (sz > 0) + p.p = g_strndup (str, sz); + p.u.len = sz; + } + g_array_append_val (a, p); + } + else if (dsrc == DATA_SRC_MEM) + { + DIFFLN *p; + + p = &g_array_index (a, DIFFLN, a->len - 1); + if (sz != 0 && str[sz - 1] == '\n') + sz--; + if (sz != 0) + { + size_t new_size; + char *q; + + new_size = p->u.len + sz; + q = g_realloc (p->p, new_size); + memcpy (q + p->u.len, str, sz); + p->p = q; + } + p->u.len += sz; + } + if (dsrc == DATA_SRC_TMP && (line != 0 || ch == 0)) + { + FBUF *f = ((PRINTER_CTX *) ctx)->f; + mc_diffviewer_file_write (f, str, sz); + } + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * 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, GArray * ret, int min) +{ + int i, j; + int *Lprev, *Lcurr; + int z = 0; + + if (m < min || n < min) + { + /* XXX early culling */ + return 0; + } + + Lprev = g_try_new0 (int, n + 1); + if (Lprev == NULL) + return -1; + + Lcurr = g_try_new0 (int, n + 1); + if (Lcurr == NULL) + { + g_free (Lprev); + return -1; + } + + for (i = 0; i < m; i++) + { + int *L; + + 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; + + v = Lprev[j] + 1; + Lcurr[j + 1] = v; + if (z < v) + { + z = v; + g_array_set_size (ret, 0); + } + if (z == v && z >= min) + { + int off0, off1; + size_t k; + + off0 = i - z + 1; + off1 = j - z + 1; + + for (k = 0; k < ret->len; k++) + { + PAIR *p = (PAIR *) g_array_index (ret, PAIR, k); + if ((*p)[0] == off0 || (*p)[1] >= off1) + break; + } + if (k == ret->len) + { + PAIR p2; + + p2[0] = off0; + p2[1] = off1; + g_array_append_val (ret, p2); + } + } + } + } + } + + g_free (Lcurr); + g_free (Lprev); + return z; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * 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 gboolean +hdiff_multi (const char *s, const char *t, const BRACKET bracket, int min, GArray * hdiff, + unsigned int depth) +{ + BRACKET p; + + if (depth-- != 0) + { + GArray *ret; + BRACKET b; + int len; + + ret = g_array_new (FALSE, TRUE, sizeof (PAIR)); + if (ret == NULL) + return FALSE; + + len = lcsubstr (s + bracket[DIFF_LEFT].off, bracket[DIFF_LEFT].len, + t + bracket[DIFF_RIGHT].off, bracket[DIFF_RIGHT].len, ret, min); + if (ret->len != 0) + { + size_t k = 0; + const PAIR *data = (const PAIR *) &g_array_index (ret, PAIR, 0); + const PAIR *data2; + + b[DIFF_LEFT].off = bracket[DIFF_LEFT].off; + b[DIFF_LEFT].len = (*data)[0]; + b[DIFF_RIGHT].off = bracket[DIFF_RIGHT].off; + b[DIFF_RIGHT].len = (*data)[1]; + if (!hdiff_multi (s, t, b, min, hdiff, depth)) + return FALSE; + + for (k = 0; k < ret->len - 1; k++) + { + data = (const PAIR *) &g_array_index (ret, PAIR, k); + data2 = (const PAIR *) &g_array_index (ret, PAIR, k + 1); + b[DIFF_LEFT].off = bracket[DIFF_LEFT].off + (*data)[0] + len; + b[DIFF_LEFT].len = (*data2)[0] - (*data)[0] - len; + b[DIFF_RIGHT].off = bracket[DIFF_RIGHT].off + (*data)[1] + len; + b[DIFF_RIGHT].len = (*data2)[1] - (*data)[1] - len; + if (!hdiff_multi (s, t, b, min, hdiff, depth)) + return FALSE; + } + data = (const PAIR *) &g_array_index (ret, PAIR, k); + b[DIFF_LEFT].off = bracket[DIFF_LEFT].off + (*data)[0] + len; + b[DIFF_LEFT].len = bracket[DIFF_LEFT].len - (*data)[0] - len; + b[DIFF_RIGHT].off = bracket[DIFF_RIGHT].off + (*data)[1] + len; + b[DIFF_RIGHT].len = bracket[DIFF_RIGHT].len - (*data)[1] - len; + if (!hdiff_multi (s, t, b, min, hdiff, depth)) + return FALSE; + + g_array_free (ret, TRUE); + return TRUE; + } + } + + p[DIFF_LEFT].off = bracket[DIFF_LEFT].off; + p[DIFF_LEFT].len = bracket[DIFF_LEFT].len; + p[DIFF_RIGHT].off = bracket[DIFF_RIGHT].off; + p[DIFF_RIGHT].len = bracket[DIFF_RIGHT].len; + g_array_append_val (hdiff, p); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * 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 gboolean +hdiff_scan (const char *s, int m, const char *t, int n, int min, GArray * 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[DIFF_LEFT].off = i; + b[DIFF_LEFT].len = m - i; + b[DIFF_RIGHT].off = i; + b[DIFF_RIGHT].len = n - i; + + /* smartscan (multiple horizontal diff) */ + return hdiff_multi (s, t, b, min, hdiff, depth); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +rewrite_backup_content (const vfs_path_t * from_file_name_vpath, const char *to_file_name) +{ + FILE *backup_fd; + char *contents; + gsize length; + const char *from_file_name; + + from_file_name = vfs_path_get_by_index (from_file_name_vpath, -1)->path; + if (!g_file_get_contents (from_file_name, &contents, &length, NULL)) + return FALSE; + + backup_fd = fopen (to_file_name, "w"); + if (backup_fd == NULL) + { + g_free (contents); + return FALSE; + } + + length = fwrite ((const void *) contents, length, 1, backup_fd); + + fflush (backup_fd); + fclose (backup_fd); + g_free (contents); + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Add hunk to file. + * + * @param dview WDiff widget + * @param merge_file file stream for writing data + * @param from1 first line of source hunk + * @param from2 first line of destination hunk + * @param to1 last line of source hunk + * @param merge_direction in what direction files should be merged + */ + +static void +dview_add_hunk (WDiff * dview, FILE * merge_file, int from1, int from2, int to2, + diff_place_t source_diff, diff_place_t dest_diff) +{ + int line; + char buf[BUF_10K]; + FILE *f0; + FILE *f1; + + f0 = fopen (dview->file[source_diff], "r"); + f1 = fopen (dview->file[dest_diff], "r"); + + line = 0; + while (fgets (buf, sizeof (buf), f0) != NULL && line < from1 - 1) + { + line++; + fputs (buf, merge_file); + } + line = 0; + while (fgets (buf, sizeof (buf), f1) != NULL && line <= to2) + { + line++; + if (line >= from2) + fputs (buf, merge_file); + } + while (fgets (buf, sizeof (buf), f0) != NULL) + fputs (buf, merge_file); + + fclose (f0); + fclose (f1); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Remove hunk from file. + * + * @param dview WDiff widget + * @param merge_file file stream for writing data + * @param from1 first line of hunk + * @param to1 last line of hunk + * @param merge_direction in what direction files should be merged + */ + +static void +dview_remove_hunk (WDiff * dview, FILE * merge_file, int from1, int to1, diff_place_t diff_index) +{ + int line; + char buf[BUF_10K]; + FILE *f0; + + f0 = fopen (dview->file[diff_index], "r"); + + line = 0; + while (fgets (buf, sizeof (buf), f0) != NULL && line < from1 - 1) + { + line++; + fputs (buf, merge_file); + } + while (fgets (buf, sizeof (buf), f0) != NULL) + { + line++; + if (line >= to1) + fputs (buf, merge_file); + } + fclose (f0); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Replace hunk in file. + * + * @param dview WDiff widget + * @param merge_file file stream for writing data + * @param from1 first line of source hunk + * @param to1 last line of source hunk + * @param from2 first line of destination hunk + * @param to2 last line of destination hunk + * @param merge_direction in what direction files should be merged + */ + +static void +dview_replace_hunk (WDiff * dview, FILE * merge_file, int from1, int to1, int from2, int to2, + diff_place_t source_diff, diff_place_t dest_diff) +{ + int line1 = 0, line2 = 0; + char buf[BUF_10K]; + FILE *f0; + FILE *f1; + + f0 = fopen (dview->file[source_diff], "r"); + f1 = fopen (dview->file[dest_diff], "r"); + + while (fgets (buf, sizeof (buf), f0) != NULL && line1 < from1 - 1) + { + line1++; + fputs (buf, merge_file); + } + while (fgets (buf, sizeof (buf), f1) != NULL && line2 <= to2) + { + line2++; + if (line2 >= from2) + fputs (buf, merge_file); + } + while (fgets (buf, sizeof (buf), f0) != NULL) + { + line1++; + if (line1 > to1) + fputs (buf, merge_file); + } + fclose (f0); + fclose (f1); +} + +/* --------------------------------------------------------------------------------------------- */ +/** + * Find start and end lines of the current hunk. + * + * @param dview WDiff widget + * @return boolean and + * start_line1 first line of current hunk (file[0]) + * end_line1 last line of current hunk (file[0]) + * start_line1 first line of current hunk (file[0]) + * end_line1 last line of current hunk (file[0]) + */ + +static int +get_current_hunk (WDiff * dview, int *start_line1, int *end_line1, int *start_line2, int *end_line2) +{ + const GArray *a0 = dview->a[DIFF_LEFT]; + const GArray *a1 = dview->a[DIFF_RIGHT]; + size_t pos; + int ch; + int res = 0; + + *start_line1 = 1; + *start_line2 = 1; + *end_line1 = 1; + *end_line2 = 1; + + pos = dview->skip_rows; + ch = ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->ch; + if (ch != EQU_CH) + { + switch (ch) + { + case ADD_CH: + res = DIFF_DEL; + break; + case DEL_CH: + res = DIFF_ADD; + break; + case CHG_CH: + res = DIFF_CHG; + break; + } + while (pos > 0 && ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->ch != EQU_CH) + pos--; + if (pos > 0) + { + *start_line1 = ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->line + 1; + *start_line2 = ((DIFFLN *) & g_array_index (a1, DIFFLN, pos))->line + 1; + } + pos = dview->skip_rows; + while (pos < a0->len && ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->ch != EQU_CH) + { + int l0, l1; + + l0 = ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->line; + l1 = ((DIFFLN *) & g_array_index (a1, DIFFLN, pos))->line; + if (l0 > 0) + *end_line1 = max (*start_line1, l0); + if (l1 > 0) + *end_line2 = max (*start_line2, l1); + pos++; + } + } + return res; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +mc_diffviewer_reread (WDiff * dview) +{ + int ndiff; + + mc_diffviewer_destroy_hdiff (dview); + if (dview->a[DIFF_LEFT] != NULL) + { + g_array_foreach (dview->a[DIFF_LEFT], DIFFLN, cc_free_elt); + g_array_free (dview->a[DIFF_LEFT], TRUE); + } + if (dview->a[DIFF_RIGHT] != NULL) + { + g_array_foreach (dview->a[DIFF_RIGHT], DIFFLN, cc_free_elt); + g_array_free (dview->a[DIFF_RIGHT], TRUE); + } + + dview->a[DIFF_LEFT] = g_array_new (FALSE, FALSE, sizeof (DIFFLN)); + dview->a[DIFF_RIGHT] = g_array_new (FALSE, FALSE, sizeof (DIFFLN)); + + ndiff = mc_diffviewer_redo_diff (dview); + if (ndiff >= 0) + dview->ndiff = ndiff; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +mc_diffviewer_update (WDiff * dview) +{ + int height = dview->height; + int width1; + int width2; + int last; + + last = dview->a[DIFF_LEFT]->len - 1; + + if (dview->skip_rows > last) + dview->skip_rows = dview->search.last_accessed_num_line = last; + if (dview->skip_rows < 0) + dview->skip_rows = dview->search.last_accessed_num_line = 0; + if (dview->skip_cols < 0) + dview->skip_cols = 0; + + if (height < 2) + return; + + width1 = dview->half1 + dview->bias; + width2 = dview->half2 - dview->bias; + if (dview->full) + { + width1 = COLS; + width2 = 0; + } + + if (dview->new_frame) + { + int xwidth; + + tty_setcolor (NORMAL_COLOR); + xwidth = dview->display_symbols + dview->display_numbers; + if (width1 > 1) + tty_draw_box (1, 0, height, width1, FALSE); + if (width2 > 1) + tty_draw_box (1, width1, height, width2, FALSE); + + if (xwidth != 0) + { + xwidth++; + if (xwidth < width1 - 1) + { + tty_gotoyx (1, xwidth); + tty_print_alt_char (ACS_TTEE, FALSE); + tty_gotoyx (height, xwidth); + tty_print_alt_char (ACS_BTEE, FALSE); + tty_draw_vline (2, xwidth, ACS_VLINE, height - 2); + } + if (xwidth < width2 - 1) + { + tty_gotoyx (1, width1 + xwidth); + tty_print_alt_char (ACS_TTEE, FALSE); + tty_gotoyx (height, width1 + xwidth); + tty_print_alt_char (ACS_BTEE, FALSE); + tty_draw_vline (2, width1 + xwidth, ACS_VLINE, height - 2); + } + } + dview->new_frame = 0; + } + + if (width1 > 2) + { + dview_status (dview, dview->ord, width1, 0); + dview_display_file (dview, dview->ord, 2, 1, height - 2, width1 - 2); + } + if (width2 > 2) + { + dview_status (dview, dview->ord ^ 1, width2, width1); + dview_display_file (dview, dview->ord ^ 1, 2, width1 + 1, height - 2, width2 - 2); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +int +mc_diffviewer_calc_nwidth (const GArray ** const a) +{ + int l1, o1; + int l2, o2; + + mc_diffviewer_get_line_numbers (a[DIFF_LEFT], a[DIFF_LEFT]->len - 1, &l1, &o1); + mc_diffviewer_get_line_numbers (a[DIFF_RIGHT], a[DIFF_RIGHT]->len - 1, &l2, &o2); + if (l1 < l2) + l1 = l2; + return get_digits (l1); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +mc_diffviewer_compute_split (WDiff * dview, int i) +{ + dview->bias += i; + if (dview->bias < 2 - dview->half1) + dview->bias = 2 - dview->half1; + if (dview->bias > dview->half2 - 2) + dview->bias = dview->half2 - 2; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +mc_diffviewer_get_line_numbers (const GArray * a, size_t pos, int *linenum, int *lineofs) +{ + const DIFFLN *p; + + *linenum = 0; + *lineofs = 0; + + if (a->len != 0) + { + if (pos >= a->len) + pos = a->len - 1; + + p = &g_array_index (a, DIFFLN, pos); + + if (p->line == 0) + { + int n; + + for (n = pos; n > 0; n--) + { + p--; + if (p->line != 0) + break; + } + *lineofs = pos - n + 1; + } + + *linenum = p->line; + } + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +mc_diffviewer_destroy_hdiff (WDiff * dview) +{ + if (dview->hdiff != NULL) + { + int i; + int len; + + len = dview->a[DIFF_LEFT]->len; + + for (i = 0; i < len; i++) + { + GArray *h; + + h = (GArray *) g_ptr_array_index (dview->hdiff, i); + if (h != NULL) + g_array_free (h, TRUE); + } + g_ptr_array_free (dview->hdiff, TRUE); + dview->hdiff = NULL; + } + + mc_search_free (dview->search.handle); + dview->search.handle = NULL; + MC_PTR_FREE (dview->search.last_string); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +mc_diffviewer_deinit (WDiff * dview) +{ + if (dview->dsrc != DATA_SRC_MEM) + { + mc_diffviewer_file_close (dview->f[DIFF_RIGHT]); + mc_diffviewer_file_close (dview->f[DIFF_LEFT]); + } +#if HAVE_CHARSET + if (dview->converter != str_cnv_from_term) + str_close_conv (dview->converter); +#endif /* HAVE_CHARSET */ + + mc_diffviewer_destroy_hdiff (dview); + if (dview->a[DIFF_LEFT] != NULL) + { + g_array_foreach (dview->a[DIFF_LEFT], DIFFLN, cc_free_elt); + g_array_free (dview->a[DIFF_LEFT], TRUE); + dview->a[DIFF_LEFT] = NULL; + } + if (dview->a[DIFF_RIGHT] != NULL) + { + g_array_foreach (dview->a[DIFF_RIGHT], DIFFLN, cc_free_elt); + g_array_free (dview->a[DIFF_RIGHT], TRUE); + dview->a[DIFF_RIGHT] = NULL; + } + + g_free (dview->label[DIFF_LEFT]); + g_free (dview->label[DIFF_RIGHT]); +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + +gboolean +mc_diffviewer_cmd_merge (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) +{ + WDiff *dview = (WDiff *) data; + diff_place_t source_diff = (diff_place_t) init_data; + diff_place_t dest_diff; + int from1, to1, from2, to2; + int *source_from, *source_to, *dest_from, *dest_to; + int hunk; + + (void) event_group_name; + (void) event_name; + + switch (source_diff) + { + case DIFF_OTHER: + source_diff = dview->ord ^ 1; + break; + case DIFF_CURRENT: + source_diff = dview->ord; + break; + default: + break; + } + + switch (source_diff) + { + case DIFF_LEFT: + dest_diff = DIFF_RIGHT; + source_from = &from1; + source_to = &to1; + dest_from = &from2; + dest_to = &to2; + break; + case DIFF_RIGHT: + dest_diff = DIFF_LEFT; + source_from = &from2; + source_to = &to2; + dest_from = &from1; + dest_to = &to1; + break; + default: + return FALSE; + } + hunk = get_current_hunk (dview, source_from, source_to, dest_from, dest_to); + + if (hunk > 0) + { + int merge_file_fd; + FILE *merge_file; + vfs_path_t *merge_file_name_vpath = NULL; + + if (!dview->merged[source_diff]) + { + dview->merged[source_diff] = + mc_util_make_backup_if_possible (dview->file[source_diff], "~~~"); + if (!dview->merged[source_diff]) + { + message (D_ERROR, MSG_ERROR, + _("Cannot create backup file\n%s%s\n%s"), + dview->file[source_diff], "~~~", unix_error_string (errno)); + return TRUE; + } + } + + merge_file_fd = mc_mkstemps (&merge_file_name_vpath, "mcmerge", NULL); + if (merge_file_fd == -1) + { + message (D_ERROR, MSG_ERROR, _("Cannot create temporary merge file\n%s"), + unix_error_string (errno)); + return TRUE; + } + + merge_file = fdopen (merge_file_fd, "w"); + + switch (hunk) + { + case DIFF_DEL: + if (source_diff == DIFF_RIGHT) + dview_add_hunk (dview, merge_file, *dest_from, *source_from, *source_to, + source_diff, dest_diff); + else + dview_remove_hunk (dview, merge_file, *dest_from, *dest_to, dest_diff); + break; + case DIFF_ADD: + if (source_diff == DIFF_RIGHT) + dview_remove_hunk (dview, merge_file, *dest_from, *dest_to, source_diff); + else + dview_add_hunk (dview, merge_file, *dest_from, *source_from, to2, dest_diff, + source_diff); + break; + case DIFF_CHG: + dview_replace_hunk (dview, merge_file, *dest_from, *dest_to, *source_from, *source_to, + source_diff, dest_diff); + break; + } + fflush (merge_file); + fclose (merge_file); + { + int res; + + res = rewrite_backup_content (merge_file_name_vpath, dview->file[source_diff]); + (void) res; + } + mc_unlink (merge_file_name_vpath); + vfs_path_free (merge_file_name_vpath); + } + + mc_event_raise (MCEVENT_GROUP_DIFFVIEWER, "redo", dview); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +int +mc_diffviewer_redo_diff (WDiff * dview) +{ + FBUF *const *f = dview->f; + PRINTER_CTX ctx; + GArray *ops; + int ndiff; + int rv; + char extra[256]; + + extra[0] = '\0'; + if (dview->opt.quality == 2) + strcat (extra, " -d"); + if (dview->opt.quality == 1) + strcat (extra, " --speed-large-files"); + if (dview->opt.strip_trailing_cr) + strcat (extra, " --strip-trailing-cr"); + if (dview->opt.ignore_tab_expansion) + strcat (extra, " -E"); + if (dview->opt.ignore_space_change) + strcat (extra, " -b"); + if (dview->opt.ignore_all_space) + strcat (extra, " -w"); + if (dview->opt.ignore_case) + strcat (extra, " -i"); + + if (dview->dsrc != DATA_SRC_MEM) + { + mc_diffviewer_file_reset (f[DIFF_LEFT]); + mc_diffviewer_file_reset (f[DIFF_RIGHT]); + } + + ops = g_array_new (FALSE, FALSE, sizeof (DIFFCMD)); + ndiff = + mc_diffviewer_execute (dview->args, extra, dview->file[DIFF_LEFT], dview->file[DIFF_RIGHT], + ops); + if (ndiff < 0) + { + if (ops != NULL) + g_array_free (ops, TRUE); + return -1; + } + + ctx.dsrc = dview->dsrc; + + rv = 0; + ctx.a = dview->a[DIFF_LEFT]; + ctx.f = f[DIFF_LEFT]; + rv |= dff_reparse (DIFF_LEFT, dview->file[DIFF_LEFT], ops, printer, &ctx); + + ctx.a = dview->a[DIFF_RIGHT]; + ctx.f = f[DIFF_RIGHT]; + rv |= dff_reparse (DIFF_RIGHT, dview->file[DIFF_RIGHT], ops, printer, &ctx); + + if (ops != NULL) + g_array_free (ops, TRUE); + + if (rv != 0 || dview->a[DIFF_LEFT]->len != dview->a[DIFF_RIGHT]->len) + return -1; + + if (dview->dsrc == DATA_SRC_TMP) + { + mc_diffviewer_file_trunc (f[DIFF_LEFT]); + mc_diffviewer_file_trunc (f[DIFF_RIGHT]); + } + + if (dview->dsrc == DATA_SRC_MEM && HDIFF_ENABLE) + { + dview->hdiff = g_ptr_array_new (); + if (dview->hdiff != NULL) + { + size_t i; + + for (i = 0; i < dview->a[DIFF_LEFT]->len; i++) + { + GArray *h = NULL; + const DIFFLN *p; + const DIFFLN *q; + + p = &g_array_index (dview->a[DIFF_LEFT], DIFFLN, i); + q = &g_array_index (dview->a[DIFF_RIGHT], DIFFLN, i); + if (p->line && q->line && p->ch == CHG_CH) + { + h = g_array_new (FALSE, FALSE, sizeof (BRACKET)); + if (h != NULL) + { + gboolean runresult; + + runresult = + hdiff_scan (p->p, p->u.len, q->p, q->u.len, HDIFF_MINCTX, h, + HDIFF_DEPTH); + if (!runresult) + { + g_array_free (h, TRUE); + h = NULL; + } + } + } + g_ptr_array_add (dview->hdiff, h); + } + } + } + return ndiff; +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/diffviewer/ydiff.c b/src/diffviewer/ydiff.c index 73bc3bc2b..8b06f2570 100644 --- a/src/diffviewer/ydiff.c +++ b/src/diffviewer/ydiff.c @@ -4,8 +4,8 @@ Written by: Daniel Borca , 2007 - Slava Zanko , 2010, 2013 - Andrew Borodin , 2010, 2012, 2013 + Slava Zanko , 2010, 2013, 2014, 2015 + Andrew Borodin , 2010, 2012, 2013, 2015 Ilia Maslakov , 2010 This file is part of the Midnight Commander. @@ -28,22 +28,19 @@ #include #include #include -#include #include #include #include #include #include "lib/global.h" +#include "lib/event.h" #include "lib/tty/tty.h" #include "lib/tty/color.h" #include "lib/tty/key.h" #include "lib/skin.h" /* EDITOR_NORMAL_COLOR */ -#include "lib/vfs/vfs.h" /* mc_opendir, mc_readdir, mc_closedir, */ -#include "lib/util.h" #include "lib/widget.h" #include "lib/strutil.h" -#include "lib/strescape.h" /* strutils_glob_escape() */ #ifdef HAVE_CHARSET #include "lib/charsets.h" #endif @@ -51,2223 +48,30 @@ #include "src/filemanager/cmd.h" /* edit_file_at_line(), view_other_cmd() */ #include "src/filemanager/panel.h" -#include "src/filemanager/layout.h" /* Needed for get_current_index and get_other_panel */ #include "src/keybind-defaults.h" #include "src/setup.h" #include "src/history.h" -#ifdef HAVE_CHARSET -#include "src/selcodepage.h" -#endif #include "ydiff.h" #include "internal.h" +#ifdef HAVE_CHARSET +#include "charset.h" +#endif + /*** global variables ****************************************************************************/ /*** file scope macro definitions ****************************************************************/ -#define g_array_foreach(a, TP, cbf) \ -do { \ - size_t g_array_foreach_i;\ - \ - for (g_array_foreach_i = 0; g_array_foreach_i < a->len; g_array_foreach_i++) \ - { \ - TP *g_array_foreach_var; \ - \ - g_array_foreach_var = &g_array_index (a, TP, g_array_foreach_i); \ - (*cbf) (g_array_foreach_var); \ - } \ -} while (0) - -#define FILE_READ_BUF 4096 -#define FILE_FLAG_TEMP (1 << 0) - -#define ADD_CH '+' -#define DEL_CH '-' -#define CHG_CH '*' -#define EQU_CH ' ' - -#define HDIFF_ENABLE 1 -#define HDIFF_MINCTX 5 -#define HDIFF_DEPTH 10 - -#define FILE_DIRTY(fs) \ -do \ -{ \ - (fs)->pos = 0; \ - (fs)->len = 0; \ -} \ -while (0) - /*** file scope type declarations ****************************************************************/ -typedef enum -{ - FROM_LEFT_TO_RIGHT, - FROM_RIGHT_TO_LEFT -} action_direction_t; - /*** file scope variables ************************************************************************/ /*** file scope functions ************************************************************************/ -/* --------------------------------------------------------------------------------------------- */ - -static inline int -TAB_SKIP (int ts, int pos) -{ - if (ts > 0 && ts < 9) - return ts - pos % ts; - else - return 8 - pos % 8; -} - -/* --------------------------------------------------------------------------------------------- */ - -static gboolean -rewrite_backup_content (const vfs_path_t * from_file_name_vpath, const char *to_file_name) -{ - FILE *backup_fd; - char *contents; - gsize length; - const char *from_file_name; - - from_file_name = vfs_path_get_by_index (from_file_name_vpath, -1)->path; - if (!g_file_get_contents (from_file_name, &contents, &length, NULL)) - return FALSE; - - backup_fd = fopen (to_file_name, "w"); - if (backup_fd == NULL) - { - g_free (contents); - return FALSE; - } - - length = fwrite ((const void *) contents, length, 1, backup_fd); - - fflush (backup_fd); - fclose (backup_fd); - g_free (contents); - return TRUE; -} - -/* buffered I/O ************************************************************* */ - -/** - * Try to open a temporary file. - * @note the name is not altered if this function fails - * - * @param[out] name address of a pointer to store the temporary name - * @return file descriptor on success, negative on error - */ - -static int -open_temp (void **name) -{ - int fd; - vfs_path_t *diff_file_name = NULL; - - fd = mc_mkstemps (&diff_file_name, "mcdiff", NULL); - if (fd == -1) - { - message (D_ERROR, MSG_ERROR, - _("Cannot create temporary diff file\n%s"), unix_error_string (errno)); - return -1; - } - *name = g_strdup (vfs_path_as_str (diff_file_name)); - vfs_path_free (diff_file_name); - 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 = g_try_malloc (sizeof (FBUF)); - if (fs == NULL) - return NULL; - - fs->buf = g_try_malloc (FILE_READ_BUF); - if (fs->buf == NULL) - { - g_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); - g_free (fs->data); - } - g_free (fs->buf); - g_free (fs); - return rv; -} - -/* --------------------------------------------------------------------------------------------- */ - -/** - * Open a binary temporary file in R/W mode. - * @note the file will be deleted when closed - * - * @return file structure - */ -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. - * @note does not stop on null-byte - * @note buf will not be null-terminated - * - * @param buf destination buffer - * @param size size of buffer - * @param fs file structure - * - * @return number of bytes read - */ - -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; -} - -/* --------------------------------------------------------------------------------------------- */ - -/** - * Seek into file. - * @note avoids thrashing read cache when possible - * - * @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 - */ - -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; - - rv = lseek (fs->fd, 0, SEEK_SET); - if (rv != -1) - FILE_DIRTY (fs); - return rv; -} - -/* --------------------------------------------------------------------------------------------- */ - -/** - * Write bytes to file. - * @note thrashes read cache - * - * @param fs file structure - * @param buf source buffer - * @param size size of buffer - * - * @return number of written bytes, -1 on error - */ - -static ssize_t -f_write (FBUF * fs, const char *buf, size_t size) -{ - ssize_t rv; - - rv = write (fs->fd, buf, size); - if (rv >= 0) - FILE_DIRTY (fs); - return rv; -} - -/* --------------------------------------------------------------------------------------------- */ - -/** - * Truncate file to the current position. - * @note thrashes read cache - * - * @param fs file structure - * - * @return current file size on success, negative on error - */ - -static off_t -f_trunc (FBUF * fs) -{ - off_t off; - - off = lseek (fs->fd, 0, SEEK_CUR); - if (off != -1) - { - int rv; - - rv = ftruncate (fs->fd, off); - if (rv != 0) - off = -1; - else - FILE_DIRTY (fs); - } - return off; -} - -/* --------------------------------------------------------------------------------------------- */ - -/** - * Close file. - * @note if this is temporary file, it is deleted - * - * @param fs file structure - * @return 0 on success, non-zero on error - */ - -static int -f_close (FBUF * fs) -{ - int rv = -1; - - if (fs != NULL) - { - 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"; - else 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 = -1; - - if (fs != NULL) - { - rv = pclose (fs->data); - f_free (fs); - } - - return rv; -} - -/* --------------------------------------------------------------------------------------------- */ - -/** - * Get one char (byte) from string - * - * @param char * str, gboolean * result - * @return int as character or 0 and result == FALSE if fail - */ - -static int -dview_get_byte (char *str, gboolean * result) -{ - if (str == NULL) - { - *result = FALSE; - return 0; - } - *result = TRUE; - return (unsigned char) *str; -} - -/* --------------------------------------------------------------------------------------------- */ - -#ifdef HAVE_CHARSET -/** - * Get utf multibyte char from string - * - * @param char * str, int * char_length, gboolean * result - * @return int as utf character or 0 and result == FALSE if fail - */ - -static int -dview_get_utf (char *str, int *char_length, gboolean * result) -{ - int res = -1; - gunichar ch; - int ch_len = 0; - - *result = TRUE; - - if (str == NULL) - { - *result = FALSE; - return 0; - } - - res = g_utf8_get_char_validated (str, -1); - - if (res < 0) - ch = *str; - else - { - gchar *next_ch; - - ch = res; - /* Calculate UTF-8 char length */ - next_ch = g_utf8_next_char (str); - ch_len = next_ch - str; - } - *char_length = ch_len; - return ch; -} - -/* --------------------------------------------------------------------------------------------- */ - -static int -dview_str_utf8_offset_to_pos (const char *text, size_t length) -{ - ptrdiff_t result; - - if (text == NULL || text[0] == '\0') - return length; - - if (g_utf8_validate (text, -1, NULL)) - result = g_utf8_offset_to_pointer (text, length) - text; - else - { - gunichar uni; - char *tmpbuf, *buffer; - - buffer = tmpbuf = g_strdup (text); - while (tmpbuf[0] != '\0') - { - uni = g_utf8_get_char_validated (tmpbuf, -1); - if ((uni != (gunichar) (-1)) && (uni != (gunichar) (-2))) - tmpbuf = g_utf8_next_char (tmpbuf); - else - { - tmpbuf[0] = '.'; - tmpbuf++; - } - } - result = g_utf8_offset_to_pointer (tmpbuf, length) - tmpbuf; - g_free (buffer); - } - return max (length, (size_t) result); -} -#endif /*HAVE_CHARSET */ - -/* --------------------------------------------------------------------------------------------- */ - -/* diff parse *************************************************************** */ - -/** - * 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 != 0 || 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, GArray * 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 != 0) - 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' && range != 0) - 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; - g_array_append_val (ops, op); - 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, GArray * ops) -{ - int sz; - char buf[BUFSIZ]; - - while ((sz = f_gets (buf, sizeof (buf) - 1, f)) != 0) - { - 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)) != 0) - ; - } - - 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, - GArray * 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; - char *file1_esc, *file2_esc; - - /* escape potential $ to avoid shell variable substitutions in popen() */ - file1_esc = strutils_shell_escape (file1); - file2_esc = strutils_shell_escape (file2); - cmd = g_strdup_printf ("diff %s %s %s %s %s", args, extra, opt, file1_esc, file2_esc); - g_free (file1_esc); - g_free (file2_esc); - - if (cmd == NULL) - return -1; - - f = p_open (cmd, O_RDONLY); - g_free (cmd); - - if (f == NULL) - return -1; - - rv = scan_diff (f, ops); - code = p_close (f); - - if (rv < 0 || code == -1 || !WIFEXITED (code) || WEXITSTATUS (code) == 2) - rv = -1; - - return rv; -} - -/* --------------------------------------------------------------------------------------------- */ - -/** - * Reparse and display file according to diff statements. - * - * @param ord DIFF_LEFT if 1nd file is displayed , DIFF_RIGHT if 2nd file is displayed. - * @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 (diff_place_t ord, const char *filename, const GArray * ops, DFUNC printer, void *ctx) -{ - size_t i; - FBUF *f; - size_t sz; - char buf[BUFSIZ]; - int line = 0; - off_t off = 0; - const DIFFCMD *op; - diff_place_t eff; - int add_cmd; - int del_cmd; - - f = f_open (filename, O_RDONLY); - if (f == NULL) - return -1; - - ord &= 1; - eff = ord; - - add_cmd = 'a'; - del_cmd = 'd'; - if (ord != 0) - { - add_cmd = 'd'; - del_cmd = 'a'; - } -#define F1 a[eff][0] -#define F2 a[eff][1] -#define T1 a[ ord^1 ][0] -#define T2 a[ ord^1 ][1] - for (i = 0; i < ops->len; i++) - { - int n; - - op = &g_array_index (ops, DIFFCMD, i); - n = op->F1 - (op->cmd != add_cmd); - - while (line < n && (sz = f_gets (buf, sizeof (buf), f)) != 0) - { - line++; - printer (ctx, EQU_CH, line, off, sz, buf); - off += sz; - while (buf[sz - 1] != '\n') - { - sz = f_gets (buf, sizeof (buf), f); - if (sz == 0) - { - 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 != 0) - { - printer (ctx, DEL_CH, 0, 0, 1, "\n"); - n--; - } - } - - if (op->cmd == del_cmd) - { - n = op->F2 - op->F1 + 1; - while (n != 0 && (sz = f_gets (buf, sizeof (buf), f)) != 0) - { - line++; - printer (ctx, ADD_CH, line, off, sz, buf); - off += sz; - while (buf[sz - 1] != '\n') - { - sz = f_gets (buf, sizeof (buf), f); - if (sz == 0) - { - printer (ctx, 0, 0, 0, 1, "\n"); - break; - } - printer (ctx, 0, 0, 0, sz, buf); - off += sz; - } - n--; - } - - if (n != 0) - goto err; - } - - if (op->cmd == 'c') - { - n = op->F2 - op->F1 + 1; - while (n != 0 && (sz = f_gets (buf, sizeof (buf), f)) != 0) - { - line++; - printer (ctx, CHG_CH, line, off, sz, buf); - off += sz; - while (buf[sz - 1] != '\n') - { - sz = f_gets (buf, sizeof (buf), f); - if (sz == 0) - { - printer (ctx, 0, 0, 0, 1, "\n"); - break; - } - printer (ctx, 0, 0, 0, sz, buf); - off += sz; - } - n--; - } - - if (n != 0) - 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)) != 0) - { - line++; - printer (ctx, EQU_CH, line, off, sz, buf); - off += sz; - while (buf[sz - 1] != '\n') - { - sz = f_gets (buf, sizeof (buf), f); - if (sz == 0) - { - 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 ********************************************************** */ - -/** - * 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, GArray * ret, int min) -{ - int i, j; - int *Lprev, *Lcurr; - int z = 0; - - if (m < min || n < min) - { - /* XXX early culling */ - return 0; - } - - Lprev = g_try_new0 (int, n + 1); - if (Lprev == NULL) - return -1; - - Lcurr = g_try_new0 (int, n + 1); - if (Lcurr == NULL) - { - g_free (Lprev); - return -1; - } - - for (i = 0; i < m; i++) - { - int *L; - - 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; - - v = Lprev[j] + 1; - Lcurr[j + 1] = v; - if (z < v) - { - z = v; - g_array_set_size (ret, 0); - } - if (z == v && z >= min) - { - int off0, off1; - size_t k; - - off0 = i - z + 1; - off1 = j - z + 1; - - for (k = 0; k < ret->len; k++) - { - PAIR *p = (PAIR *) g_array_index (ret, PAIR, k); - if ((*p)[0] == off0 || (*p)[1] >= off1) - break; - } - if (k == ret->len) - { - PAIR p2; - - p2[0] = off0; - p2[1] = off1; - g_array_append_val (ret, p2); - } - } - } - } - } - - g_free (Lcurr); - g_free (Lprev); - return z; -} - -/* --------------------------------------------------------------------------------------------- */ - -/** - * 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 gboolean -hdiff_multi (const char *s, const char *t, const BRACKET bracket, int min, GArray * hdiff, - unsigned int depth) -{ - BRACKET p; - - if (depth-- != 0) - { - GArray *ret; - BRACKET b; - int len; - - ret = g_array_new (FALSE, TRUE, sizeof (PAIR)); - if (ret == NULL) - return FALSE; - - len = lcsubstr (s + bracket[DIFF_LEFT].off, bracket[DIFF_LEFT].len, - t + bracket[DIFF_RIGHT].off, bracket[DIFF_RIGHT].len, ret, min); - if (ret->len != 0) - { - size_t k = 0; - const PAIR *data = (const PAIR *) &g_array_index (ret, PAIR, 0); - const PAIR *data2; - - b[DIFF_LEFT].off = bracket[DIFF_LEFT].off; - b[DIFF_LEFT].len = (*data)[0]; - b[DIFF_RIGHT].off = bracket[DIFF_RIGHT].off; - b[DIFF_RIGHT].len = (*data)[1]; - if (!hdiff_multi (s, t, b, min, hdiff, depth)) - return FALSE; - - for (k = 0; k < ret->len - 1; k++) - { - data = (const PAIR *) &g_array_index (ret, PAIR, k); - data2 = (const PAIR *) &g_array_index (ret, PAIR, k + 1); - b[DIFF_LEFT].off = bracket[DIFF_LEFT].off + (*data)[0] + len; - b[DIFF_LEFT].len = (*data2)[0] - (*data)[0] - len; - b[DIFF_RIGHT].off = bracket[DIFF_RIGHT].off + (*data)[1] + len; - b[DIFF_RIGHT].len = (*data2)[1] - (*data)[1] - len; - if (!hdiff_multi (s, t, b, min, hdiff, depth)) - return FALSE; - } - data = (const PAIR *) &g_array_index (ret, PAIR, k); - b[DIFF_LEFT].off = bracket[DIFF_LEFT].off + (*data)[0] + len; - b[DIFF_LEFT].len = bracket[DIFF_LEFT].len - (*data)[0] - len; - b[DIFF_RIGHT].off = bracket[DIFF_RIGHT].off + (*data)[1] + len; - b[DIFF_RIGHT].len = bracket[DIFF_RIGHT].len - (*data)[1] - len; - if (!hdiff_multi (s, t, b, min, hdiff, depth)) - return FALSE; - - g_array_free (ret, TRUE); - return TRUE; - } - } - - p[DIFF_LEFT].off = bracket[DIFF_LEFT].off; - p[DIFF_LEFT].len = bracket[DIFF_LEFT].len; - p[DIFF_RIGHT].off = bracket[DIFF_RIGHT].off; - p[DIFF_RIGHT].len = bracket[DIFF_RIGHT].len; - g_array_append_val (hdiff, p); - - return TRUE; -} - -/* --------------------------------------------------------------------------------------------- */ - -/** - * 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 gboolean -hdiff_scan (const char *s, int m, const char *t, int n, int min, GArray * 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[DIFF_LEFT].off = i; - b[DIFF_LEFT].len = m - i; - b[DIFF_RIGHT].off = i; - b[DIFF_RIGHT].len = n - i; - - /* smartscan (multiple horizontal diff) */ - return hdiff_multi (s, t, b, min, hdiff, depth); -} - -/* --------------------------------------------------------------------------------------------- */ - -/* read line **************************************************************** */ - -/** - * Check if character is inside horizontal diff limits. - * - * @param k rank of character inside line - * @param hdiff horizontal diff structure - * @param ord DIFF_LEFT if reading from first file, DIFF_RIGHT if reading from 2nd file - * - * @return TRUE if inside hdiff limits, FALSE otherwise - */ - -static gboolean -is_inside (int k, GArray * hdiff, diff_place_t ord) -{ - size_t i; - BRACKET *b; - - for (i = 0; i < hdiff->len; i++) - { - int start, end; - - b = &g_array_index (hdiff, BRACKET, i); - start = (*b)[ord].off; - end = start + (*b)[ord].len; - if (k >= start && k < end) - return TRUE; - } - return FALSE; -} - -/* --------------------------------------------------------------------------------------------- */ - -/** - * Copy 'src' to 'dst' expanding tabs. - * @note The procedure returns when all bytes are consumed from 'src' - * - * @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 - */ - -static int -cvt_cpy (char *dst, const char *src, size_t srcsize, int base, int ts) -{ - int i; - - for (i = 0; srcsize != 0; i++, src++, dst++, srcsize--) - { - *dst = *src; - if (*src == '\t') - { - int j; - - 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 != 0; i++, src++, dst++, srcsize--) - { - *dst = *src; - if (*src == '\t') - { - int j; - - 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 != 0 && srcsize != 0 && *src != '\n'; i++, src++, srcsize--) - { - if (*src == '\t') - { - int j; - - j = TAB_SKIP (ts, i + base); - i += j - 1; - while (j-- > 0) - { - if (skip > 0) - skip--; - else if (dstsize != 0) - { - dstsize--; - *dst++ = ' '; - } - } - } - else if (src[0] == '\r' && (srcsize == 1 || src[1] == '\n')) - { - if (skip == 0 && show_cr) - { - if (dstsize > 1) - { - dstsize -= 2; - *dst++ = '^'; - *dst++ = 'M'; - } - else - { - dstsize--; - *dst++ = '.'; - } - } - break; - } - else if (skip > 0) - { -#ifdef HAVE_CHARSET - gboolean res; - int ch_len = 1; - - (void) dview_get_utf ((char *) src, &ch_len, &res); - - if (ch_len > 1) - skip += ch_len - 1; -#endif - - skip--; - } - else - { - dstsize--; - *dst++ = *src; - } - } - sz = dst - tmp; - } - while (dstsize != 0) - { - 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 DIFF_LEFT if reading from first file, DIFF_RIGHT 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, - GArray * hdiff, diff_place_t 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 != 0 && srcsize != 0 && *src != '\n'; i++, k++, src++, srcsize--) - { - if (*src == '\t') - { - int j; - - j = TAB_SKIP (ts, i + base); - i += j - 1; - while (j-- > 0) - { - if (skip != 0) - skip--; - else if (dstsize != 0) - { - dstsize--; - *att++ = is_inside (k, hdiff, ord); - *dst++ = ' '; - } - } - } - else if (src[0] == '\r' && (srcsize == 1 || src[1] == '\n')) - { - if (skip == 0 && 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 != 0) - { -#ifdef HAVE_CHARSET - gboolean res; - int ch_len = 1; - - (void) dview_get_utf ((char *) src, &ch_len, &res); - if (ch_len > 1) - skip += ch_len - 1; -#endif - - skip--; - } - else - { - dstsize--; - *att++ = is_inside (k, hdiff, ord); - *dst++ = *src; - } - } - sz = dst - tmp; - } - while (dstsize != 0) - { - 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, size_t dstsize, int skip, int ts, int show_cr) -{ - int base = 0; - int old_base = base; - size_t amount = dstsize; - size_t useful, offset; - size_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 (sizeof (tmp) < amount || sizeof (tmp) <= dstsize || 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; - sz = f_gets (tmp, amount, f); - if (sz == 0) - 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; - } - } - - if (base < skip) - { - memset (dst, ' ', dstsize); - dst[dstsize] = '\0'; - return 0; - } - - useful = base - skip; - offset = skip - old_base; - - if (useful <= dstsize) - { - if (useful != 0) - memmove (dst, cvt + offset, useful); - - if (q == NULL) - { - sz = f_gets (tmp, dstsize - useful + 1, f); - if (sz != 0) - { - 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; - } - } - - for (; i < dstsize; i++) - dst[i] = ' '; - dst[i] = '\0'; - return sz; -} - -/* --------------------------------------------------------------------------------------------- */ -/* diff printers et al ****************************************************** */ - -static void -cc_free_elt (void *elt) -{ - DIFFLN *p = elt; - - if (p != NULL) - g_free (p->p); -} - -/* --------------------------------------------------------------------------------------------- */ - -static int -printer (void *ctx, int ch, int line, off_t off, size_t sz, const char *str) -{ - GArray *a = ((PRINTER_CTX *) ctx)->a; - DSRC dsrc = ((PRINTER_CTX *) ctx)->dsrc; - - if (ch != 0) - { - DIFFLN p; - - p.p = NULL; - p.ch = ch; - p.line = line; - p.u.off = off; - if (dsrc == DATA_SRC_MEM && line != 0) - { - if (sz != 0 && str[sz - 1] == '\n') - sz--; - if (sz > 0) - p.p = g_strndup (str, sz); - p.u.len = sz; - } - g_array_append_val (a, p); - } - else if (dsrc == DATA_SRC_MEM) - { - DIFFLN *p; - - p = &g_array_index (a, DIFFLN, a->len - 1); - if (sz != 0 && str[sz - 1] == '\n') - sz--; - if (sz != 0) - { - size_t new_size; - char *q; - - new_size = p->u.len + sz; - q = g_realloc (p->p, new_size); - memcpy (q + p->u.len, str, sz); - p->p = q; - } - p->u.len += sz; - } - if (dsrc == DATA_SRC_TMP && (line != 0 || ch == 0)) - { - FBUF *f = ((PRINTER_CTX *) ctx)->f; - f_write (f, str, sz); - } - return 0; -} - -/* --------------------------------------------------------------------------------------------- */ - -static int -redo_diff (WDiff * dview) -{ - FBUF *const *f = dview->f; - PRINTER_CTX ctx; - GArray *ops; - int ndiff; - int rv; - char extra[256]; - - extra[0] = '\0'; - if (dview->opt.quality == 2) - strcat (extra, " -d"); - if (dview->opt.quality == 1) - strcat (extra, " --speed-large-files"); - if (dview->opt.strip_trailing_cr) - strcat (extra, " --strip-trailing-cr"); - if (dview->opt.ignore_tab_expansion) - strcat (extra, " -E"); - if (dview->opt.ignore_space_change) - strcat (extra, " -b"); - if (dview->opt.ignore_all_space) - strcat (extra, " -w"); - if (dview->opt.ignore_case) - strcat (extra, " -i"); - - if (dview->dsrc != DATA_SRC_MEM) - { - f_reset (f[DIFF_LEFT]); - f_reset (f[DIFF_RIGHT]); - } - - ops = g_array_new (FALSE, FALSE, sizeof (DIFFCMD)); - ndiff = dff_execute (dview->args, extra, dview->file[DIFF_LEFT], dview->file[DIFF_RIGHT], ops); - if (ndiff < 0) - { - if (ops != NULL) - g_array_free (ops, TRUE); - return -1; - } - - ctx.dsrc = dview->dsrc; - - rv = 0; - ctx.a = dview->a[DIFF_LEFT]; - ctx.f = f[DIFF_LEFT]; - rv |= dff_reparse (DIFF_LEFT, dview->file[DIFF_LEFT], ops, printer, &ctx); - - ctx.a = dview->a[DIFF_RIGHT]; - ctx.f = f[DIFF_RIGHT]; - rv |= dff_reparse (DIFF_RIGHT, dview->file[DIFF_RIGHT], ops, printer, &ctx); - - if (ops != NULL) - g_array_free (ops, TRUE); - - if (rv != 0 || dview->a[DIFF_LEFT]->len != dview->a[DIFF_RIGHT]->len) - return -1; - - if (dview->dsrc == DATA_SRC_TMP) - { - f_trunc (f[DIFF_LEFT]); - f_trunc (f[DIFF_RIGHT]); - } - - if (dview->dsrc == DATA_SRC_MEM && HDIFF_ENABLE) - { - dview->hdiff = g_ptr_array_new (); - if (dview->hdiff != NULL) - { - size_t i; - - for (i = 0; i < dview->a[DIFF_LEFT]->len; i++) - { - GArray *h = NULL; - const DIFFLN *p; - const DIFFLN *q; - - p = &g_array_index (dview->a[DIFF_LEFT], DIFFLN, i); - q = &g_array_index (dview->a[DIFF_RIGHT], DIFFLN, i); - if (p->line && q->line && p->ch == CHG_CH) - { - h = g_array_new (FALSE, FALSE, sizeof (BRACKET)); - if (h != NULL) - { - gboolean runresult; - - runresult = - hdiff_scan (p->p, p->u.len, q->p, q->u.len, HDIFF_MINCTX, h, - HDIFF_DEPTH); - if (!runresult) - { - g_array_free (h, TRUE); - h = NULL; - } - } - } - g_ptr_array_add (dview->hdiff, h); - } - } - } - return ndiff; -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -destroy_hdiff (WDiff * dview) -{ - if (dview->hdiff != NULL) - { - int i; - int len; - - len = dview->a[DIFF_LEFT]->len; - - for (i = 0; i < len; i++) - { - GArray *h; - - h = (GArray *) g_ptr_array_index (dview->hdiff, i); - if (h != NULL) - g_array_free (h, TRUE); - } - g_ptr_array_free (dview->hdiff, TRUE); - dview->hdiff = NULL; - } - - mc_search_free (dview->search.handle); - dview->search.handle = NULL; - MC_PTR_FREE (dview->search.last_string); -} - -/* --------------------------------------------------------------------------------------------- */ -/* stuff ******************************************************************** */ - -static int -get_digits (unsigned int n) -{ - int d = 1; - - while (n /= 10) - d++; - return d; -} - -/* --------------------------------------------------------------------------------------------- */ - -static int -get_line_numbers (const GArray * a, size_t pos, int *linenum, int *lineofs) -{ - const DIFFLN *p; - - *linenum = 0; - *lineofs = 0; - - if (a->len != 0) - { - if (pos >= a->len) - pos = a->len - 1; - - p = &g_array_index (a, DIFFLN, pos); - - if (p->line == 0) - { - int n; - - for (n = pos; n > 0; n--) - { - p--; - if (p->line != 0) - break; - } - *lineofs = pos - n + 1; - } - - *linenum = p->line; - } - return 0; -} - -/* --------------------------------------------------------------------------------------------- */ - -static int -calc_nwidth (const GArray ** const a) -{ - int l1, o1; - int l2, o2; - - get_line_numbers (a[DIFF_LEFT], a[DIFF_LEFT]->len - 1, &l1, &o1); - get_line_numbers (a[DIFF_RIGHT], a[DIFF_RIGHT]->len - 1, &l2, &o2); - if (l1 < l2) - l1 = l2; - return get_digits (l1); -} - -/* --------------------------------------------------------------------------------------------- */ - -static int -find_prev_hunk (const GArray * a, int pos) -{ -#if 1 - while (pos > 0 && ((DIFFLN *) & g_array_index (a, DIFFLN, pos))->ch != EQU_CH) - pos--; - while (pos > 0 && ((DIFFLN *) & g_array_index (a, DIFFLN, pos))->ch == EQU_CH) - pos--; - while (pos > 0 && ((DIFFLN *) & g_array_index (a, DIFFLN, pos))->ch != EQU_CH) - pos--; - if (pos > 0 && (size_t) pos < a->len) - pos++; -#else - while (pos > 0 && ((DIFFLN *) & g_array_index (a, DIFFLN, pos - 1))->ch == EQU_CH) - pos--; - while (pos > 0 && ((DIFFLN *) & g_array_index (a, DIFFLN, pos - 1))->ch != EQU_CH) - pos--; -#endif - - return pos; -} - -/* --------------------------------------------------------------------------------------------- */ - -static size_t -find_next_hunk (const GArray * a, size_t pos) -{ - while (pos < a->len && ((DIFFLN *) & g_array_index (a, DIFFLN, pos))->ch != EQU_CH) - pos++; - while (pos < a->len && ((DIFFLN *) & g_array_index (a, DIFFLN, pos))->ch == EQU_CH) - pos++; - return pos; -} - -/* --------------------------------------------------------------------------------------------- */ -/** - * Find start and end lines of the current hunk. - * - * @param dview WDiff widget - * @return boolean and - * start_line1 first line of current hunk (file[0]) - * end_line1 last line of current hunk (file[0]) - * start_line1 first line of current hunk (file[0]) - * end_line1 last line of current hunk (file[0]) - */ - -static int -get_current_hunk (WDiff * dview, int *start_line1, int *end_line1, int *start_line2, int *end_line2) -{ - const GArray *a0 = dview->a[DIFF_LEFT]; - const GArray *a1 = dview->a[DIFF_RIGHT]; - size_t pos; - int ch; - int res = 0; - - *start_line1 = 1; - *start_line2 = 1; - *end_line1 = 1; - *end_line2 = 1; - - pos = dview->skip_rows; - ch = ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->ch; - if (ch != EQU_CH) - { - switch (ch) - { - case ADD_CH: - res = DIFF_DEL; - break; - case DEL_CH: - res = DIFF_ADD; - break; - case CHG_CH: - res = DIFF_CHG; - break; - } - while (pos > 0 && ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->ch != EQU_CH) - pos--; - if (pos > 0) - { - *start_line1 = ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->line + 1; - *start_line2 = ((DIFFLN *) & g_array_index (a1, DIFFLN, pos))->line + 1; - } - pos = dview->skip_rows; - while (pos < a0->len && ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->ch != EQU_CH) - { - int l0, l1; - - l0 = ((DIFFLN *) & g_array_index (a0, DIFFLN, pos))->line; - l1 = ((DIFFLN *) & g_array_index (a1, DIFFLN, pos))->line; - if (l0 > 0) - *end_line1 = max (*start_line1, l0); - if (l1 > 0) - *end_line2 = max (*start_line2, l1); - pos++; - } - } - return res; -} - -/* --------------------------------------------------------------------------------------------- */ -/** - * Remove hunk from file. - * - * @param dview WDiff widget - * @param merge_file file stream for writing data - * @param from1 first line of hunk - * @param to1 last line of hunk - * @param merge_direction in what direction files should be merged - */ - -static void -dview_remove_hunk (WDiff * dview, FILE * merge_file, int from1, int to1, - action_direction_t merge_direction) -{ - int line; - char buf[BUF_10K]; - FILE *f0; - - if (merge_direction == FROM_RIGHT_TO_LEFT) - f0 = fopen (dview->file[DIFF_RIGHT], "r"); - else - f0 = fopen (dview->file[DIFF_LEFT], "r"); - - line = 0; - while (fgets (buf, sizeof (buf), f0) != NULL && line < from1 - 1) - { - line++; - fputs (buf, merge_file); - } - while (fgets (buf, sizeof (buf), f0) != NULL) - { - line++; - if (line >= to1) - fputs (buf, merge_file); - } - fclose (f0); -} - -/* --------------------------------------------------------------------------------------------- */ -/** - * Add hunk to file. - * - * @param dview WDiff widget - * @param merge_file file stream for writing data - * @param from1 first line of source hunk - * @param from2 first line of destination hunk - * @param to1 last line of source hunk - * @param merge_direction in what direction files should be merged - */ - -static void -dview_add_hunk (WDiff * dview, FILE * merge_file, int from1, int from2, int to2, - action_direction_t merge_direction) -{ - int line; - char buf[BUF_10K]; - FILE *f0; - FILE *f1; - - if (merge_direction == FROM_RIGHT_TO_LEFT) - { - f0 = fopen (dview->file[DIFF_RIGHT], "r"); - f1 = fopen (dview->file[DIFF_LEFT], "r"); - } - else - { - f0 = fopen (dview->file[DIFF_LEFT], "r"); - f1 = fopen (dview->file[DIFF_RIGHT], "r"); - } - - line = 0; - while (fgets (buf, sizeof (buf), f0) != NULL && line < from1 - 1) - { - line++; - fputs (buf, merge_file); - } - line = 0; - while (fgets (buf, sizeof (buf), f1) != NULL && line <= to2) - { - line++; - if (line >= from2) - fputs (buf, merge_file); - } - while (fgets (buf, sizeof (buf), f0) != NULL) - fputs (buf, merge_file); - - fclose (f0); - fclose (f1); -} - -/* --------------------------------------------------------------------------------------------- */ -/** - * Replace hunk in file. - * - * @param dview WDiff widget - * @param merge_file file stream for writing data - * @param from1 first line of source hunk - * @param to1 last line of source hunk - * @param from2 first line of destination hunk - * @param to2 last line of destination hunk - * @param merge_direction in what direction files should be merged - */ - -static void -dview_replace_hunk (WDiff * dview, FILE * merge_file, int from1, int to1, int from2, int to2, - action_direction_t merge_direction) -{ - int line1 = 0, line2 = 0; - char buf[BUF_10K]; - FILE *f0; - FILE *f1; - - if (merge_direction == FROM_RIGHT_TO_LEFT) - { - f0 = fopen (dview->file[DIFF_RIGHT], "r"); - f1 = fopen (dview->file[DIFF_LEFT], "r"); - } - else - { - f0 = fopen (dview->file[DIFF_LEFT], "r"); - f1 = fopen (dview->file[DIFF_RIGHT], "r"); - } - - while (fgets (buf, sizeof (buf), f0) != NULL && line1 < from1 - 1) - { - line1++; - fputs (buf, merge_file); - } - while (fgets (buf, sizeof (buf), f1) != NULL && line2 <= to2) - { - line2++; - if (line2 >= from2) - fputs (buf, merge_file); - } - while (fgets (buf, sizeof (buf), f0) != NULL) - { - line1++; - if (line1 > to1) - fputs (buf, merge_file); - } - fclose (f0); - fclose (f1); -} - -/* --------------------------------------------------------------------------------------------- */ -/** - * Merge hunk. - * - * @param dview WDiff widget - * @param merge_direction in what direction files should be merged - */ - -static void -do_merge_hunk (WDiff * dview, action_direction_t merge_direction) -{ - int from1, to1, from2, to2; - int hunk; - diff_place_t n_merge = (merge_direction == FROM_RIGHT_TO_LEFT) ? DIFF_RIGHT : DIFF_LEFT; - - if (merge_direction == FROM_RIGHT_TO_LEFT) - hunk = get_current_hunk (dview, &from2, &to2, &from1, &to1); - else - hunk = get_current_hunk (dview, &from1, &to1, &from2, &to2); - - if (hunk > 0) - { - int merge_file_fd; - FILE *merge_file; - vfs_path_t *merge_file_name_vpath = NULL; - - if (!dview->merged[n_merge]) - { - dview->merged[n_merge] = mc_util_make_backup_if_possible (dview->file[n_merge], "~~~"); - if (!dview->merged[n_merge]) - { - message (D_ERROR, MSG_ERROR, - _("Cannot create backup file\n%s%s\n%s"), - dview->file[n_merge], "~~~", unix_error_string (errno)); - return; - } - } - - merge_file_fd = mc_mkstemps (&merge_file_name_vpath, "mcmerge", NULL); - if (merge_file_fd == -1) - { - message (D_ERROR, MSG_ERROR, _("Cannot create temporary merge file\n%s"), - unix_error_string (errno)); - return; - } - - merge_file = fdopen (merge_file_fd, "w"); - - switch (hunk) - { - case DIFF_DEL: - if (merge_direction == FROM_RIGHT_TO_LEFT) - dview_add_hunk (dview, merge_file, from1, from2, to2, FROM_RIGHT_TO_LEFT); - else - dview_remove_hunk (dview, merge_file, from1, to1, FROM_LEFT_TO_RIGHT); - break; - case DIFF_ADD: - if (merge_direction == FROM_RIGHT_TO_LEFT) - dview_remove_hunk (dview, merge_file, from1, to1, FROM_RIGHT_TO_LEFT); - else - dview_add_hunk (dview, merge_file, from1, from2, to2, FROM_LEFT_TO_RIGHT); - break; - case DIFF_CHG: - dview_replace_hunk (dview, merge_file, from1, to1, from2, to2, merge_direction); - break; - } - fflush (merge_file); - fclose (merge_file); - { - int res; - - res = rewrite_backup_content (merge_file_name_vpath, dview->file[n_merge]); - (void) res; - } - mc_unlink (merge_file_name_vpath); - vfs_path_free (merge_file_name_vpath); - } -} - /* --------------------------------------------------------------------------------------------- */ /* view routines and callbacks ********************************************** */ -static void -dview_compute_split (WDiff * dview, int i) -{ - dview->bias += i; - if (dview->bias < 2 - dview->half1) - dview->bias = 2 - dview->half1; - if (dview->bias > dview->half2 - 2) - dview->bias = dview->half2 - 2; -} - -/* --------------------------------------------------------------------------------------------- */ - static void dview_compute_areas (WDiff * dview) { @@ -2275,113 +79,7 @@ dview_compute_areas (WDiff * dview) dview->half1 = COLS / 2; dview->half2 = COLS - dview->half1; - dview_compute_split (dview, 0); -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_reread (WDiff * dview) -{ - int ndiff; - - destroy_hdiff (dview); - if (dview->a[DIFF_LEFT] != NULL) - { - g_array_foreach (dview->a[DIFF_LEFT], DIFFLN, cc_free_elt); - g_array_free (dview->a[DIFF_LEFT], TRUE); - } - if (dview->a[DIFF_RIGHT] != NULL) - { - g_array_foreach (dview->a[DIFF_RIGHT], DIFFLN, cc_free_elt); - g_array_free (dview->a[DIFF_RIGHT], TRUE); - } - - dview->a[DIFF_LEFT] = g_array_new (FALSE, FALSE, sizeof (DIFFLN)); - dview->a[DIFF_RIGHT] = g_array_new (FALSE, FALSE, sizeof (DIFFLN)); - - ndiff = redo_diff (dview); - if (ndiff >= 0) - dview->ndiff = ndiff; -} - -/* --------------------------------------------------------------------------------------------- */ - -#ifdef HAVE_CHARSET -static void -dview_set_codeset (WDiff * dview) -{ - const char *encoding_id = NULL; - - dview->utf8 = TRUE; - encoding_id = - get_codepage_id (mc_global.source_codepage >= - 0 ? mc_global.source_codepage : mc_global.display_codepage); - if (encoding_id != NULL) - { - GIConv conv; - - conv = str_crt_conv_from (encoding_id); - if (conv != INVALID_CONV) - { - if (dview->converter != str_cnv_from_term) - str_close_conv (dview->converter); - dview->converter = conv; - } - dview->utf8 = (gboolean) str_isutf8 (encoding_id); - } -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_select_encoding (WDiff * dview) -{ - if (do_select_codepage ()) - dview_set_codeset (dview); - dview_reread (dview); - tty_touch_screen (); - repaint_screen (); -} -#endif /* HAVE_CHARSET */ - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_diff_options (WDiff * dview) -{ - const char *quality_str[] = { - N_("No&rmal"), - N_("&Fastest (Assume large files)"), - N_("&Minimal (Find a smaller set of change)") - }; - - quick_widget_t quick_widgets[] = { - /* *INDENT-OFF* */ - QUICK_START_GROUPBOX (N_("Diff algorithm")), - QUICK_RADIO (3, (const char **) quality_str, (int *) &dview->opt.quality, NULL), - QUICK_STOP_GROUPBOX, - QUICK_START_GROUPBOX (N_("Diff extra options")), - QUICK_CHECKBOX (N_("&Ignore case"), &dview->opt.ignore_case, NULL), - QUICK_CHECKBOX (N_("Ignore tab &expansion"), &dview->opt.ignore_tab_expansion, NULL), - QUICK_CHECKBOX (N_("Ignore &space change"), &dview->opt.ignore_space_change, NULL), - QUICK_CHECKBOX (N_("Ignore all &whitespace"), &dview->opt.ignore_all_space, NULL), - QUICK_CHECKBOX (N_("Strip &trailing carriage return"), &dview->opt.strip_trailing_cr, - NULL), - QUICK_STOP_GROUPBOX, - QUICK_BUTTONS_OK_CANCEL, - QUICK_END - /* *INDENT-ON* */ - }; - - quick_dialog_t qdlg = { - -1, -1, 56, - N_("Diff Options"), "[Diff Options]", - quick_widgets, NULL, NULL - }; - - if (quick_dialog (&qdlg) != B_CANCEL) - dview_reread (dview); + mc_diffviewer_compute_split (dview, 0); } /* --------------------------------------------------------------------------------------------- */ @@ -2398,27 +96,27 @@ dview_init (WDiff * dview, const char *args, const char *file1, const char *file if (dsrc == DATA_SRC_TMP) { - f[DIFF_LEFT] = f_temp (); + f[DIFF_LEFT] = mc_diffviewer_file_temp (); if (f[DIFF_LEFT] == NULL) return -1; - f[DIFF_RIGHT] = f_temp (); + f[DIFF_RIGHT] = mc_diffviewer_file_temp (); if (f[DIFF_RIGHT] == NULL) { - f_close (f[DIFF_LEFT]); + mc_diffviewer_file_close (f[DIFF_LEFT]); return -1; } } else if (dsrc == DATA_SRC_ORG) { - f[DIFF_LEFT] = f_open (file1, O_RDONLY); + f[DIFF_LEFT] = mc_diffviewer_file_open (file1, O_RDONLY); if (f[DIFF_LEFT] == NULL) return -1; - f[DIFF_RIGHT] = f_open (file2, O_RDONLY); + f[DIFF_RIGHT] = mc_diffviewer_file_open (file2, O_RDONLY); if (f[DIFF_RIGHT] == NULL) { - f_close (f[DIFF_LEFT]); + mc_diffviewer_file_close (f[DIFF_LEFT]); return -1; } } @@ -2436,17 +134,17 @@ dview_init (WDiff * dview, const char *args, const char *file1, const char *file dview->dsrc = dsrc; #ifdef HAVE_CHARSET dview->converter = str_cnv_from_term; - dview_set_codeset (dview); + mc_diffviewer_set_codeset (dview); #endif dview->a[DIFF_LEFT] = g_array_new (FALSE, FALSE, sizeof (DIFFLN)); dview->a[DIFF_RIGHT] = g_array_new (FALSE, FALSE, sizeof (DIFFLN)); - ndiff = redo_diff (dview); + ndiff = mc_diffviewer_redo_diff (dview); if (ndiff < 0) { - /* goto MSG_DESTROY stage: dview_fini() */ - f_close (f[DIFF_LEFT]); - f_close (f[DIFF_RIGHT]); + /* goto MSG_DESTROY stage: mc_diffviewer_deinit() */ + mc_diffviewer_file_close (f[DIFF_LEFT]); + mc_diffviewer_file_close (f[DIFF_RIGHT]); return -1; } @@ -2484,475 +182,6 @@ dview_init (WDiff * dview, const char *args, const char *file1, const char *file /* --------------------------------------------------------------------------------------------- */ -static void -dview_fini (WDiff * dview) -{ - if (dview->dsrc != DATA_SRC_MEM) - { - f_close (dview->f[DIFF_RIGHT]); - f_close (dview->f[DIFF_LEFT]); - } - -#if HAVE_CHARSET - if (dview->converter != str_cnv_from_term) - str_close_conv (dview->converter); -#endif - - destroy_hdiff (dview); - if (dview->a[DIFF_LEFT] != NULL) - { - g_array_foreach (dview->a[DIFF_LEFT], DIFFLN, cc_free_elt); - g_array_free (dview->a[DIFF_LEFT], TRUE); - dview->a[DIFF_LEFT] = NULL; - } - if (dview->a[DIFF_RIGHT] != NULL) - { - g_array_foreach (dview->a[DIFF_RIGHT], DIFFLN, cc_free_elt); - g_array_free (dview->a[DIFF_RIGHT], TRUE); - dview->a[DIFF_RIGHT] = NULL; - } - - g_free (dview->label[DIFF_LEFT]); - g_free (dview->label[DIFF_RIGHT]); -} - -/* --------------------------------------------------------------------------------------------- */ - -static int -dview_display_file (const WDiff * dview, diff_place_t ord, int r, int c, int height, int width) -{ - size_t i, k; - int j; - char buf[BUFSIZ]; - FBUF *f = dview->f[ord]; - int skip = dview->skip_cols; - int display_symbols = dview->display_symbols; - int display_numbers = dview->display_numbers; - int show_cr = dview->show_cr; - int tab_size = 8; - const DIFFLN *p; - int nwidth = display_numbers; - int xwidth; - - xwidth = display_symbols + display_numbers; - if (dview->tab_size > 0 && dview->tab_size < 9) - tab_size = dview->tab_size; - - if (xwidth != 0) - { - 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 = dview->skip_rows, j = 0; i < dview->a[ord]->len && j < height; j++, i++) - { - int ch, next_ch, col; - size_t cnt; - - p = (DIFFLN *) & g_array_index (dview->a[ord], DIFFLN, i); - ch = p->ch; - tty_setcolor (NORMAL_COLOR); - if (display_symbols) - { - tty_gotoyx (r + j, c - 2); - tty_print_char (ch); - } - if (p->line != 0) - { - if (display_numbers) - { - tty_gotoyx (r + j, c - xwidth); - g_snprintf (buf, display_numbers + 1, "%*d", nwidth, p->line); - tty_print_string (str_fit_to_term (buf, nwidth, J_LEFT_FIT)); - } - if (ch == ADD_CH) - tty_setcolor (DFF_ADD_COLOR); - if (ch == CHG_CH) - tty_setcolor (DFF_CHG_COLOR); - if (f == NULL) - { - if (i == (size_t) dview->search.last_found_line) - tty_setcolor (MARKED_SELECTED_COLOR); - else if (dview->hdiff != NULL && g_ptr_array_index (dview->hdiff, i) != NULL) - { - char att[BUFSIZ]; - -#ifdef HAVE_CHARSET - if (dview->utf8) - k = dview_str_utf8_offset_to_pos (p->p, width); - else -#endif - k = width; - - cvt_mgeta (p->p, p->u.len, buf, k, skip, tab_size, show_cr, - g_ptr_array_index (dview->hdiff, i), ord, att); - tty_gotoyx (r + j, c); - col = 0; - - for (cnt = 0; cnt < strlen (buf) && col < width; cnt++) - { - gboolean ch_res; - -#ifdef HAVE_CHARSET - if (dview->utf8) - { - int ch_len; - - next_ch = dview_get_utf (buf + cnt, &ch_len, &ch_res); - if (ch_len > 1) - cnt += ch_len - 1; - if (!g_unichar_isprint (next_ch)) - next_ch = '.'; - } - else -#endif - next_ch = dview_get_byte (buf + cnt, &ch_res); - - if (ch_res) - { - tty_setcolor (att[cnt] ? DFF_CHH_COLOR : DFF_CHG_COLOR); -#ifdef HAVE_CHARSET - if (mc_global.utf8_display) - { - if (!dview->utf8) - { - next_ch = - convert_from_8bit_to_utf_c ((unsigned char) next_ch, - dview->converter); - } - } - else if (dview->utf8) - next_ch = convert_from_utf_to_current_c (next_ch, dview->converter); - else - next_ch = convert_to_display_c (next_ch); -#endif - tty_print_anychar (next_ch); - col++; - } - } - continue; - } - - if (ch == CHG_CH) - tty_setcolor (DFF_CHH_COLOR); - -#ifdef HAVE_CHARSET - if (dview->utf8) - k = dview_str_utf8_offset_to_pos (p->p, width); - else -#endif - k = width; - cvt_mget (p->p, p->u.len, buf, k, skip, tab_size, show_cr); - } - else - cvt_fget (f, p->u.off, buf, width, skip, tab_size, show_cr); - } - else - { - if (display_numbers) - { - tty_gotoyx (r + j, c - xwidth); - memset (buf, ' ', display_numbers); - buf[display_numbers] = '\0'; - tty_print_string (buf); - } - if (ch == DEL_CH) - tty_setcolor (DFF_DEL_COLOR); - if (ch == CHG_CH) - tty_setcolor (DFF_CHD_COLOR); - memset (buf, ' ', width); - buf[width] = '\0'; - } - tty_gotoyx (r + j, c); - /* tty_print_nstring (buf, width); */ - col = 0; - for (cnt = 0; cnt < strlen (buf) && col < width; cnt++) - { - gboolean ch_res; - -#ifdef HAVE_CHARSET - if (dview->utf8) - { - int ch_len; - - next_ch = dview_get_utf (buf + cnt, &ch_len, &ch_res); - if (ch_len > 1) - cnt += ch_len - 1; - if (!g_unichar_isprint (next_ch)) - next_ch = '.'; - } - else -#endif - next_ch = dview_get_byte (buf + cnt, &ch_res); - if (ch_res) - { -#ifdef HAVE_CHARSET - if (mc_global.utf8_display) - { - if (!dview->utf8) - { - next_ch = - convert_from_8bit_to_utf_c ((unsigned char) next_ch, dview->converter); - } - } - else if (dview->utf8) - next_ch = convert_from_utf_to_current_c (next_ch, dview->converter); - else - next_ch = convert_to_display_c (next_ch); -#endif - - tty_print_anychar (next_ch); - col++; - } - } - } - tty_setcolor (NORMAL_COLOR); - k = width; - if (width < xwidth - 1) - k = xwidth - 1; - memset (buf, ' ', k); - buf[k] = '\0'; - for (; j < height; j++) - { - if (xwidth != 0) - { - tty_gotoyx (r + j, c - xwidth); - /* tty_print_nstring (buf, xwidth - 1); */ - tty_print_string (str_fit_to_term (buf, xwidth - 1, J_LEFT_FIT)); - } - tty_gotoyx (r + j, c); - /* tty_print_nstring (buf, width); */ - tty_print_string (str_fit_to_term (buf, width, J_LEFT_FIT)); - } - - return 0; -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_status (const WDiff * dview, diff_place_t ord, int width, int c) -{ - const char *buf; - int filename_width; - int linenum, lineofs; - vfs_path_t *vpath; - char *path; - - tty_setcolor (STATUSBAR_COLOR); - - tty_gotoyx (0, c); - get_line_numbers (dview->a[ord], dview->skip_rows, &linenum, &lineofs); - - filename_width = width - 24; - if (filename_width < 8) - filename_width = 8; - - vpath = vfs_path_from_str (dview->label[ord]); - path = vfs_path_to_str_flags (vpath, 0, VPF_STRIP_HOME | VPF_STRIP_PASSWORD); - vfs_path_free (vpath); - buf = str_term_trim (path, filename_width); - if (ord == DIFF_LEFT) - tty_printf ("%s%-*s %6d+%-4d Col %-4d ", dview->merged[ord] ? "* " : " ", filename_width, - buf, linenum, lineofs, dview->skip_cols); - else - tty_printf ("%s%-*s %6d+%-4d Dif %-4d ", dview->merged[ord] ? "* " : " ", filename_width, - buf, linenum, lineofs, dview->ndiff); - g_free (path); -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_redo (WDiff * dview) -{ - if (dview->display_numbers) - { - int old; - - old = dview->display_numbers; - dview->display_numbers = calc_nwidth ((const GArray **) dview->a); - dview->new_frame = (old != dview->display_numbers); - } - dview_reread (dview); -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_update (WDiff * dview) -{ - int height = dview->height; - int width1; - int width2; - int last; - - last = dview->a[DIFF_LEFT]->len - 1; - - if (dview->skip_rows > last) - dview->skip_rows = dview->search.last_accessed_num_line = last; - if (dview->skip_rows < 0) - dview->skip_rows = dview->search.last_accessed_num_line = 0; - if (dview->skip_cols < 0) - dview->skip_cols = 0; - - if (height < 2) - return; - - width1 = dview->half1 + dview->bias; - width2 = dview->half2 - dview->bias; - if (dview->full) - { - width1 = COLS; - width2 = 0; - } - - if (dview->new_frame) - { - int xwidth; - - tty_setcolor (NORMAL_COLOR); - xwidth = dview->display_symbols + dview->display_numbers; - if (width1 > 1) - tty_draw_box (1, 0, height, width1, FALSE); - if (width2 > 1) - tty_draw_box (1, width1, height, width2, FALSE); - - if (xwidth != 0) - { - xwidth++; - if (xwidth < width1 - 1) - { - tty_gotoyx (1, xwidth); - tty_print_alt_char (ACS_TTEE, FALSE); - tty_gotoyx (height, xwidth); - tty_print_alt_char (ACS_BTEE, FALSE); - tty_draw_vline (2, xwidth, ACS_VLINE, height - 2); - } - if (xwidth < width2 - 1) - { - tty_gotoyx (1, width1 + xwidth); - tty_print_alt_char (ACS_TTEE, FALSE); - tty_gotoyx (height, width1 + xwidth); - tty_print_alt_char (ACS_BTEE, FALSE); - tty_draw_vline (2, width1 + xwidth, ACS_VLINE, height - 2); - } - } - dview->new_frame = 0; - } - - if (width1 > 2) - { - dview_status (dview, dview->ord, width1, 0); - dview_display_file (dview, dview->ord, 2, 1, height - 2, width1 - 2); - } - if (width2 > 2) - { - dview_status (dview, dview->ord ^ 1, width2, width1); - dview_display_file (dview, dview->ord ^ 1, 2, width1 + 1, height - 2, width2 - 2); - } -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_edit (WDiff * dview, diff_place_t ord) -{ - WDialog *h; - gboolean h_modal; - int linenum, lineofs; - - if (dview->dsrc == DATA_SRC_TMP) - { - error_dialog (_("Edit"), _("Edit is disabled")); - return; - } - - h = WIDGET (dview)->owner; - h_modal = h->modal; - - get_line_numbers (dview->a[ord], dview->skip_rows, &linenum, &lineofs); - h->modal = TRUE; /* not allow edit file in several editors */ - { - vfs_path_t *tmp_vpath; - - tmp_vpath = vfs_path_from_str (dview->file[ord]); - edit_file_at_line (tmp_vpath, use_internal_edit != 0, linenum); - vfs_path_free (tmp_vpath); - } - h->modal = h_modal; - dview_redo (dview); - dview_update (dview); -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_goto_cmd (WDiff * dview, diff_place_t ord) -{ - /* *INDENT-OFF* */ - static const char *title[2] = { - N_("Goto line (left)"), - N_("Goto line (right)") - }; - /* *INDENT-ON* */ - static char prev[256]; - /* XXX some statics here, to be remembered between runs */ - - int newline; - char *input; - - input = - input_dialog (_(title[ord]), _("Enter line:"), MC_HISTORY_YDIFF_GOTO_LINE, prev, - INPUT_COMPLETE_NONE); - if (input != NULL) - { - const char *s = input; - - if (scan_deci (&s, &newline) == 0 && *s == '\0') - { - size_t i = 0; - - if (newline > 0) - { - for (; i < dview->a[ord]->len; i++) - { - const DIFFLN *p; - - p = &g_array_index (dview->a[ord], DIFFLN, i); - if (p->line == newline) - break; - } - } - dview->skip_rows = dview->search.last_accessed_num_line = (ssize_t) i; - g_snprintf (prev, sizeof (prev), "%d", newline); - } - g_free (input); - } -} - -/* --------------------------------------------------------------------------------------------- */ - static void dview_labels (WDiff * dview) { @@ -2992,112 +221,24 @@ dview_event (Gpm_Event * event, void *data) { dview->skip_rows -= 2; dview->search.last_accessed_num_line = dview->skip_rows; - dview_update (dview); + mc_diffviewer_update (dview); } else if ((event->buttons & GPM_B_DOWN) != 0 && (event->type & GPM_DOWN) != 0) { dview->skip_rows += 2; dview->search.last_accessed_num_line = dview->skip_rows; - dview_update (dview); + mc_diffviewer_update (dview); } return MOU_NORMAL; } /* --------------------------------------------------------------------------------------------- */ - -static gboolean -dview_save (WDiff * dview) -{ - gboolean res = TRUE; - - if (dview->merged[DIFF_LEFT]) - { - res = mc_util_unlink_backup_if_possible (dview->file[DIFF_LEFT], "~~~"); - dview->merged[DIFF_LEFT] = !res; - } - if (dview->merged[DIFF_RIGHT]) - { - res = mc_util_unlink_backup_if_possible (dview->file[DIFF_RIGHT], "~~~"); - dview->merged[DIFF_RIGHT] = !res; - } - return res; -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_do_save (WDiff * dview) -{ - (void) dview_save (dview); -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_save_options (WDiff * dview) -{ - mc_config_set_bool (mc_main_config, "DiffView", "show_symbols", - dview->display_symbols != 0 ? TRUE : FALSE); - mc_config_set_bool (mc_main_config, "DiffView", "show_numbers", - dview->display_numbers != 0 ? TRUE : FALSE); - mc_config_set_int (mc_main_config, "DiffView", "tab_size", dview->tab_size); - - mc_config_set_int (mc_main_config, "DiffView", "diff_quality", dview->opt.quality); - - mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_tws", - dview->opt.strip_trailing_cr); - mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_all_space", - dview->opt.ignore_all_space); - mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_space_change", - dview->opt.ignore_space_change); - mc_config_set_bool (mc_main_config, "DiffView", "diff_tab_expansion", - dview->opt.ignore_tab_expansion); - mc_config_set_bool (mc_main_config, "DiffView", "diff_ignore_case", dview->opt.ignore_case); -} - -/* --------------------------------------------------------------------------------------------- */ - -static void -dview_load_options (WDiff * dview) -{ - gboolean show_numbers, show_symbols; - int tab_size; - - show_symbols = mc_config_get_bool (mc_main_config, "DiffView", "show_symbols", FALSE); - if (show_symbols) - dview->display_symbols = 1; - show_numbers = mc_config_get_bool (mc_main_config, "DiffView", "show_numbers", FALSE); - if (show_numbers) - dview->display_numbers = calc_nwidth ((const GArray ** const) dview->a); - tab_size = mc_config_get_int (mc_main_config, "DiffView", "tab_size", 8); - if (tab_size > 0 && tab_size < 9) - dview->tab_size = tab_size; - else - dview->tab_size = 8; - - dview->opt.quality = mc_config_get_int (mc_main_config, "DiffView", "diff_quality", 0); - - dview->opt.strip_trailing_cr = - mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_tws", FALSE); - dview->opt.ignore_all_space = - mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_all_space", FALSE); - dview->opt.ignore_space_change = - mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_space_change", FALSE); - dview->opt.ignore_tab_expansion = - mc_config_get_bool (mc_main_config, "DiffView", "diff_tab_expansion", FALSE); - dview->opt.ignore_case = - mc_config_get_bool (mc_main_config, "DiffView", "diff_ignore_case", FALSE); - - dview->new_frame = 1; -} - -/* --------------------------------------------------------------------------------------------- */ - /* * Check if it's OK to close the diff viewer. If there are unsaved changes, * ask user. */ + static gboolean dview_ok_to_exit (WDiff * dview) { @@ -3122,7 +263,7 @@ dview_ok_to_exit (WDiff * dview) res = FALSE; break; case 0: /* Yes */ - (void) dview_save (dview); + mc_event_raise (MCEVENT_GROUP_DIFFVIEWER, "save_changes", dview); res = TRUE; break; case 1: /* No */ @@ -3143,162 +284,134 @@ dview_ok_to_exit (WDiff * dview) static cb_ret_t dview_execute_cmd (WDiff * dview, unsigned long command) { - cb_ret_t res = MSG_HANDLED; + cb_ret_t res = MSG_NOT_HANDLED; + const char *event_name = NULL; switch (command) { case CK_ShowSymbols: - dview->display_symbols ^= 1; - dview->new_frame = 1; + event_name = "show_symbols"; break; case CK_ShowNumbers: - dview->display_numbers ^= calc_nwidth ((const GArray ** const) dview->a); - dview->new_frame = 1; + event_name = "show_numbers"; break; case CK_SplitFull: - dview->full ^= 1; - dview->new_frame = 1; + event_name = "split_full"; break; case CK_SplitEqual: - if (!dview->full) - { - dview->bias = 0; - dview->new_frame = 1; - } + event_name = "split_equal"; break; case CK_SplitMore: - if (!dview->full) - { - dview_compute_split (dview, 1); - dview->new_frame = 1; - } + event_name = "split_more"; break; - case CK_SplitLess: - if (!dview->full) - { - dview_compute_split (dview, -1); - dview->new_frame = 1; - } + event_name = "split_less"; break; case CK_Tab2: - dview->tab_size = 2; + event_name = "tab_size_2"; break; case CK_Tab3: - dview->tab_size = 3; + event_name = "tab_size_3"; break; case CK_Tab4: - dview->tab_size = 4; + event_name = "tab_size_4"; break; case CK_Tab8: - dview->tab_size = 8; + event_name = "tab_size_8"; break; case CK_Swap: - dview->ord ^= 1; + event_name = "swap"; break; case CK_Redo: - dview_redo (dview); + event_name = "redo"; break; case CK_HunkNext: - dview->skip_rows = dview->search.last_accessed_num_line = - find_next_hunk (dview->a[DIFF_LEFT], dview->skip_rows); + event_name = "hunk_next"; break; case CK_HunkPrev: - dview->skip_rows = dview->search.last_accessed_num_line = - find_prev_hunk (dview->a[DIFF_LEFT], dview->skip_rows); + event_name = "hunk_prev"; break; case CK_Goto: - dview_goto_cmd (dview, TRUE); + event_name = "goto_line"; break; case CK_Edit: - dview_edit (dview, dview->ord); + event_name = "edit_current"; break; case CK_Merge: - do_merge_hunk (dview, FROM_LEFT_TO_RIGHT); - dview_redo (dview); + event_name = "merge_from_left_to_right"; break; case CK_MergeOther: - do_merge_hunk (dview, FROM_RIGHT_TO_LEFT); - dview_redo (dview); + event_name = "merge_from_right_to_left"; break; case CK_EditOther: - dview_edit (dview, dview->ord ^ 1); + event_name = "edit_other"; break; case CK_Search: - dview_search_cmd (dview); + event_name = "search"; break; case CK_SearchContinue: - dview_continue_search_cmd (dview); + event_name = "continue_search"; break; case CK_Top: - dview->skip_rows = dview->search.last_accessed_num_line = 0; + event_name = "goto_top"; break; case CK_Bottom: - dview->skip_rows = dview->search.last_accessed_num_line = dview->a[DIFF_LEFT]->len - 1; + event_name = "goto_bottom"; break; case CK_Up: - if (dview->skip_rows > 0) - { - dview->skip_rows--; - dview->search.last_accessed_num_line = dview->skip_rows; - } + event_name = "goto_up"; break; case CK_Down: - dview->skip_rows++; - dview->search.last_accessed_num_line = dview->skip_rows; + event_name = "goto_down"; break; case CK_PageDown: - if (dview->height > 2) - { - dview->skip_rows += dview->height - 2; - dview->search.last_accessed_num_line = dview->skip_rows; - } + event_name = "goto_page_down"; break; case CK_PageUp: - if (dview->height > 2) - { - dview->skip_rows -= dview->height - 2; - dview->search.last_accessed_num_line = dview->skip_rows; - } + event_name = "goto_page_up"; break; case CK_Left: - dview->skip_cols--; + event_name = "goto_left"; break; case CK_Right: - dview->skip_cols++; + event_name = "goto_right"; break; case CK_LeftQuick: - dview->skip_cols -= 8; + event_name = "goto_left_quick"; break; case CK_RightQuick: - dview->skip_cols += 8; + event_name = "goto_right_quick"; break; case CK_Home: - dview->skip_cols = 0; + event_name = "goto_start_of_line"; break; case CK_Shell: view_other_cmd (); + res = MSG_HANDLED; break; case CK_Quit: - dview->view_quit = 1; + event_name = "quit"; break; case CK_Save: - dview_do_save (dview); + event_name = "save_changes"; break; case CK_Options: - dview_diff_options (dview); + event_name = "options_show_dialog"; break; #ifdef HAVE_CHARSET case CK_SelectCodepage: - dview_select_encoding (dview); + event_name = "select_encoding_show_dialog"; break; #endif case CK_Cancel: /* don't close diffviewer due to SIGINT */ break; - default: - res = MSG_NOT_HANDLED; + default:; } + + if (event_name != NULL && mc_event_raise (MCEVENT_GROUP_DIFFVIEWER, event_name, dview)) + res = MSG_HANDLED; + return res; } @@ -3334,13 +447,13 @@ dview_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d { case MSG_INIT: dview_labels (dview); - dview_load_options (dview); - dview_update (dview); + mc_event_raise (MCEVENT_GROUP_DIFFVIEWER, "options_load", dview); + mc_diffviewer_update (dview); return MSG_HANDLED; case MSG_DRAW: dview->new_frame = 1; - dview_update (dview); + mc_diffviewer_update (dview); return MSG_HANDLED; case MSG_KEY: @@ -3348,7 +461,7 @@ dview_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d if (dview->view_quit) dlg_stop (h); else - dview_update (dview); + mc_diffviewer_update (dview); return i; case MSG_ACTION: @@ -3356,12 +469,12 @@ dview_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *d if (dview->view_quit) dlg_stop (h); else - dview_update (dview); + mc_diffviewer_update (dview); return i; case MSG_DESTROY: - dview_save_options (dview); - dview_fini (dview); + mc_event_raise (MCEVENT_GROUP_DIFFVIEWER, "options_save", dview); + mc_diffviewer_deinit (dview); return MSG_HANDLED; default: @@ -3492,9 +605,101 @@ diff_view (const char *file1, const char *file2, const char *label1, const char return error == 0 ? 1 : 0; } +/* --------------------------------------------------------------------------------------------- */ + +static vfs_path_t * +mc_diffviewer_get_fname_from_wpanel (WPanel * panel) +{ + if (S_ISDIR (selection (panel)->st.st_mode)) + { + message (D_ERROR, MSG_ERROR, _("\"%s\" is a directory"), + path_trunc (selection (panel)->fname, 30)); + return NULL; + } + return vfs_path_append_new (panel->cwd_vpath, selection (panel)->fname, NULL); +} + +/* --------------------------------------------------------------------------------------------- */ + +static vfs_path_t * +mc_diffviewer_get_fname_from_string (const char *fname) +{ + struct stat st; + vfs_path_t *file; + + file = vfs_path_from_str (fname); + if (mc_stat (file, &st) == 0) + { + if (S_ISDIR (st.st_mode)) + { + message (D_ERROR, MSG_ERROR, _("\"%s\" is a directory"), path_trunc (fname, 30)); + vfs_path_free (file); + return NULL; + } + } + else + { + message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"), + path_trunc (fname, 30), unix_error_string (errno)); + vfs_path_free (file); + return NULL; + } + return file; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +mc_diffviewer_get_fnames_of_diffs (ev_diffviewer_run_t * info, vfs_path_t ** file0, + vfs_path_t ** file1) +{ + switch (info->run_mode) + { + case MC_RUN_FULL: + /* run from panels */ + *file0 = mc_diffviewer_get_fname_from_wpanel (info->data.panel.first); + if (*file0 == NULL) + return FALSE; + + *file1 = mc_diffviewer_get_fname_from_wpanel (info->data.panel.second); + if (*file1 == NULL) + return FALSE; + + break; + + case MC_RUN_DIFFVIEWER: + /* run from command line */ + *file0 = mc_diffviewer_get_fname_from_string (info->data.file.first); + if (*file0 == NULL) + return FALSE; + + *file1 = mc_diffviewer_get_fname_from_string (info->data.file.second); + if (*file1 == NULL) + return FALSE; + + break; + + default: + /* this should not happaned */ + message (D_ERROR, MSG_ERROR, _("Diff viewer: invalid mode")); + return FALSE; + } + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ +void +mc_diffviewer_init (GError ** error) +{ + mc_diffviewer_init_events (error); +} + +/* --------------------------------------------------------------------------------------------- */ +/* event callback */ + #define GET_FILE_AND_STAMP(n) \ do \ { \ @@ -3532,124 +737,54 @@ do \ } \ while (0) + gboolean -dview_diff_cmd (const void *f0, const void *f1) +mc_diffviewer_cmd_run (const gchar * event_group_name, const gchar * event_name, + gpointer init_data, gpointer data) { + ev_diffviewer_run_t *info = (ev_diffviewer_run_t *) data; int rv = 0; vfs_path_t *file0 = NULL; vfs_path_t *file1 = NULL; - gboolean is_dir0 = FALSE; - gboolean is_dir1 = FALSE; - switch (mc_global.mc_run_mode) + (void) event_group_name; + (void) event_name; + (void) init_data; + + if (mc_diffviewer_get_fnames_of_diffs (info, &file0, &file1)) { - case MC_RUN_FULL: + if (rv == 0) { - /* run from panels */ - const WPanel *panel0 = (const WPanel *) f0; - const WPanel *panel1 = (const WPanel *) f1; - - file0 = vfs_path_append_new (panel0->cwd_vpath, selection (panel0)->fname, NULL); - is_dir0 = S_ISDIR (selection (panel0)->st.st_mode); - if (is_dir0) + rv = -1; + if (file0 != NULL && file1 != NULL) { - message (D_ERROR, MSG_ERROR, _("\"%s\" is a directory"), - path_trunc (selection (panel0)->fname, 30)); - goto ret; - } + int use_copy0; + int use_copy1; + struct stat st0; + struct stat st1; + vfs_path_t *real_file0; + vfs_path_t *real_file1; - file1 = vfs_path_append_new (panel1->cwd_vpath, selection (panel1)->fname, NULL); - is_dir1 = S_ISDIR (selection (panel1)->st.st_mode); - if (is_dir1) - { - message (D_ERROR, MSG_ERROR, _("\"%s\" is a directory"), - path_trunc (selection (panel1)->fname, 30)); - goto ret; + GET_FILE_AND_STAMP (0); + GET_FILE_AND_STAMP (1); + + if (real_file0 != NULL && real_file1 != NULL) + rv = diff_view (vfs_path_as_str (real_file0), vfs_path_as_str (real_file1), + vfs_path_as_str (file0), vfs_path_as_str (file1)); + + UNGET_FILE (1); + UNGET_FILE (0); } - break; } - case MC_RUN_DIFFVIEWER: - { - /* run from command line */ - const char *p0 = (const char *) f0; - const char *p1 = (const char *) f1; - struct stat st; - - file0 = vfs_path_from_str (p0); - if (mc_stat (file0, &st) == 0) - { - is_dir0 = S_ISDIR (st.st_mode); - if (is_dir0) - { - message (D_ERROR, MSG_ERROR, _("\"%s\" is a directory"), path_trunc (p0, 30)); - goto ret; - } - } - else - { - message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"), - path_trunc (p0, 30), unix_error_string (errno)); - goto ret; - } - - file1 = vfs_path_from_str (p1); - if (mc_stat (file1, &st) == 0) - { - is_dir1 = S_ISDIR (st.st_mode); - if (is_dir1) - { - message (D_ERROR, MSG_ERROR, _("\"%s\" is a directory"), path_trunc (p1, 30)); - goto ret; - } - } - else - { - message (D_ERROR, MSG_ERROR, _("Cannot stat \"%s\"\n%s"), - path_trunc (p1, 30), unix_error_string (errno)); - goto ret; - } - break; - } - - default: - /* this should not happaned */ - message (D_ERROR, MSG_ERROR, _("Diff viewer: invalid mode")); - return FALSE; + if (rv == 0) + message (D_ERROR, MSG_ERROR, _("Two files are needed to compare")); } - - if (rv == 0) - { - rv = -1; - if (file0 != NULL && file1 != NULL) - { - int use_copy0; - int use_copy1; - struct stat st0; - struct stat st1; - vfs_path_t *real_file0; - vfs_path_t *real_file1; - - GET_FILE_AND_STAMP (0); - GET_FILE_AND_STAMP (1); - - if (real_file0 != NULL && real_file1 != NULL) - rv = diff_view (vfs_path_as_str (real_file0), vfs_path_as_str (real_file1), - vfs_path_as_str (file0), vfs_path_as_str (file1)); - - UNGET_FILE (1); - UNGET_FILE (0); - } - } - - if (rv == 0) - message (D_ERROR, MSG_ERROR, _("Two files are needed to compare")); - - ret: vfs_path_free (file1); vfs_path_free (file0); - return (rv != 0); + info->ret_value = (rv != 0); + return TRUE; } #undef GET_FILE_AND_STAMP diff --git a/src/diffviewer/ydiff.h b/src/diffviewer/ydiff.h index 90462c019..2164472e9 100644 --- a/src/diffviewer/ydiff.h +++ b/src/diffviewer/ydiff.h @@ -1,16 +1,40 @@ #ifndef MC__DIFFVIEW_YDIFF_H #define MC__DIFFVIEW_YDIFF_H +#include "src/filemanager/panel.h" + /*** typedefs(not structures) and defined constants **********************************************/ /*** enums ***************************************************************************************/ /*** structures declarations (and typedefs of structures)*****************************************/ +typedef struct +{ + mc_run_mode_t run_mode; + gboolean ret_value; + + union + { + struct + { + WPanel *first; + WPanel *second; + } panel; + struct + { + const char *first; + const char *second; + } file; + } data; +} ev_diffviewer_run_t; + /*** global variables defined in .c file *********************************************************/ /*** declarations of public functions ************************************************************/ -gboolean dview_diff_cmd (const void *f0, const void *f1); +void mc_diffviewer_init (GError ** error); + +/*** inline functions ****************************************************************************/ #endif /* MC__DIFFVIEW_YDIFF_H */ diff --git a/src/filemanager/cmd.c b/src/filemanager/cmd.c index 031fb10ef..da6bb24e4 100644 --- a/src/filemanager/cmd.c +++ b/src/filemanager/cmd.c @@ -1181,14 +1181,17 @@ compare_dirs_cmd (void) void diff_view_cmd (void) { + ev_diffviewer_run_t event_info; + /* both panels must be in the list mode */ if (get_current_type () != view_listing || get_other_type () != view_listing) return; - if (get_current_index () == 0) - dview_diff_cmd (current_panel, other_panel); - else - dview_diff_cmd (other_panel, current_panel); + event_info.run_mode = mc_global.mc_run_mode; + event_info.data.panel.first = current_panel; + event_info.data.panel.second = other_panel; + + mc_event_raise (MCEVENT_GROUP_DIFFVIEWER, "run", &event_info); if (mc_global.mc_run_mode == MC_RUN_FULL) update_panels (UP_OPTIMIZE, UP_KEEPSEL); diff --git a/src/filemanager/midnight.c b/src/filemanager/midnight.c index 2a17ec927..cd62b8829 100644 --- a/src/filemanager/midnight.c +++ b/src/filemanager/midnight.c @@ -1007,7 +1007,16 @@ mc_maybe_editor_or_viewer (void) } #ifdef USE_DIFF_VIEW case MC_RUN_DIFFVIEWER: - ret = dview_diff_cmd (mc_run_param0, mc_run_param1); + { + ev_diffviewer_run_t event_info; + + event_info.run_mode = mc_global.mc_run_mode; + event_info.data.file.first = mc_run_param0; + event_info.data.file.second = mc_run_param1; + + mc_event_raise (MCEVENT_GROUP_DIFFVIEWER, "run", &event_info); + ret = event_info.ret_value; + } break; #endif /* USE_DIFF_VIEW */ default: @@ -1736,7 +1745,7 @@ quiet_quit_cmd (void) /** Run the main dialog that occupies the whole screen */ gboolean -do_nc (void) +do_nc (GError ** error) { gboolean ret; @@ -1744,6 +1753,10 @@ do_nc (void) edit_stack_init (); #endif +#ifdef USE_DIFF_VIEW + mc_diffviewer_init (error); +#endif + midnight_dlg = dlg_create (FALSE, 0, 0, LINES, COLS, dialog_colors, midnight_callback, midnight_event, "[main]", NULL, DLG_NONE); diff --git a/src/filemanager/midnight.h b/src/filemanager/midnight.h index 4ad362904..717445f5f 100644 --- a/src/filemanager/midnight.h +++ b/src/filemanager/midnight.h @@ -45,7 +45,7 @@ void load_hint (gboolean force); void change_panel (void); void save_cwds_stat (void); gboolean quiet_quit_cmd (void); -gboolean do_nc (void); +gboolean do_nc (GError **); /*** inline functions ****************************************************************************/ diff --git a/src/main.c b/src/main.c index a08a8f0b8..582cdc33c 100644 --- a/src/main.c +++ b/src/main.c @@ -415,7 +415,14 @@ main (int argc, char *argv[]) if (mc_global.midnight_shutdown) exit_code = EXIT_SUCCESS; else - exit_code = do_nc ()? EXIT_SUCCESS : EXIT_FAILURE; + exit_code = do_nc (&error) ? EXIT_SUCCESS : EXIT_FAILURE; + + if (error != NULL) + { + message (D_ERROR, _("Warning"), "%s", error->message); + g_error_free (error); + error = NULL; + } /* Save the tree store */ (void) tree_store_save ();