diff options
Diffstat (limited to '')
-rw-r--r-- | src/diffviewer/Makefile.am | 8 | ||||
-rw-r--r-- | src/diffviewer/Makefile.in | 739 | ||||
-rw-r--r-- | src/diffviewer/internal.h | 153 | ||||
-rw-r--r-- | src/diffviewer/search.c | 283 | ||||
-rw-r--r-- | src/diffviewer/ydiff.c | 3651 | ||||
-rw-r--r-- | src/diffviewer/ydiff.h | 16 |
6 files changed, 4850 insertions, 0 deletions
diff --git a/src/diffviewer/Makefile.am b/src/diffviewer/Makefile.am new file mode 100644 index 0000000..88c0cea --- /dev/null +++ b/src/diffviewer/Makefile.am @@ -0,0 +1,8 @@ +noinst_LTLIBRARIES = libdiffviewer.la + +libdiffviewer_la_SOURCES = \ + internal.h \ + search.c \ + ydiff.c ydiff.h + +AM_CPPFLAGS = -I$(top_srcdir) $(GLIB_CFLAGS) $(PCRE_CPPFLAGS) diff --git a/src/diffviewer/Makefile.in b/src/diffviewer/Makefile.in new file mode 100644 index 0000000..661acc5 --- /dev/null +++ b/src/diffviewer/Makefile.in @@ -0,0 +1,739 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/diffviewer +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/m4.include/gnulib/mode_t.m4 \ + $(top_srcdir)/m4.include/gnulib/stat-size.m4 \ + $(top_srcdir)/m4.include/gnulib/fstypename.m4 \ + $(top_srcdir)/m4.include/gnulib/fsusage.m4 \ + $(top_srcdir)/m4.include/gnulib/mountlist.m4 \ + $(top_srcdir)/m4.include/gnulib/windows-stat-inodes.m4 \ + $(top_srcdir)/m4.include/gnulib/sys_types_h.m4 \ + $(top_srcdir)/m4.include/ax_path_lib_pcre.m4 \ + $(top_srcdir)/m4.include/dx_doxygen.m4 \ + $(top_srcdir)/m4.include/ax_require_defined.m4 \ + $(top_srcdir)/m4.include/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4.include/ax_append_flag.m4 \ + $(top_srcdir)/m4.include/ax_append_compile_flags.m4 \ + $(top_srcdir)/m4.include/mc-cflags.m4 \ + $(top_srcdir)/m4.include/ax_gcc_func_attribute.m4 \ + $(top_srcdir)/m4.include/mc-check-search-type.m4 \ + $(top_srcdir)/m4.include/mc-get-fs-info.m4 \ + $(top_srcdir)/m4.include/mc-with-x.m4 \ + $(top_srcdir)/m4.include/mc-use-termcap.m4 \ + $(top_srcdir)/m4.include/mc-with-screen.m4 \ + $(top_srcdir)/m4.include/mc-with-screen-ncurses.m4 \ + $(top_srcdir)/m4.include/mc-with-screen-slang.m4 \ + $(top_srcdir)/m4.include/mc-with-internal-edit.m4 \ + $(top_srcdir)/m4.include/mc-subshell.m4 \ + $(top_srcdir)/m4.include/mc-background.m4 \ + $(top_srcdir)/m4.include/mc-ext2fs-attr.m4 \ + $(top_srcdir)/m4.include/mc-glib.m4 \ + $(top_srcdir)/m4.include/mc-vfs.m4 \ + $(top_srcdir)/m4.include/vfs/rpc.m4 \ + $(top_srcdir)/m4.include/vfs/socket.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-extfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-sfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-ftp.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-sftp.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-fish.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-undelfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-tarfs.m4 \ + $(top_srcdir)/m4.include/vfs/mc-vfs-cpiofs.m4 \ + $(top_srcdir)/m4.include/mc-version.m4 \ + $(top_srcdir)/m4.include/mc-tests.m4 \ + $(top_srcdir)/m4.include/mc-i18n.m4 \ + $(top_srcdir)/m4.include/mc-assert.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libdiffviewer_la_LIBADD = +am_libdiffviewer_la_OBJECTS = search.lo ydiff.lo +libdiffviewer_la_OBJECTS = $(am_libdiffviewer_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/config/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/search.Plo ./$(DEPDIR)/ydiff.Plo +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libdiffviewer_la_SOURCES) +DIST_SOURCES = $(libdiffviewer_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/config/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CHECK_CFLAGS = @CHECK_CFLAGS@ +CHECK_LIBS = @CHECK_LIBS@ +COM_ERR_CFLAGS = @COM_ERR_CFLAGS@ +COM_ERR_LIBS = @COM_ERR_LIBS@ +CP1251 = @CP1251@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOC_LINGUAS = @DOC_LINGUAS@ +DOXYGEN_PAPER_SIZE = @DOXYGEN_PAPER_SIZE@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DX_CONFIG = @DX_CONFIG@ +DX_DOCDIR = @DX_DOCDIR@ +DX_DOT = @DX_DOT@ +DX_DOXYGEN = @DX_DOXYGEN@ +DX_DVIPS = @DX_DVIPS@ +DX_EGREP = @DX_EGREP@ +DX_ENV = @DX_ENV@ +DX_FLAG_chi = @DX_FLAG_chi@ +DX_FLAG_chm = @DX_FLAG_chm@ +DX_FLAG_doc = @DX_FLAG_doc@ +DX_FLAG_dot = @DX_FLAG_dot@ +DX_FLAG_html = @DX_FLAG_html@ +DX_FLAG_man = @DX_FLAG_man@ +DX_FLAG_pdf = @DX_FLAG_pdf@ +DX_FLAG_ps = @DX_FLAG_ps@ +DX_FLAG_rtf = @DX_FLAG_rtf@ +DX_FLAG_xml = @DX_FLAG_xml@ +DX_HHC = @DX_HHC@ +DX_LATEX = @DX_LATEX@ +DX_MAKEINDEX = @DX_MAKEINDEX@ +DX_PDFLATEX = @DX_PDFLATEX@ +DX_PERL = @DX_PERL@ +DX_PROJECT = @DX_PROJECT@ +E2P_CFLAGS = @E2P_CFLAGS@ +E2P_LIBS = @E2P_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +EXT2FS_CFLAGS = @EXT2FS_CFLAGS@ +EXT2FS_LIBS = @EXT2FS_LIBS@ +EXTHELPERSDIR = @EXTHELPERSDIR@ +FGREP = @FGREP@ +FILECMD = @FILECMD@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_LIBS = @GLIB_LIBS@ +GMODULE_CFLAGS = @GMODULE_CFLAGS@ +GMODULE_LIBS = @GMODULE_LIBS@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +HAVE_FILECMD = @HAVE_FILECMD@ +HAVE_ZIPINFO = @HAVE_ZIPINFO@ +HAVE_nroff = @HAVE_nroff@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBMC_RELEASE = @LIBMC_RELEASE@ +LIBMC_VERSION = @LIBMC_VERSION@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSSH_CFLAGS = @LIBSSH_CFLAGS@ +LIBSSH_LIBS = @LIBSSH_LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANDOC = @MANDOC@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MAN_DATE = @MAN_DATE@ +MAN_FLAGS = @MAN_FLAGS@ +MAN_VERSION = @MAN_VERSION@ +MCLIBS = @MCLIBS@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PCRE_CPPFLAGS = @PCRE_CPPFLAGS@ +PCRE_LIBS = @PCRE_LIBS@ +PERL = @PERL@ +PERL_FOR_BUILD = @PERL_FOR_BUILD@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POSUB = @POSUB@ +PYTHON = @PYTHON@ +RANLIB = @RANLIB@ +RUBY = @RUBY@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SLANG_CFLAGS = @SLANG_CFLAGS@ +SLANG_LIBS = @SLANG_LIBS@ +STRIP = @STRIP@ +TESTS_LDFLAGS = @TESTS_LDFLAGS@ +UNZIP = @UNZIP@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +X11_WWW = @X11_WWW@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +XMKMF = @XMKMF@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +ZIP = @ZIP@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LTLIBRARIES = libdiffviewer.la +libdiffviewer_la_SOURCES = \ + internal.h \ + search.c \ + ydiff.c ydiff.h + +AM_CPPFLAGS = -I$(top_srcdir) $(GLIB_CFLAGS) $(PCRE_CPPFLAGS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/diffviewer/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/diffviewer/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libdiffviewer.la: $(libdiffviewer_la_OBJECTS) $(libdiffviewer_la_DEPENDENCIES) $(EXTRA_libdiffviewer_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libdiffviewer_la_OBJECTS) $(libdiffviewer_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ydiff.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/search.Plo + -rm -f ./$(DEPDIR)/ydiff.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/search.Plo + -rm -f ./$(DEPDIR)/ydiff.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/diffviewer/internal.h b/src/diffviewer/internal.h new file mode 100644 index 0000000..728d4b5 --- /dev/null +++ b/src/diffviewer/internal.h @@ -0,0 +1,153 @@ +#ifndef MC__DIFFVIEW_INTERNAL_H +#define MC__DIFFVIEW_INTERNAL_H + +#include "lib/global.h" +#include "lib/mcconfig.h" +#include "lib/search.h" +#include "lib/tty/color.h" +#include "lib/widget.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); +typedef int PAIR[2]; + +#define error_dialog(h, s) query_dialog(h, s, D_ERROR, 1, _("&Dismiss")) + +/*** enums ***************************************************************************************/ + +typedef enum +{ + DATA_SRC_MEM = 0, + DATA_SRC_TMP = 1, + DATA_SRC_ORG = 2 +} DSRC; + +typedef enum +{ + DIFF_LEFT = 0, + DIFF_RIGHT = 1, + DIFF_COUNT = 2 +} diff_place_t; + +typedef enum +{ + DIFF_NONE = 0, + DIFF_ADD = 1, + DIFF_DEL = 2, + DIFF_CHG = 3 +} DiffState; + +/*** 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; + int len; +} BRACKET[DIFF_COUNT]; + +typedef struct +{ + int ch; + int line; + union + { + off_t off; + size_t len; + } u; + void *p; +} DIFFLN; + +typedef struct +{ + FBUF *f; + GArray *a; + DSRC dsrc; +} PRINTER_CTX; + +typedef struct WDiff +{ + Widget widget; + + const char *args; /* Args passed to diff */ + const char *file[DIFF_COUNT]; /* filenames */ + char *label[DIFF_COUNT]; + FBUF *f[DIFF_COUNT]; + const char *backup_sufix; + gboolean merged[DIFF_COUNT]; + GArray *a[DIFF_COUNT]; + GPtrArray *hdiff; + int ndiff; /* number of hunks */ + DSRC dsrc; /* data source: memory or temporary file */ + + gboolean view_quit; /* Quit flag */ + + int height; + int half1; + int half2; + int width1; + int width2; + int bias; + gboolean new_frame; + int skip_rows; + int skip_cols; + int display_symbols; + int display_numbers; + gboolean show_cr; + int tab_size; + diff_place_t ord; + gboolean full; + +#ifdef HAVE_CHARSET + gboolean utf8; + /* converter for translation of text */ + GIConv converter; +#endif /* HAVE_CHARSET */ + + struct + { + int quality; + gboolean strip_trailing_cr; + gboolean ignore_tab_expansion; + gboolean ignore_space_change; + gboolean ignore_all_space; + gboolean ignore_case; + } opt; + + /* Search variables */ + struct + { + mc_search_t *handle; + gchar *last_string; + + ssize_t last_found_line; + ssize_t last_accessed_num_line; + } search; +} WDiff; + +/*** global variables defined in .c file *********************************************************/ + +/*** declarations of public functions ************************************************************/ + +/* search.c */ +void dview_search_cmd (WDiff * dview); +void dview_continue_search_cmd (WDiff * dview); + +#endif /* MC__DIFFVIEW_INTERNAL_H */ diff --git a/src/diffviewer/search.c b/src/diffviewer/search.c new file mode 100644 index 0000000..6521c98 --- /dev/null +++ b/src/diffviewer/search.c @@ -0,0 +1,283 @@ +/* + Search functions for diffviewer. + + Copyright (C) 2010-2022 + Free Software Foundation, Inc. + + Written by: + Slava Zanko <slavazanko@gmail.com>, 2010. + Andrew Borodin <aborodin@vmail.ru>, 2012-2022 + + 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 <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <stdio.h> + +#include "lib/global.h" +#include "lib/strutil.h" +#include "lib/tty/key.h" +#include "lib/widget.h" +#ifdef HAVE_CHARSET +#include "lib/charsets.h" +#endif + +#include "src/history.h" + +#include "internal.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +typedef struct mcdiffview_search_options_struct +{ + mc_search_type_t type; + gboolean case_sens; + gboolean backwards; + gboolean whole_words; + gboolean all_codepages; +} mcdiffview_search_options_t; + +/*** file scope variables ************************************************************************/ + +static mcdiffview_search_options_t mcdiffview_search_options = { + .type = MC_SEARCH_T_NORMAL, + .case_sens = FALSE, + .backwards = FALSE, + .whole_words = FALSE, + .all_codepages = FALSE, +}; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +mcdiffview_dialog_search (WDiff * dview) +{ + char *exp = NULL; + int qd_result; + size_t num_of_types = 0; + gchar **list_of_types; + + list_of_types = mc_search_get_types_strings_array (&num_of_types); + + { + quick_widget_t quick_widgets[] = { + /* *INDENT-OFF* */ + QUICK_LABELED_INPUT (N_("Enter search string:"), input_label_above, INPUT_LAST_TEXT, + MC_HISTORY_SHARED_SEARCH, &exp, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE), + QUICK_SEPARATOR (TRUE), + QUICK_START_COLUMNS, + QUICK_RADIO (num_of_types, (const char **) list_of_types, + (int *) &mcdiffview_search_options.type, NULL), + QUICK_NEXT_COLUMN, + QUICK_CHECKBOX (N_("Cas&e sensitive"), &mcdiffview_search_options.case_sens, NULL), + QUICK_CHECKBOX (N_("&Backwards"), &mcdiffview_search_options.backwards, NULL), + QUICK_CHECKBOX (N_("&Whole words"), &mcdiffview_search_options.whole_words, NULL), +#ifdef HAVE_CHARSET + QUICK_CHECKBOX (N_("&All charsets"), &mcdiffview_search_options.all_codepages, NULL), +#endif + QUICK_STOP_COLUMNS, + QUICK_BUTTONS_OK_CANCEL, + QUICK_END + /* *INDENT-ON* */ + }; + + WRect r = { -1, -1, 0, 58 }; + + quick_dialog_t qdlg = { + r, N_("Search"), "[Input Line Keys]", + quick_widgets, NULL, NULL + }; + + qd_result = quick_dialog (&qdlg); + } + + g_strfreev (list_of_types); + + if (qd_result == B_CANCEL || exp[0] == '\0') + { + g_free (exp); + return FALSE; + } + +#ifdef HAVE_CHARSET + { + GString *tmp; + + tmp = str_convert_to_input (exp); + g_free (exp); + exp = g_string_free (tmp, FALSE); + } +#endif + + g_free (dview->search.last_string); + dview->search.last_string = exp; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +mcdiffview_do_search_backward (WDiff * dview) +{ + ssize_t ind; + + if (dview->search.last_accessed_num_line < 0) + { + dview->search.last_accessed_num_line = -1; + return FALSE; + } + + if ((size_t) dview->search.last_accessed_num_line >= dview->a[dview->ord]->len) + dview->search.last_accessed_num_line = (ssize_t) dview->a[dview->ord]->len; + + for (ind = --dview->search.last_accessed_num_line; ind >= 0; ind--) + { + DIFFLN *p; + + p = (DIFFLN *) & g_array_index (dview->a[dview->ord], DIFFLN, (size_t) ind); + if (p->u.len == 0) + continue; + + if (mc_search_run (dview->search.handle, p->p, 0, p->u.len, NULL)) + { + dview->skip_rows = dview->search.last_found_line = + dview->search.last_accessed_num_line = ind; + return TRUE; + } + } + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + + +static gboolean +mcdiffview_do_search_forward (WDiff * dview) +{ + size_t ind; + + if (dview->search.last_accessed_num_line < 0) + dview->search.last_accessed_num_line = -1; + else if ((size_t) dview->search.last_accessed_num_line >= dview->a[dview->ord]->len) + { + dview->search.last_accessed_num_line = (ssize_t) dview->a[dview->ord]->len; + return FALSE; + } + + for (ind = (size_t)++ dview->search.last_accessed_num_line; ind < dview->a[dview->ord]->len; + ind++) + { + DIFFLN *p; + + p = (DIFFLN *) & g_array_index (dview->a[dview->ord], DIFFLN, ind); + if (p->u.len == 0) + continue; + + if (mc_search_run (dview->search.handle, p->p, 0, p->u.len, NULL)) + { + dview->skip_rows = dview->search.last_found_line = + dview->search.last_accessed_num_line = (ssize_t) ind; + return TRUE; + } + } + return FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +mcdiffview_do_search (WDiff * dview) +{ + gboolean present_result = FALSE; + + tty_enable_interrupt_key (); + + if (mcdiffview_search_options.backwards) + { + present_result = mcdiffview_do_search_backward (dview); + } + else + { + present_result = mcdiffview_do_search_forward (dview); + } + + tty_disable_interrupt_key (); + + if (!present_result) + { + dview->search.last_found_line = -1; + query_dialog (_("Search"), _(STR_E_NOTFOUND), D_NORMAL, 1, _("&Dismiss")); + } +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +dview_search_cmd (WDiff * dview) +{ + if (dview->dsrc != DATA_SRC_MEM) + { + error_dialog (_("Search"), _("Search is disabled")); + return; + } + + if (!mcdiffview_dialog_search (dview)) + return; + + mc_search_free (dview->search.handle); +#ifdef HAVE_CHARSET + dview->search.handle = mc_search_new (dview->search.last_string, cp_source); +#else + dview->search.handle = mc_search_new (dview->search.last_string, NULL); +#endif + + if (dview->search.handle == NULL) + return; + + dview->search.handle->search_type = mcdiffview_search_options.type; +#ifdef HAVE_CHARSET + dview->search.handle->is_all_charsets = mcdiffview_search_options.all_codepages; +#endif + dview->search.handle->is_case_sensitive = mcdiffview_search_options.case_sens; + dview->search.handle->whole_words = mcdiffview_search_options.whole_words; + + mcdiffview_do_search (dview); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +dview_continue_search_cmd (WDiff * dview) +{ + if (dview->dsrc != DATA_SRC_MEM) + error_dialog (_("Search"), _("Search is disabled")); + else if (dview->search.handle == NULL) + dview_search_cmd (dview); + else + mcdiffview_do_search (dview); +} + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/diffviewer/ydiff.c b/src/diffviewer/ydiff.c new file mode 100644 index 0000000..afe1dc1 --- /dev/null +++ b/src/diffviewer/ydiff.c @@ -0,0 +1,3651 @@ +/* + File difference viewer + + Copyright (C) 2007-2022 + Free Software Foundation, Inc. + + Written by: + Daniel Borca <dborca@yahoo.com>, 2007 + Slava Zanko <slavazanko@gmail.com>, 2010, 2013 + Andrew Borodin <aborodin@vmail.ru>, 2010-2022 + Ilia Maslakov <il.smind@gmail.com>, 2010 + + 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 <http://www.gnu.org/licenses/>. + */ + + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <stddef.h> /* ptrdiff_t */ +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include "lib/global.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 +#include "lib/event.h" /* mc_event_raise() */ + +#include "src/filemanager/cmd.h" /* edit_file_at_line() */ +#include "src/filemanager/panel.h" +#include "src/filemanager/layout.h" /* Needed for get_current_index and get_other_panel */ + +#include "src/execute.h" /* toggle_subshell() */ +#include "src/keymap.h" +#include "src/setup.h" +#include "src/history.h" +#ifdef HAVE_CHARSET +#include "src/selcodepage.h" +#endif + +#include "ydiff.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 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 = vfs_path_free (diff_file_name, FALSE); + 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 str ... + * @param ch ... + * @return TRUE on success, FALSE otherwise + */ + +static gboolean +dview_get_byte (const char *str, int *ch) +{ + if (str == NULL) + return FALSE; + + *ch = (unsigned char) (*str); + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +#ifdef HAVE_CHARSET +/** + * Get utf multibyte char from string + * + * @param str ... + * @param ch ... + * @param ch_length ... + * @return TRUE on success, FALSE otherwise + */ + +static gboolean +dview_get_utf (const char *str, int *ch, int *ch_length) +{ + if (str == NULL) + return FALSE; + + *ch = g_utf8_get_char_validated (str, -1); + + if (*ch < 0) + { + *ch = (unsigned char) (*str); + *ch_length = 1; + } + else + { + char *next_ch; + + /* Calculate UTF-8 char length */ + next_ch = g_utf8_next_char (str); + *ch_length = next_ch - str; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +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 (*Lcurr)); +#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, + gboolean 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 + int ch = 0; + int ch_length = 1; + + (void) dview_get_utf (src, &ch, &ch_length); + + if (ch_length > 1) + skip += ch_length - 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, + gboolean 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 + int ch = 0; + int ch_length = 1; + + (void) dview_get_utf (src, &ch, &ch_length); + if (ch_length > 1) + skip += ch_length - 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, gboolean 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; + default: + 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; + default: + 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, TRUE); + } +} + +/* --------------------------------------------------------------------------------------------- */ +/* 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) +{ + Widget *w = WIDGET (dview); + + dview->height = w->rect.lines - 1; + dview->half1 = w->rect.cols / 2; + dview->half2 = w->rect.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* */ + }; + + WRect r = { -1, -1, 0, 56 }; + + quick_dialog_t qdlg = { + r, N_("Diff Options"), "[Diff Options]", + quick_widgets, NULL, NULL + }; + + if (quick_dialog (&qdlg) != B_CANCEL) + dview_reread (dview); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +dview_init (WDiff * dview, const char *args, const char *file1, const char *file2, + const char *label1, const char *label2, DSRC dsrc) +{ + int ndiff; + FBUF *f[DIFF_COUNT]; + + f[DIFF_LEFT] = NULL; + f[DIFF_RIGHT] = NULL; + + if (dsrc == DATA_SRC_TMP) + { + f[DIFF_LEFT] = f_temp (); + if (f[DIFF_LEFT] == NULL) + return -1; + + f[DIFF_RIGHT] = f_temp (); + if (f[DIFF_RIGHT] == NULL) + { + f_close (f[DIFF_LEFT]); + return -1; + } + } + else if (dsrc == DATA_SRC_ORG) + { + f[DIFF_LEFT] = f_open (file1, O_RDONLY); + if (f[DIFF_LEFT] == NULL) + return -1; + + f[DIFF_RIGHT] = f_open (file2, O_RDONLY); + if (f[DIFF_RIGHT] == NULL) + { + f_close (f[DIFF_LEFT]); + return -1; + } + } + + dview->args = args; + dview->file[DIFF_LEFT] = file1; + dview->file[DIFF_RIGHT] = file2; + dview->label[DIFF_LEFT] = g_strdup (label1); + dview->label[DIFF_RIGHT] = g_strdup (label2); + dview->f[DIFF_LEFT] = f[0]; + dview->f[DIFF_RIGHT] = f[1]; + dview->merged[DIFF_LEFT] = FALSE; + dview->merged[DIFF_RIGHT] = FALSE; + dview->hdiff = NULL; + dview->dsrc = dsrc; +#ifdef HAVE_CHARSET + dview->converter = str_cnv_from_term; + dview_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); + if (ndiff < 0) + { + /* goto MSG_DESTROY stage: dview_fini() */ + f_close (f[DIFF_LEFT]); + f_close (f[DIFF_RIGHT]); + return -1; + } + + dview->ndiff = ndiff; + + dview->view_quit = FALSE; + + dview->bias = 0; + dview->new_frame = TRUE; + dview->skip_rows = 0; + dview->skip_cols = 0; + dview->display_symbols = 0; + dview->display_numbers = 0; + dview->show_cr = TRUE; + dview->tab_size = 8; + dview->ord = DIFF_LEFT; + dview->full = FALSE; + + dview->search.handle = NULL; + dview->search.last_string = NULL; + dview->search.last_found_line = -1; + dview->search.last_accessed_num_line = -1; + + dview->opt.quality = 0; + dview->opt.strip_trailing_cr = 0; + dview->opt.ignore_tab_expansion = 0; + dview->opt.ignore_space_change = 0; + dview->opt.ignore_all_space = 0; + dview->opt.ignore_case = 0; + + dview_compute_areas (dview); + + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +dview_fini (WDiff * dview) +{ + if (dview->dsrc != DATA_SRC_MEM) + { + f_close (dview->f[DIFF_RIGHT]); + f_close (dview->f[DIFF_LEFT]); + } + +#ifdef 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; + gboolean 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 = 0, 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_length = 0; + + ch_res = dview_get_utf (buf + cnt, &next_ch, &ch_length); + if (ch_length > 1) + cnt += ch_length - 1; + if (!g_unichar_isprint (next_ch)) + next_ch = '.'; + } + else +#endif + ch_res = dview_get_byte (buf + cnt, &next_ch); + + 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_length = 0; + + ch_res = dview_get_utf (buf + cnt, &next_ch, &ch_length); + if (ch_length > 1) + cnt += ch_length - 1; + if (!g_unichar_isprint (next_ch)) + next_ch = '.'; + } + else +#endif + ch_res = dview_get_byte (buf + cnt, &next_ch); + + 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, TRUE); + 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 * const *) 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 = FALSE; + } + + 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) +{ + Widget *h; + gboolean h_modal; + int linenum, lineofs; + + if (dview->dsrc == DATA_SRC_TMP) + { + error_dialog (_("Edit"), _("Edit is disabled")); + return; + } + + h = WIDGET (WIDGET (dview)->owner); + h_modal = widget_get_state (h, WST_MODAL); + + get_line_numbers (dview->a[ord], dview->skip_rows, &linenum, &lineofs); + + /* disallow edit file in several editors */ + widget_set_state (h, WST_MODAL, TRUE); + + { + vfs_path_t *tmp_vpath; + + tmp_vpath = vfs_path_from_str (dview->file[ord]); + edit_file_at_line (tmp_vpath, use_internal_edit, linenum); + vfs_path_free (tmp_vpath, TRUE); + } + + widget_set_state (h, WST_MODAL, h_modal); + dview_redo (dview); + dview_update (dview); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +dview_goto_cmd (WDiff * dview, diff_place_t ord) +{ + static gboolean first_run = TRUE; + + /* *INDENT-OFF* */ + static const char *title[2] = { + N_("Goto line (left)"), + N_("Goto line (right)") + }; + /* *INDENT-ON* */ + + int newline; + char *input; + + input = + input_dialog (_(title[ord]), _("Enter line:"), MC_HISTORY_YDIFF_GOTO_LINE, + first_run ? NULL : INPUT_LAST_TEXT, 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_free (input); + } + + first_run = FALSE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +dview_labels (WDiff * dview) +{ + Widget *d = WIDGET (dview); + WButtonBar *b; + + b = find_buttonbar (DIALOG (d->owner)); + + buttonbar_set_label (b, 1, Q_ ("ButtonBar|Help"), d->keymap, d); + buttonbar_set_label (b, 2, Q_ ("ButtonBar|Save"), d->keymap, d); + buttonbar_set_label (b, 4, Q_ ("ButtonBar|Edit"), d->keymap, d); + buttonbar_set_label (b, 5, Q_ ("ButtonBar|Merge"), d->keymap, d); + buttonbar_set_label (b, 7, Q_ ("ButtonBar|Search"), d->keymap, d); + buttonbar_set_label (b, 9, Q_ ("ButtonBar|Options"), d->keymap, d); + buttonbar_set_label (b, 10, Q_ ("ButtonBar|Quit"), d->keymap, d); +} + +/* --------------------------------------------------------------------------------------------- */ + +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_global.main_config, "DiffView", "show_symbols", + dview->display_symbols != 0); + mc_config_set_bool (mc_global.main_config, "DiffView", "show_numbers", + dview->display_numbers != 0); + mc_config_set_int (mc_global.main_config, "DiffView", "tab_size", dview->tab_size); + + mc_config_set_int (mc_global.main_config, "DiffView", "diff_quality", dview->opt.quality); + + mc_config_set_bool (mc_global.main_config, "DiffView", "diff_ignore_tws", + dview->opt.strip_trailing_cr); + mc_config_set_bool (mc_global.main_config, "DiffView", "diff_ignore_all_space", + dview->opt.ignore_all_space); + mc_config_set_bool (mc_global.main_config, "DiffView", "diff_ignore_space_change", + dview->opt.ignore_space_change); + mc_config_set_bool (mc_global.main_config, "DiffView", "diff_tab_expansion", + dview->opt.ignore_tab_expansion); + mc_config_set_bool (mc_global.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_global.main_config, "DiffView", "show_symbols", FALSE); + if (show_symbols) + dview->display_symbols = 1; + show_numbers = mc_config_get_bool (mc_global.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_global.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_global.main_config, "DiffView", "diff_quality", 0); + + dview->opt.strip_trailing_cr = + mc_config_get_bool (mc_global.main_config, "DiffView", "diff_ignore_tws", FALSE); + dview->opt.ignore_all_space = + mc_config_get_bool (mc_global.main_config, "DiffView", "diff_ignore_all_space", FALSE); + dview->opt.ignore_space_change = + mc_config_get_bool (mc_global.main_config, "DiffView", "diff_ignore_space_change", FALSE); + dview->opt.ignore_tab_expansion = + mc_config_get_bool (mc_global.main_config, "DiffView", "diff_tab_expansion", FALSE); + dview->opt.ignore_case = + mc_config_get_bool (mc_global.main_config, "DiffView", "diff_ignore_case", FALSE); + + dview->new_frame = TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/* + * 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) +{ + gboolean res = TRUE; + int act; + + if (!dview->merged[DIFF_LEFT] && !dview->merged[DIFF_RIGHT]) + return res; + + act = query_dialog (_("Quit"), !mc_global.midnight_shutdown ? + _("File(s) was modified. Save with exit?") : + _("Midnight Commander is being shut down.\nSave modified file(s)?"), + D_NORMAL, 2, _("&Yes"), _("&No")); + + /* Esc is No */ + if (mc_global.midnight_shutdown || (act == -1)) + act = 1; + + switch (act) + { + case -1: /* Esc */ + res = FALSE; + break; + case 0: /* Yes */ + (void) dview_save (dview); + res = TRUE; + break; + case 1: /* No */ + if (mc_util_restore_from_backup_if_possible (dview->file[DIFF_LEFT], "~~~")) + res = mc_util_unlink_backup_if_possible (dview->file[DIFF_LEFT], "~~~"); + if (mc_util_restore_from_backup_if_possible (dview->file[DIFF_RIGHT], "~~~")) + res = mc_util_unlink_backup_if_possible (dview->file[DIFF_RIGHT], "~~~"); + MC_FALLTHROUGH; + default: + res = TRUE; + break; + } + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +dview_execute_cmd (WDiff * dview, long command) +{ + cb_ret_t res = MSG_HANDLED; + + switch (command) + { + case CK_ShowSymbols: + dview->display_symbols ^= 1; + dview->new_frame = TRUE; + break; + case CK_ShowNumbers: + dview->display_numbers ^= calc_nwidth ((const GArray * const *) dview->a); + dview->new_frame = TRUE; + break; + case CK_SplitFull: + dview->full = !dview->full; + dview->new_frame = TRUE; + break; + case CK_SplitEqual: + if (!dview->full) + { + dview->bias = 0; + dview->new_frame = TRUE; + } + break; + case CK_SplitMore: + if (!dview->full) + { + dview_compute_split (dview, 1); + dview->new_frame = TRUE; + } + break; + + case CK_SplitLess: + if (!dview->full) + { + dview_compute_split (dview, -1); + dview->new_frame = TRUE; + } + break; + case CK_Tab2: + dview->tab_size = 2; + break; + case CK_Tab3: + dview->tab_size = 3; + break; + case CK_Tab4: + dview->tab_size = 4; + break; + case CK_Tab8: + dview->tab_size = 8; + break; + case CK_Swap: + dview->ord ^= 1; + break; + case CK_Redo: + dview_redo (dview); + break; + case CK_HunkNext: + dview->skip_rows = dview->search.last_accessed_num_line = + find_next_hunk (dview->a[DIFF_LEFT], dview->skip_rows); + break; + case CK_HunkPrev: + dview->skip_rows = dview->search.last_accessed_num_line = + find_prev_hunk (dview->a[DIFF_LEFT], dview->skip_rows); + break; + case CK_Goto: + dview_goto_cmd (dview, DIFF_RIGHT); + break; + case CK_Edit: + dview_edit (dview, dview->ord); + break; + case CK_Merge: + do_merge_hunk (dview, FROM_LEFT_TO_RIGHT); + dview_redo (dview); + break; + case CK_MergeOther: + do_merge_hunk (dview, FROM_RIGHT_TO_LEFT); + dview_redo (dview); + break; + case CK_EditOther: + dview_edit (dview, dview->ord ^ 1); + break; + case CK_Search: + dview_search_cmd (dview); + break; + case CK_SearchContinue: + dview_continue_search_cmd (dview); + break; + case CK_Top: + dview->skip_rows = dview->search.last_accessed_num_line = 0; + break; + case CK_Bottom: + dview->skip_rows = dview->search.last_accessed_num_line = dview->a[DIFF_LEFT]->len - 1; + break; + case CK_Up: + if (dview->skip_rows > 0) + { + dview->skip_rows--; + dview->search.last_accessed_num_line = dview->skip_rows; + } + break; + case CK_Down: + dview->skip_rows++; + dview->search.last_accessed_num_line = dview->skip_rows; + break; + case CK_PageDown: + if (dview->height > 2) + { + dview->skip_rows += dview->height - 2; + dview->search.last_accessed_num_line = dview->skip_rows; + } + break; + case CK_PageUp: + if (dview->height > 2) + { + dview->skip_rows -= dview->height - 2; + dview->search.last_accessed_num_line = dview->skip_rows; + } + break; + case CK_Left: + dview->skip_cols--; + break; + case CK_Right: + dview->skip_cols++; + break; + case CK_LeftQuick: + dview->skip_cols -= 8; + break; + case CK_RightQuick: + dview->skip_cols += 8; + break; + case CK_Home: + dview->skip_cols = 0; + break; + case CK_Shell: + toggle_subshell (); + break; + case CK_Quit: + dview->view_quit = TRUE; + break; + case CK_Save: + dview_do_save (dview); + break; + case CK_Options: + dview_diff_options (dview); + break; +#ifdef HAVE_CHARSET + case CK_SelectCodepage: + dview_select_encoding (dview); + break; +#endif + case CK_Cancel: + /* don't close diffviewer due to SIGINT */ + break; + default: + res = MSG_NOT_HANDLED; + } + return res; +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +dview_handle_key (WDiff * dview, int key) +{ + long command; + +#ifdef HAVE_CHARSET + key = convert_from_input_c (key); +#endif + + command = widget_lookup_key (WIDGET (dview), key); + if (command == CK_IgnoreKey) + return MSG_NOT_HANDLED; + + return dview_execute_cmd (dview, command); +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +dview_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) +{ + WDiff *dview = (WDiff *) w; + WDialog *h = DIALOG (w->owner); + cb_ret_t i; + + switch (msg) + { + case MSG_INIT: + dview_labels (dview); + dview_load_options (dview); + dview_update (dview); + return MSG_HANDLED; + + case MSG_DRAW: + dview->new_frame = TRUE; + dview_update (dview); + return MSG_HANDLED; + + case MSG_KEY: + i = dview_handle_key (dview, parm); + if (dview->view_quit) + dlg_stop (h); + else + dview_update (dview); + return i; + + case MSG_ACTION: + i = dview_execute_cmd (dview, parm); + if (dview->view_quit) + dlg_stop (h); + else + dview_update (dview); + return i; + + case MSG_RESIZE: + widget_default_callback (w, NULL, MSG_RESIZE, 0, data); + dview_compute_areas (dview); + return MSG_HANDLED; + + case MSG_DESTROY: + dview_save_options (dview); + dview_fini (dview); + return MSG_HANDLED; + + default: + return widget_default_callback (w, sender, msg, parm, data); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +dview_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) +{ + WDiff *dview = (WDiff *) w; + + (void) event; + + switch (msg) + { + case MSG_MOUSE_SCROLL_UP: + case MSG_MOUSE_SCROLL_DOWN: + if (msg == MSG_MOUSE_SCROLL_UP) + dview->skip_rows -= 2; + else + dview->skip_rows += 2; + + dview->search.last_accessed_num_line = dview->skip_rows; + dview_update (dview); + break; + + default: + break; + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static cb_ret_t +dview_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data) +{ + WDiff *dview; + WDialog *h = DIALOG (w); + + switch (msg) + { + case MSG_ACTION: + /* Handle shortcuts. */ + + /* Note: the buttonbar sends messages directly to the the WDiff, not to + * here, which is why we can pass NULL in the following call. */ + return dview_execute_cmd (NULL, parm); + + case MSG_VALIDATE: + dview = (WDiff *) widget_find_by_type (CONST_WIDGET (h), dview_callback); + /* don't stop the dialog before final decision */ + widget_set_state (w, WST_ACTIVE, TRUE); + if (dview_ok_to_exit (dview)) + dlg_stop (h); + return MSG_HANDLED; + + default: + return dlg_default_callback (w, sender, msg, parm, data); + } +} + +/* --------------------------------------------------------------------------------------------- */ + +static char * +dview_get_title (const WDialog * h, size_t len) +{ + const WDiff *dview; + const char *modified = " (*) "; + const char *notmodified = " "; + size_t len1; + GString *title; + + dview = (const WDiff *) widget_find_by_type (CONST_WIDGET (h), dview_callback); + len1 = (len - str_term_width1 (_("Diff:")) - strlen (modified) - 3) / 2; + + title = g_string_sized_new (len); + g_string_append (title, _("Diff:")); + g_string_append (title, dview->merged[DIFF_LEFT] ? modified : notmodified); + g_string_append (title, str_term_trim (dview->label[DIFF_LEFT], len1)); + g_string_append (title, " | "); + g_string_append (title, dview->merged[DIFF_RIGHT] ? modified : notmodified); + g_string_append (title, str_term_trim (dview->label[DIFF_RIGHT], len1)); + + return g_string_free (title, FALSE); +} + +/* --------------------------------------------------------------------------------------------- */ + +static int +diff_view (const char *file1, const char *file2, const char *label1, const char *label2) +{ + int error; + WDiff *dview; + Widget *w; + WDialog *dview_dlg; + Widget *dw; + WRect r; + WGroup *g; + + /* Create dialog and widgets, put them on the dialog */ + dview_dlg = + dlg_create (FALSE, 0, 0, 1, 1, WPOS_FULLSCREEN, FALSE, NULL, dview_dialog_callback, NULL, + "[Diff Viewer]", NULL); + dw = WIDGET (dview_dlg); + widget_want_tab (dw, TRUE); + r = dw->rect; + + g = GROUP (dview_dlg); + + dview = g_new0 (WDiff, 1); + w = WIDGET (dview); + r.lines--; + widget_init (w, &r, dview_callback, dview_mouse_callback); + w->options |= WOP_SELECTABLE; + w->keymap = diff_map; + group_add_widget_autopos (g, w, WPOS_KEEP_ALL, NULL); + + w = WIDGET (buttonbar_new ()); + group_add_widget_autopos (g, w, w->pos_flags, NULL); + + dview_dlg->get_title = dview_get_title; + + error = dview_init (dview, "-a", file1, file2, label1, label2, DATA_SRC_MEM); /* XXX binary diff? */ + + if (error == 0) + dlg_run (dview_dlg); + + if (error != 0 || widget_get_state (dw, WST_CLOSED)) + widget_destroy (dw); + + return error == 0 ? 1 : 0; +} + +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +#define GET_FILE_AND_STAMP(n) \ +do \ +{ \ + use_copy##n = 0; \ + real_file##n = file##n; \ + if (!vfs_file_is_local (file##n)) \ + { \ + real_file##n = mc_getlocalcopy (file##n); \ + if (real_file##n != NULL) \ + { \ + use_copy##n = 1; \ + if (mc_stat (real_file##n, &st##n) != 0) \ + use_copy##n = -1; \ + } \ + } \ +} \ +while (0) + +#define UNGET_FILE(n) \ +do \ +{ \ + if (use_copy##n) \ + { \ + int changed = 0; \ + if (use_copy##n > 0) \ + { \ + time_t mtime; \ + mtime = st##n.st_mtime; \ + if (mc_stat (real_file##n, &st##n) == 0) \ + changed = (mtime != st##n.st_mtime); \ + } \ + mc_ungetlocalcopy (file##n, real_file##n, changed); \ + vfs_path_free (real_file##n, TRUE); \ + } \ +} \ +while (0) + +gboolean +dview_diff_cmd (const void *f0, const void *f1) +{ + 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) + { + case MC_RUN_FULL: + { + /* 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->str, + (char *) NULL); + is_dir0 = S_ISDIR (selection (panel0)->st.st_mode); + if (is_dir0) + { + message (D_ERROR, MSG_ERROR, _("\"%s\" is a directory"), + path_trunc (selection (panel0)->fname->str, 30)); + goto ret; + } + + file1 = + vfs_path_append_new (panel1->cwd_vpath, selection (panel1)->fname->str, + (char *) 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->str, 30)); + goto ret; + } + 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) + { + 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, TRUE); + vfs_path_free (file0, TRUE); + + return (rv != 0); +} + +#undef GET_FILE_AND_STAMP +#undef UNGET_FILE + +/* --------------------------------------------------------------------------------------------- */ diff --git a/src/diffviewer/ydiff.h b/src/diffviewer/ydiff.h new file mode 100644 index 0000000..90462c0 --- /dev/null +++ b/src/diffviewer/ydiff.h @@ -0,0 +1,16 @@ +#ifndef MC__DIFFVIEW_YDIFF_H +#define MC__DIFFVIEW_YDIFF_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 dview_diff_cmd (const void *f0, const void *f1); + +#endif /* MC__DIFFVIEW_YDIFF_H */ |