summaryrefslogtreecommitdiffstats
path: root/src/vfs/extfs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:22:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:22:03 +0000
commitffccd5b2b05243e7976db80f90f453dccfae9886 (patch)
tree39a43152d27f7390d8f7a6fb276fa6887f87c6e8 /src/vfs/extfs
parentInitial commit. (diff)
downloadmc-ffccd5b2b05243e7976db80f90f453dccfae9886.tar.xz
mc-ffccd5b2b05243e7976db80f90f453dccfae9886.zip
Adding upstream version 3:4.8.30.upstream/3%4.8.30
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/vfs/extfs')
-rw-r--r--src/vfs/extfs/Makefile.am12
-rw-r--r--src/vfs/extfs/Makefile.in856
-rw-r--r--src/vfs/extfs/extfs.c1722
-rw-r--r--src/vfs/extfs/extfs.h18
-rw-r--r--src/vfs/extfs/helpers/Makefile.am76
-rw-r--r--src/vfs/extfs/helpers/Makefile.in812
-rw-r--r--src/vfs/extfs/helpers/README200
-rw-r--r--src/vfs/extfs/helpers/README.extfs78
-rw-r--r--src/vfs/extfs/helpers/a+.in126
-rw-r--r--src/vfs/extfs/helpers/apt+.in359
-rwxr-xr-xsrc/vfs/extfs/helpers/audio.in53
-rwxr-xr-xsrc/vfs/extfs/helpers/bpp50
-rwxr-xr-xsrc/vfs/extfs/helpers/changesetfs109
-rw-r--r--src/vfs/extfs/helpers/deb.in203
-rw-r--r--src/vfs/extfs/helpers/deba.in107
-rw-r--r--src/vfs/extfs/helpers/debd.in362
-rw-r--r--src/vfs/extfs/helpers/dpkg+.in337
-rwxr-xr-xsrc/vfs/extfs/helpers/gitfs+39
-rw-r--r--src/vfs/extfs/helpers/hp48+.in132
-rw-r--r--src/vfs/extfs/helpers/iso9660.in235
-rw-r--r--src/vfs/extfs/helpers/lslR.in74
-rw-r--r--src/vfs/extfs/helpers/mailfs.in219
-rw-r--r--src/vfs/extfs/helpers/patchfs.in427
-rwxr-xr-xsrc/vfs/extfs/helpers/patchsetfs104
-rwxr-xr-xsrc/vfs/extfs/helpers/rpm349
-rw-r--r--src/vfs/extfs/helpers/rpms+.in66
-rw-r--r--src/vfs/extfs/helpers/s3+.in490
-rwxr-xr-xsrc/vfs/extfs/helpers/trpm176
-rwxr-xr-xsrc/vfs/extfs/helpers/u7z135
-rw-r--r--src/vfs/extfs/helpers/uace.in67
-rw-r--r--src/vfs/extfs/helpers/ualz.in68
-rw-r--r--src/vfs/extfs/helpers/uar.in60
-rw-r--r--src/vfs/extfs/helpers/uarc.in92
-rw-r--r--src/vfs/extfs/helpers/uarj.in75
-rwxr-xr-xsrc/vfs/extfs/helpers/uc1541702
-rw-r--r--src/vfs/extfs/helpers/ucab.in40
-rw-r--r--src/vfs/extfs/helpers/uha.in52
-rw-r--r--src/vfs/extfs/helpers/ulha.in142
-rw-r--r--src/vfs/extfs/helpers/ulib.in146
-rw-r--r--src/vfs/extfs/helpers/unar.in59
-rw-r--r--src/vfs/extfs/helpers/urar.in180
-rw-r--r--src/vfs/extfs/helpers/uwim.in208
-rw-r--r--src/vfs/extfs/helpers/uzip.in483
-rw-r--r--src/vfs/extfs/helpers/uzoo.in69
44 files changed, 10369 insertions, 0 deletions
diff --git a/src/vfs/extfs/Makefile.am b/src/vfs/extfs/Makefile.am
new file mode 100644
index 0000000..39762a3
--- /dev/null
+++ b/src/vfs/extfs/Makefile.am
@@ -0,0 +1,12 @@
+SUBDIRS = helpers
+DIST_SUBDIRS = helpers
+
+AM_CPPFLAGS = \
+ -DLIBEXECDIR=\""$(libexecdir)/@PACKAGE@/"\" \
+ $(GLIB_CFLAGS) \
+ -I$(top_srcdir)
+
+noinst_LTLIBRARIES = libvfs-extfs.la
+
+libvfs_extfs_la_SOURCES = \
+ extfs.c extfs.h
diff --git a/src/vfs/extfs/Makefile.in b/src/vfs/extfs/Makefile.in
new file mode 100644
index 0000000..317af30
--- /dev/null
+++ b/src/vfs/extfs/Makefile.in
@@ -0,0 +1,856 @@
+# 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/vfs/extfs
+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/ax_check_pcre2.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)
+libvfs_extfs_la_LIBADD =
+am_libvfs_extfs_la_OBJECTS = extfs.lo
+libvfs_extfs_la_OBJECTS = $(am_libvfs_extfs_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)/extfs.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 = $(libvfs_extfs_la_SOURCES)
+DIST_SOURCES = $(libvfs_extfs_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+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)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+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_CFLAGS = @PCRE_CFLAGS@
+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@
+SUBDIRS = helpers
+DIST_SUBDIRS = helpers
+AM_CPPFLAGS = \
+ -DLIBEXECDIR=\""$(libexecdir)/@PACKAGE@/"\" \
+ $(GLIB_CFLAGS) \
+ -I$(top_srcdir)
+
+noinst_LTLIBRARIES = libvfs-extfs.la
+libvfs_extfs_la_SOURCES = \
+ extfs.c extfs.h
+
+all: all-recursive
+
+.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/vfs/extfs/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/vfs/extfs/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}; \
+ }
+
+libvfs-extfs.la: $(libvfs_extfs_la_OBJECTS) $(libvfs_extfs_la_DEPENDENCIES) $(EXTRA_libvfs_extfs_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libvfs_extfs_la_OBJECTS) $(libvfs_extfs_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extfs.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
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(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-recursive
+
+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-recursive
+
+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
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LTLIBRARIES)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+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-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/extfs.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/extfs.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) 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 \
+ installdirs-am 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/vfs/extfs/extfs.c b/src/vfs/extfs/extfs.c
new file mode 100644
index 0000000..d6ef7af
--- /dev/null
+++ b/src/vfs/extfs/extfs.c
@@ -0,0 +1,1722 @@
+/*
+ Virtual File System: External file system.
+
+ Copyright (C) 1995-2023
+ Free Software Foundation, Inc.
+
+ Written by:
+ Jakub Jelinek, 1995
+ Pavel Machek, 1998
+ Andrew T. Veliath, 1999
+ Slava Zanko <slavazanko@gmail.com>, 2013
+
+ 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/>.
+ */
+
+/**
+ * \file
+ * \brief Source: Virtual File System: External file system
+ * \author Jakub Jelinek
+ * \author Pavel Machek
+ * \author Andrew T. Veliath
+ * \date 1995, 1998, 1999
+ */
+
+/* Namespace: init_extfs */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include "lib/global.h"
+#include "lib/fileloc.h"
+#include "lib/mcconfig.h"
+#include "lib/util.h"
+#include "lib/widget.h" /* message() */
+
+#include "src/execute.h" /* For shell_execute */
+
+#include "lib/vfs/vfs.h"
+#include "lib/vfs/utilvfs.h"
+#include "lib/vfs/xdirentry.h"
+#include "lib/vfs/gc.h" /* vfs_rmstamp */
+
+#include "extfs.h"
+
+/*** global variables ****************************************************************************/
+
+/*** file scope macro definitions ****************************************************************/
+
+#undef ERRNOR
+#define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
+
+#define RECORDSIZE 512
+
+#define EXTFS_SUPER(a) ((struct extfs_super_t *) (a))
+
+/*** file scope type declarations ****************************************************************/
+
+struct extfs_super_t
+{
+ struct vfs_s_super base; /* base class */
+
+ int fstype;
+ char *local_name;
+ struct stat local_stat;
+ dev_t rdev;
+};
+
+typedef struct
+{
+ char *path;
+ char *prefix;
+ gboolean need_archive;
+} extfs_plugin_info_t;
+
+/*** forward declarations (file scope functions) *************************************************/
+
+static struct vfs_s_entry *extfs_resolve_symlinks_int (struct vfs_s_entry *entry, GSList * list);
+
+/*** file scope variables ************************************************************************/
+
+static GArray *extfs_plugins = NULL;
+
+static gboolean errloop;
+static gboolean notadir;
+
+static struct vfs_s_subclass extfs_subclass;
+static struct vfs_class *vfs_extfs_ops = VFS_CLASS (&extfs_subclass);
+
+static int my_errno = 0;
+
+/* --------------------------------------------------------------------------------------------- */
+/*** file scope functions ************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+static struct extfs_super_t *
+extfs_super_new (struct vfs_class *me, const char *name, const vfs_path_t * local_name_vpath,
+ int fstype)
+{
+ struct extfs_super_t *super;
+ struct vfs_s_super *vsuper;
+
+ super = g_new0 (struct extfs_super_t, 1);
+ vsuper = VFS_SUPER (super);
+
+ vsuper->me = me;
+ vsuper->name = g_strdup (name);
+
+ super->fstype = fstype;
+
+ if (local_name_vpath != NULL)
+ {
+ super->local_name = g_strdup (vfs_path_get_last_path_str (local_name_vpath));
+ mc_stat (local_name_vpath, &super->local_stat);
+ }
+
+ VFS_SUBCLASS (me)->supers = g_list_prepend (VFS_SUBCLASS (me)->supers, super);
+
+ return super;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/* unlike vfs_s_new_entry(), inode->ent is kept */
+static struct vfs_s_entry *
+extfs_entry_new (struct vfs_class *me, const char *name, struct vfs_s_inode *inode)
+{
+ struct vfs_s_entry *entry;
+
+ (void) me;
+
+ entry = g_new0 (struct vfs_s_entry, 1);
+
+ entry->name = g_strdup (name);
+ entry->ino = inode;
+
+ return entry;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+extfs_fill_name (void *data, void *user_data)
+{
+ struct vfs_s_super *a = VFS_SUPER (data);
+ fill_names_f func = (fill_names_f) user_data;
+ extfs_plugin_info_t *info;
+ char *name;
+
+ info = &g_array_index (extfs_plugins, extfs_plugin_info_t, EXTFS_SUPER (a)->fstype);
+ name =
+ g_strconcat (a->name != NULL ? a->name : "", PATH_SEP_STR, info->prefix,
+ VFS_PATH_URL_DELIMITER, (char *) NULL);
+ func (name);
+ g_free (name);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static gint
+extfs_cmp_archive (const void *a, const void *b)
+{
+ const struct vfs_s_super *ar = (const struct vfs_s_super *) a;
+ const char *archive_name = (const char *) b;
+
+ return (ar->name != NULL && strcmp (ar->name, archive_name) == 0) ? 0 : 1;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static struct vfs_s_entry *
+extfs_generate_entry (struct extfs_super_t *archive, const char *name, struct vfs_s_inode *parent,
+ mode_t mode)
+{
+ struct vfs_class *me = VFS_SUPER (archive)->me;
+ struct stat st;
+ mode_t myumask;
+ struct vfs_s_inode *inode;
+ struct vfs_s_entry *entry;
+
+ memset (&st, 0, sizeof (st));
+ st.st_ino = VFS_SUPER (archive)->ino_usage++;
+ st.st_dev = archive->rdev;
+ myumask = umask (022);
+ umask (myumask);
+ st.st_mode = mode & ~myumask;
+ st.st_uid = getuid ();
+ st.st_gid = getgid ();
+ st.st_mtime = time (NULL);
+ st.st_atime = st.st_mtime;
+ st.st_ctime = st.st_mtime;
+ st.st_nlink = 1;
+
+ inode = vfs_s_new_inode (me, VFS_SUPER (archive), &st);
+ entry = vfs_s_new_entry (me, name, inode);
+ if (parent != NULL)
+ vfs_s_insert_entry (me, parent, entry);
+
+ return entry;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static struct vfs_s_entry *
+extfs_find_entry_int (struct vfs_s_inode *dir, const char *name, GSList * list, int flags)
+{
+ struct vfs_s_entry *pent, *pdir;
+ const char *p, *name_end;
+ char *q;
+ char c = PATH_SEP;
+ struct extfs_super_t *super;
+
+ if (g_path_is_absolute (name))
+ {
+ /* Handle absolute paths */
+ name = g_path_skip_root (name);
+ dir = dir->super->root;
+ }
+
+ super = EXTFS_SUPER (dir->super);
+ pent = dir->ent;
+ p = name;
+ name_end = name + strlen (name);
+
+ while ((pent != NULL) && (c != '\0') && (*p != '\0'))
+ {
+ q = strchr (p, PATH_SEP);
+ if (q == NULL)
+ q = (char *) name_end;
+
+ c = *q;
+ *q = '\0';
+
+ if (DIR_IS_DOTDOT (p))
+ pent = pent->dir != NULL ? pent->dir->ent : NULL;
+ else
+ {
+ GList *pl;
+
+ pent = extfs_resolve_symlinks_int (pent, list);
+ if (pent == NULL)
+ {
+ *q = c;
+ return NULL;
+ }
+
+ if (!S_ISDIR (pent->ino->st.st_mode))
+ {
+ *q = c;
+ notadir = TRUE;
+ return NULL;
+ }
+
+ pdir = pent;
+ pl = g_queue_find_custom (pent->ino->subdir, p, vfs_s_entry_compare);
+ pent = pl != NULL ? VFS_ENTRY (pl->data) : NULL;
+ if (pent != NULL && q + 1 > name_end)
+ {
+ /* Hack: I keep the original semanthic unless q+1 would break in the strchr */
+ *q = c;
+ notadir = !S_ISDIR (pent->ino->st.st_mode);
+ return pent;
+ }
+
+ /* When we load archive, we create automagically non-existent directories */
+ if (pent == NULL && (flags & FL_MKDIR) != 0)
+ pent = extfs_generate_entry (super, p, pdir->ino, S_IFDIR | 0777);
+ if (pent == NULL && (flags & FL_MKFILE) != 0)
+ pent = extfs_generate_entry (super, p, pdir->ino, S_IFREG | 0666);
+ }
+
+ /* Next iteration */
+ *q = c;
+ if (c != '\0')
+ p = q + 1;
+ }
+ if (pent == NULL)
+ my_errno = ENOENT;
+ return pent;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static struct vfs_s_entry *
+extfs_find_entry (struct vfs_s_inode *dir, const char *name, int flags)
+{
+ struct vfs_s_entry *res;
+
+ errloop = FALSE;
+ notadir = FALSE;
+
+ res = extfs_find_entry_int (dir, name, NULL, flags);
+ if (res == NULL)
+ {
+ if (errloop)
+ my_errno = ELOOP;
+ else if (notadir)
+ my_errno = ENOTDIR;
+ }
+ return res;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+extfs_fill_names (struct vfs_class *me, fill_names_f func)
+{
+ g_list_foreach (VFS_SUBCLASS (me)->supers, extfs_fill_name, func);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/* Create this function because VFSF_USETMP flag is not used in extfs */
+static void
+extfs_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
+{
+ (void) me;
+
+ if (ino->localname != NULL)
+ {
+ unlink (ino->localname);
+ MC_PTR_FREE (ino->localname);
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+extfs_free_archive (struct vfs_class *me, struct vfs_s_super *psup)
+{
+ struct extfs_super_t *archive = EXTFS_SUPER (psup);
+
+ (void) me;
+
+ if (archive->local_name != NULL)
+ {
+ struct stat my;
+ vfs_path_t *local_name_vpath, *name_vpath;
+
+ local_name_vpath = vfs_path_from_str (archive->local_name);
+ name_vpath = vfs_path_from_str (psup->name);
+ mc_stat (local_name_vpath, &my);
+ mc_ungetlocalcopy (name_vpath, local_name_vpath,
+ archive->local_stat.st_mtime != my.st_mtime);
+ vfs_path_free (local_name_vpath, TRUE);
+ vfs_path_free (name_vpath, TRUE);
+ g_free (archive->local_name);
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static inline char *
+extfs_skip_leading_dotslash (char *s)
+{
+ /* Skip leading "./" (if present).
+ * Some programs don't understand it:
+ *
+ * $ zip file.zip ./-file2.txt file1.txt
+ * adding: -file2.txt (stored 0%)
+ * adding: file1.txt (stored 0%)
+ * $ /usr/lib/mc/extfs.d/uzip copyout file.zip ./-file2.txt ./tmp-file2.txt
+ * caution: filename not matched: ./-file2.txt
+ */
+ if (s[0] == '.' && s[1] == PATH_SEP)
+ s += 2;
+
+ return s;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_add_file (struct extfs_super_t *archive, const char *file_name)
+{
+ struct vfs_s_super *super = VFS_SUPER (archive);
+ struct stat hstat;
+ char *current_file_name = NULL, *current_link_name = NULL;
+ int ret = 0;
+
+ if (vfs_parse_ls_lga (file_name, &hstat, &current_file_name, &current_link_name, NULL))
+ {
+ char *cfn = current_file_name;
+
+ if (*cfn != '\0')
+ {
+ struct vfs_s_entry *entry;
+ struct vfs_s_entry *pent = NULL;
+ struct vfs_s_inode *inode;
+ char *p, *q;
+
+ cfn = extfs_skip_leading_dotslash (cfn);
+ if (IS_PATH_SEP (*cfn))
+ cfn++;
+ p = strchr (cfn, '\0');
+ if (p != cfn && IS_PATH_SEP (p[-1]))
+ p[-1] = '\0';
+ p = strrchr (cfn, PATH_SEP);
+ if (p == NULL)
+ {
+ p = cfn;
+ q = strchr (cfn, '\0');
+ }
+ else
+ {
+ *(p++) = '\0';
+ q = cfn;
+ }
+
+ if (*q != '\0')
+ {
+ pent = extfs_find_entry (super->root, q, FL_MKDIR);
+ if (pent == NULL)
+ {
+ ret = -1;
+ goto done;
+ }
+ }
+
+ if (pent != NULL)
+ {
+ entry = extfs_entry_new (super->me, p, pent->ino);
+ entry->dir = pent->ino;
+ g_queue_push_tail (pent->ino->subdir, entry);
+ }
+ else
+ {
+ entry = extfs_entry_new (super->me, p, super->root);
+ entry->dir = super->root;
+ g_queue_push_tail (super->root->subdir, entry);
+ }
+
+ if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
+ {
+ pent = extfs_find_entry (super->root, current_link_name, FL_NONE);
+ if (pent == NULL)
+ {
+ ret = -1;
+ goto done;
+ }
+
+ pent->ino->st.st_nlink++;
+ entry->ino = pent->ino;
+ }
+ else
+ {
+ struct stat st;
+
+ memset (&st, 0, sizeof (st));
+ st.st_ino = super->ino_usage++;
+ st.st_nlink = 1;
+ st.st_dev = archive->rdev;
+ st.st_mode = hstat.st_mode;
+#ifdef HAVE_STRUCT_STAT_ST_RDEV
+ st.st_rdev = hstat.st_rdev;
+#endif
+ st.st_uid = hstat.st_uid;
+ st.st_gid = hstat.st_gid;
+ st.st_size = hstat.st_size;
+ st.st_mtime = hstat.st_mtime;
+ st.st_atime = hstat.st_atime;
+ st.st_ctime = hstat.st_ctime;
+
+ if (current_link_name == NULL && S_ISLNK (hstat.st_mode))
+ st.st_mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
+
+ inode = vfs_s_new_inode (super->me, super, &st);
+ inode->ent = entry;
+ entry->ino = inode;
+
+ if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
+ {
+ inode->linkname = current_link_name;
+ current_link_name = NULL;
+ }
+ }
+ }
+
+ done:
+ g_free (current_file_name);
+ g_free (current_link_name);
+ }
+
+ return ret;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static mc_pipe_t *
+extfs_open_archive (int fstype, const char *name, struct extfs_super_t **pparc, GError ** error)
+{
+ const extfs_plugin_info_t *info;
+ static dev_t archive_counter = 0;
+ mc_pipe_t *result = NULL;
+ mode_t mode;
+ char *cmd;
+ struct stat mystat;
+ struct extfs_super_t *current_archive;
+ struct vfs_s_entry *root_entry;
+ char *tmp = NULL;
+ vfs_path_t *local_name_vpath = NULL;
+ vfs_path_t *name_vpath;
+
+ memset (&mystat, 0, sizeof (mystat));
+
+ name_vpath = vfs_path_from_str (name);
+ info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
+
+ if (info->need_archive)
+ {
+ if (mc_stat (name_vpath, &mystat) == -1)
+ goto ret;
+
+ if (!vfs_file_is_local (name_vpath))
+ {
+ local_name_vpath = mc_getlocalcopy (name_vpath);
+ if (local_name_vpath == NULL)
+ goto ret;
+ }
+
+ tmp = name_quote (vfs_path_get_last_path_str (name_vpath), FALSE);
+ }
+
+ cmd = g_strconcat (info->path, info->prefix, " list ",
+ vfs_path_get_last_path_str (local_name_vpath) != NULL ?
+ vfs_path_get_last_path_str (local_name_vpath) : tmp, (char *) NULL);
+ g_free (tmp);
+
+ result = mc_popen (cmd, TRUE, TRUE, error);
+ g_free (cmd);
+
+ if (result == NULL)
+ {
+ if (local_name_vpath != NULL)
+ {
+ mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE);
+ vfs_path_free (local_name_vpath, TRUE);
+ }
+ goto ret;
+ }
+
+ current_archive = extfs_super_new (vfs_extfs_ops, name, local_name_vpath, fstype);
+ current_archive->rdev = archive_counter++;
+ vfs_path_free (local_name_vpath, TRUE);
+
+ mode = mystat.st_mode & 07777;
+ if (mode & 0400)
+ mode |= 0100;
+ if (mode & 0040)
+ mode |= 0010;
+ if (mode & 0004)
+ mode |= 0001;
+ mode |= S_IFDIR;
+
+ root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
+ root_entry->ino->st.st_uid = mystat.st_uid;
+ root_entry->ino->st.st_gid = mystat.st_gid;
+ root_entry->ino->st.st_atime = mystat.st_atime;
+ root_entry->ino->st.st_ctime = mystat.st_ctime;
+ root_entry->ino->st.st_mtime = mystat.st_mtime;
+ root_entry->ino->ent = root_entry;
+ VFS_SUPER (current_archive)->root = root_entry->ino;
+
+ *pparc = current_archive;
+
+ ret:
+ vfs_path_free (name_vpath, TRUE);
+ return result;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Main loop for reading an archive.
+ * Return 0 on success, -1 on error.
+ */
+
+static int
+extfs_read_archive (mc_pipe_t * pip, struct extfs_super_t *archive, GError ** error)
+{
+ int ret = 0;
+ GString *buffer;
+ GString *err_msg = NULL;
+ GString *remain_file_name = NULL;
+
+ while (ret != -1)
+ {
+ /* init buffers before call of mc_pread() */
+ pip->out.len = MC_PIPE_BUFSIZE;
+ pip->err.len = MC_PIPE_BUFSIZE;
+
+ mc_pread (pip, error);
+
+ if (*error != NULL)
+ return (-1);
+
+ if (pip->err.len > 0)
+ {
+ /* join errors/warnings */
+ if (err_msg == NULL)
+ err_msg = g_string_new_len (pip->err.buf, pip->err.len);
+ else
+ {
+ if (err_msg->str[err_msg->len - 1] != '\n')
+ g_string_append_c (err_msg, '\n');
+ g_string_append_len (err_msg, pip->err.buf, pip->err.len);
+ }
+ }
+
+ if (pip->out.len == MC_PIPE_STREAM_EOF)
+ break;
+
+ if (pip->out.len == 0)
+ continue;
+
+ if (pip->out.len == MC_PIPE_ERROR_READ)
+ return (-1);
+
+ while (ret != -1 && (buffer = mc_pstream_get_string (&pip->out)) != NULL)
+ {
+ /* handle a \n-separated file list */
+
+ if (buffer->str[buffer->len - 1] == '\n')
+ {
+ /* entire file name or last chunk */
+
+ g_string_truncate (buffer, buffer->len - 1);
+
+ /* join filename chunks */
+ if (remain_file_name != NULL)
+ {
+ g_string_append_len (remain_file_name, buffer->str, buffer->len);
+ g_string_free (buffer, TRUE);
+ buffer = remain_file_name;
+ remain_file_name = NULL;
+ }
+ }
+ else
+ {
+ /* first or middle chunk of file name */
+
+ if (remain_file_name == NULL)
+ remain_file_name = buffer;
+ else
+ {
+ g_string_append_len (remain_file_name, buffer->str, buffer->len);
+ g_string_free (buffer, TRUE);
+ }
+
+ continue;
+ }
+
+ ret = extfs_add_file (archive, buffer->str);
+
+ g_string_free (buffer, TRUE);
+ }
+ }
+
+ if (remain_file_name != NULL)
+ g_string_free (remain_file_name, TRUE);
+
+ if (err_msg != NULL)
+ {
+ if (*error == NULL)
+ mc_propagate_error (error, 0, "%s", err_msg->str);
+
+ g_string_free (err_msg, TRUE);
+ }
+ else if (ret == -1)
+ mc_propagate_error (error, 0, "%s", _("Inconsistent archive"));
+
+ return ret;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_which (struct vfs_class *me, const char *path)
+{
+ size_t path_len;
+ size_t i;
+
+ (void) me;
+
+ path_len = strlen (path);
+
+ for (i = 0; i < extfs_plugins->len; i++)
+ {
+ extfs_plugin_info_t *info;
+
+ info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
+
+ if ((strncmp (path, info->prefix, path_len) == 0)
+ && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
+ return i;
+ }
+ return -1;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_open_and_read_archive (int fstype, const char *name, struct extfs_super_t **archive)
+{
+ int result = -1;
+ struct extfs_super_t *a;
+ mc_pipe_t *pip;
+ GError *error = NULL;
+
+ pip = extfs_open_archive (fstype, name, archive, &error);
+
+ a = *archive;
+
+ if (pip == NULL)
+ {
+ const extfs_plugin_info_t *info;
+
+ info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
+ message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s:\n%s"), info->prefix, name,
+ error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ result = extfs_read_archive (pip, a, &error);
+
+ if (result != 0)
+ VFS_SUPER (a)->me->free (VFS_SUPER (a));
+
+ if (error != NULL)
+ {
+ message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message);
+ g_error_free (error);
+ }
+
+ mc_pclose (pip, NULL);
+ }
+
+ return result;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Dissect the path and create corresponding superblock.
+ */
+static const char *
+extfs_get_path (const vfs_path_t * vpath, struct extfs_super_t **archive, int flags)
+{
+ char *archive_name;
+ int result = -1;
+ GList *parc;
+ int fstype;
+ const vfs_path_element_t *path_element;
+ struct extfs_super_t *a = NULL;
+
+ path_element = vfs_path_get_by_index (vpath, -1);
+
+ fstype = extfs_which (path_element->class, path_element->vfs_prefix);
+ if (fstype == -1)
+ return NULL;
+
+ archive_name = vfs_path_to_str_elements_count (vpath, -1);
+
+ /* All filesystems should have some local archive, at least it can be PATH_SEP ('/'). */
+ parc = g_list_find_custom (extfs_subclass.supers, archive_name, extfs_cmp_archive);
+ if (parc != NULL)
+ {
+ a = EXTFS_SUPER (parc->data);
+ vfs_stamp (vfs_extfs_ops, (vfsid) a);
+ g_free (archive_name);
+ }
+ else
+ {
+ if ((flags & FL_NO_OPEN) == 0)
+ result = extfs_open_and_read_archive (fstype, archive_name, &a);
+
+ g_free (archive_name);
+
+ if (result == -1)
+ {
+ path_element->class->verrno = EIO;
+ return NULL;
+ }
+ }
+
+ *archive = a;
+ return path_element->path;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/* Return allocated path (without leading slash) inside the archive */
+
+static char *
+extfs_get_path_from_entry (const struct vfs_s_entry *entry)
+{
+ const struct vfs_s_entry *e;
+ GString *localpath;
+
+ localpath = g_string_new ("");
+
+ for (e = entry; e->dir != NULL; e = e->dir->ent)
+ {
+ g_string_prepend (localpath, e->name);
+ if (e->dir->ent->dir != NULL)
+ g_string_prepend_c (localpath, PATH_SEP);
+ }
+
+ return g_string_free (localpath, FALSE);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static struct vfs_s_entry *
+extfs_resolve_symlinks_int (struct vfs_s_entry *entry, GSList * list)
+{
+ struct vfs_s_entry *pent = NULL;
+
+ if (!S_ISLNK (entry->ino->st.st_mode))
+ return entry;
+
+ if (g_slist_find (list, entry) != NULL)
+ {
+ /* Here we protect us against symlink looping */
+ errloop = TRUE;
+ }
+ else
+ {
+ GSList *looping;
+
+ looping = g_slist_prepend (list, entry);
+ pent = extfs_find_entry_int (entry->dir, entry->ino->linkname, looping, FL_NONE);
+ looping = g_slist_delete_link (looping, looping);
+
+ if (pent == NULL)
+ my_errno = ENOENT;
+ }
+
+ return pent;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static struct vfs_s_entry *
+extfs_resolve_symlinks (struct vfs_s_entry *entry)
+{
+ struct vfs_s_entry *res;
+
+ errloop = FALSE;
+ notadir = FALSE;
+ res = extfs_resolve_symlinks_int (entry, NULL);
+ if (res == NULL)
+ {
+ if (errloop)
+ my_errno = ELOOP;
+ else if (notadir)
+ my_errno = ENOTDIR;
+ }
+ return res;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static char *
+extfs_get_archive_name (const struct extfs_super_t *archive)
+{
+ const char *archive_name;
+
+ if (archive->local_name != NULL)
+ archive_name = archive->local_name;
+ else
+ archive_name = CONST_VFS_SUPER (archive)->name;
+
+ if (archive_name == NULL || *archive_name == '\0')
+ return g_strdup ("no_archive_name");
+ else
+ {
+ char *ret_str;
+ vfs_path_t *vpath;
+ const char *path;
+
+ vpath = vfs_path_from_str (archive_name);
+ path = vfs_path_get_last_path_str (vpath);
+ ret_str = g_strdup (path);
+ vfs_path_free (vpath, TRUE);
+ return ret_str;
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/** Don't pass localname as NULL */
+
+static int
+extfs_cmd (const char *str_extfs_cmd, const struct extfs_super_t *archive,
+ const struct vfs_s_entry *entry, const char *localname)
+{
+ char *file;
+ char *quoted_file;
+ char *quoted_localname;
+ char *archive_name, *quoted_archive_name;
+ const extfs_plugin_info_t *info;
+ char *cmd;
+ int retval = 0;
+ GError *error = NULL;
+ mc_pipe_t *pip;
+
+ file = extfs_get_path_from_entry (entry);
+ quoted_file = name_quote (file, FALSE);
+ g_free (file);
+
+ /* Skip leading "./" (if present) added in name_quote() */
+ file = extfs_skip_leading_dotslash (quoted_file);
+
+ archive_name = extfs_get_archive_name (archive);
+ quoted_archive_name = name_quote (archive_name, FALSE);
+ g_free (archive_name);
+ quoted_localname = name_quote (localname, FALSE);
+ info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
+ cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
+ quoted_archive_name, " ", file, " ", quoted_localname, (char *) NULL);
+ g_free (quoted_file);
+ g_free (quoted_localname);
+ g_free (quoted_archive_name);
+
+ /* don't read stdout */
+ pip = mc_popen (cmd, FALSE, TRUE, &error);
+ g_free (cmd);
+
+ if (pip == NULL)
+ {
+ message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message);
+ g_error_free (error);
+ return (-1);
+ }
+
+ pip->err.null_term = TRUE;
+
+ mc_pread (pip, &error);
+ if (error != NULL)
+ {
+ message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), error->message);
+ g_error_free (error);
+ retval = -1;
+ }
+ else if (pip->err.len > 0)
+ message (D_ERROR, MSG_ERROR, _("EXTFS virtual file system:\n%s"), pip->err.buf);
+
+ mc_pclose (pip, NULL);
+
+ return retval;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+extfs_run (const vfs_path_t * vpath)
+{
+ struct extfs_super_t *archive = NULL;
+ const char *p;
+ char *q, *archive_name, *quoted_archive_name;
+ char *cmd;
+ const extfs_plugin_info_t *info;
+
+ p = extfs_get_path (vpath, &archive, FL_NONE);
+ if (p == NULL)
+ return;
+ q = name_quote (p, FALSE);
+
+ archive_name = extfs_get_archive_name (archive);
+ quoted_archive_name = name_quote (archive_name, FALSE);
+ g_free (archive_name);
+ info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
+ cmd =
+ g_strconcat (info->path, info->prefix, " run ", quoted_archive_name, " ", q, (char *) NULL);
+ g_free (quoted_archive_name);
+ g_free (q);
+ shell_execute (cmd, 0);
+ g_free (cmd);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void *
+extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
+{
+ vfs_file_handler_t *extfs_info;
+ struct extfs_super_t *archive = NULL;
+ const char *q;
+ struct vfs_s_entry *entry;
+ int local_handle;
+ gboolean created = FALSE;
+
+ q = extfs_get_path (vpath, &archive, FL_NONE);
+ if (q == NULL)
+ return NULL;
+ entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
+ if ((entry == NULL) && ((flags & O_CREAT) != 0))
+ {
+ /* Create new entry */
+ entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_MKFILE);
+ created = (entry != NULL);
+ }
+
+ if (entry == NULL)
+ return NULL;
+ entry = extfs_resolve_symlinks (entry);
+ if (entry == NULL)
+ return NULL;
+
+ if (S_ISDIR (entry->ino->st.st_mode))
+ ERRNOR (EISDIR, NULL);
+
+ if (entry->ino->localname == NULL)
+ {
+ vfs_path_t *local_filename_vpath;
+ const char *local_filename;
+
+ local_handle = vfs_mkstemps (&local_filename_vpath, "extfs", entry->name);
+
+ if (local_handle == -1)
+ return NULL;
+ close (local_handle);
+ local_filename = vfs_path_get_last_path_str (local_filename_vpath);
+
+ if (!created && ((flags & O_TRUNC) == 0)
+ && extfs_cmd (" copyout ", archive, entry, local_filename))
+ {
+ unlink (local_filename);
+ vfs_path_free (local_filename_vpath, TRUE);
+ my_errno = EIO;
+ return NULL;
+ }
+ entry->ino->localname = g_strdup (local_filename);
+ vfs_path_free (local_filename_vpath, TRUE);
+ }
+
+ local_handle = open (entry->ino->localname, NO_LINEAR (flags), mode);
+
+ if (local_handle == -1)
+ {
+ /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
+ flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
+ local_handle = open (entry->ino->localname, flags, mode);
+ }
+
+ if (local_handle == -1)
+ ERRNOR (EIO, NULL);
+
+ extfs_info = g_new (vfs_file_handler_t, 1);
+ vfs_s_init_fh (extfs_info, entry->ino, created);
+ extfs_info->handle = local_handle;
+
+ /* i.e. we had no open files and now we have one */
+ vfs_rmstamp (vfs_extfs_ops, (vfsid) archive);
+ VFS_SUPER (archive)->fd_usage++;
+ return extfs_info;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static ssize_t
+extfs_read (void *fh, char *buffer, size_t count)
+{
+ vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
+
+ return read (file->handle, buffer, count);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_close (void *fh)
+{
+ vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
+ int errno_code = 0;
+
+ close (file->handle);
+ file->handle = -1;
+
+ /* Commit the file if it has changed */
+ if (file->changed)
+ {
+ struct stat file_status;
+
+ if (extfs_cmd
+ (" copyin ", EXTFS_SUPER (VFS_FILE_HANDLER_SUPER (fh)), file->ino->ent,
+ file->ino->localname))
+ errno_code = EIO;
+
+ if (stat (file->ino->localname, &file_status) != 0)
+ errno_code = EIO;
+ else
+ file->ino->st.st_size = file_status.st_size;
+
+ file->ino->st.st_mtime = time (NULL);
+ }
+
+ if (--VFS_FILE_HANDLER_SUPER (fh)->fd_usage == 0)
+ vfs_stamp_create (vfs_extfs_ops, VFS_FILE_HANDLER_SUPER (fh));
+
+ g_free (fh);
+ if (errno_code != 0)
+ ERRNOR (EIO, -1);
+ return 0;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_errno (struct vfs_class *me)
+{
+ (void) me;
+ return my_errno;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void *
+extfs_opendir (const vfs_path_t * vpath)
+{
+ struct extfs_super_t *archive = NULL;
+ const char *q;
+ struct vfs_s_entry *entry;
+ GList **info;
+
+ q = extfs_get_path (vpath, &archive, FL_NONE);
+ if (q == NULL)
+ return NULL;
+ entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
+ if (entry == NULL)
+ return NULL;
+ entry = extfs_resolve_symlinks (entry);
+ if (entry == NULL)
+ return NULL;
+ if (!S_ISDIR (entry->ino->st.st_mode))
+ ERRNOR (ENOTDIR, NULL);
+
+ info = g_new (GList *, 1);
+ *info = g_queue_peek_head_link (entry->ino->subdir);
+
+ return info;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static struct vfs_dirent *
+extfs_readdir (void *data)
+{
+ struct vfs_dirent *dir;
+ GList **info = (GList **) data;
+
+ if (*info == NULL)
+ return NULL;
+
+ dir = vfs_dirent_init (NULL, VFS_ENTRY ((*info)->data)->name, 0); /* FIXME: inode */
+
+ *info = g_list_next (*info);
+
+ return dir;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_closedir (void *data)
+{
+ g_free (data);
+ return 0;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+
+static void
+extfs_stat_move (struct stat *buf, const struct vfs_s_inode *inode)
+{
+ *buf = inode->st;
+
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
+ buf->st_blksize = RECORDSIZE;
+#endif
+ vfs_adjust_stat (buf);
+#ifdef HAVE_STRUCT_STAT_ST_MTIM
+ buf->st_atim.tv_nsec = buf->st_mtim.tv_nsec = buf->st_ctim.tv_nsec = 0;
+#endif
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
+{
+ struct extfs_super_t *archive;
+ const char *q;
+ struct vfs_s_entry *entry;
+ int result = -1;
+
+ q = extfs_get_path (vpath, &archive, FL_NONE);
+ if (q == NULL)
+ goto cleanup;
+ entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
+ if (entry == NULL)
+ goto cleanup;
+ if (resolve)
+ {
+ entry = extfs_resolve_symlinks (entry);
+ if (entry == NULL)
+ goto cleanup;
+ }
+ extfs_stat_move (buf, entry->ino);
+ result = 0;
+ cleanup:
+ return result;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_stat (const vfs_path_t * vpath, struct stat *buf)
+{
+ return extfs_internal_stat (vpath, buf, TRUE);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
+{
+ return extfs_internal_stat (vpath, buf, FALSE);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_fstat (void *fh, struct stat *buf)
+{
+ vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
+
+ extfs_stat_move (buf, file->ino);
+ return 0;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
+{
+ struct extfs_super_t *archive;
+ const char *q;
+ size_t len;
+ struct vfs_s_entry *entry;
+ int result = -1;
+
+ q = extfs_get_path (vpath, &archive, FL_NONE);
+ if (q == NULL)
+ goto cleanup;
+ entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
+ if (entry == NULL)
+ goto cleanup;
+ if (!S_ISLNK (entry->ino->st.st_mode))
+ {
+ VFS_CLASS (vfs_path_get_last_path_vfs (vpath))->verrno = EINVAL;
+ goto cleanup;
+ }
+ len = strlen (entry->ino->linkname);
+ if (size < len)
+ len = size;
+ /* readlink() does not append a NUL character to buf */
+ result = len;
+ memcpy (buf, entry->ino->linkname, result);
+ cleanup:
+ return result;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
+{
+ (void) vpath;
+ (void) owner;
+ (void) group;
+ return 0;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_chmod (const vfs_path_t * vpath, mode_t mode)
+{
+ (void) vpath;
+ (void) mode;
+ return 0;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static ssize_t
+extfs_write (void *fh, const char *buf, size_t nbyte)
+{
+ vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
+
+ file->changed = TRUE;
+ return write (file->handle, buf, nbyte);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_unlink (const vfs_path_t * vpath)
+{
+ struct extfs_super_t *archive;
+ const char *q;
+ struct vfs_s_entry *entry;
+ int result = -1;
+
+ q = extfs_get_path (vpath, &archive, FL_NONE);
+ if (q == NULL)
+ goto cleanup;
+ entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
+ if (entry == NULL)
+ goto cleanup;
+ entry = extfs_resolve_symlinks (entry);
+ if (entry == NULL)
+ goto cleanup;
+ if (S_ISDIR (entry->ino->st.st_mode))
+ {
+ VFS_CLASS (vfs_path_get_last_path_vfs (vpath))->verrno = EISDIR;
+ goto cleanup;
+ }
+ if (extfs_cmd (" rm ", archive, entry, ""))
+ {
+ my_errno = EIO;
+ goto cleanup;
+ }
+ vfs_s_free_entry (VFS_SUPER (archive)->me, entry);
+ result = 0;
+ cleanup:
+ return result;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
+{
+ struct extfs_super_t *archive;
+ const char *q;
+ struct vfs_s_entry *entry;
+ int result = -1;
+ struct vfs_class *me;
+
+ (void) mode;
+
+ me = VFS_CLASS (vfs_path_get_last_path_vfs (vpath));
+ q = extfs_get_path (vpath, &archive, FL_NONE);
+ if (q == NULL)
+ goto cleanup;
+ entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
+ if (entry != NULL)
+ {
+ me->verrno = EEXIST;
+ goto cleanup;
+ }
+ entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_MKDIR);
+ if (entry == NULL)
+ goto cleanup;
+ entry = extfs_resolve_symlinks (entry);
+ if (entry == NULL)
+ goto cleanup;
+ if (!S_ISDIR (entry->ino->st.st_mode))
+ {
+ me->verrno = ENOTDIR;
+ goto cleanup;
+ }
+
+ if (extfs_cmd (" mkdir ", archive, entry, ""))
+ {
+ my_errno = EIO;
+ vfs_s_free_entry (VFS_SUPER (archive)->me, entry);
+ goto cleanup;
+ }
+ result = 0;
+ cleanup:
+ return result;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_rmdir (const vfs_path_t * vpath)
+{
+ struct extfs_super_t *archive;
+ const char *q;
+ struct vfs_s_entry *entry;
+ int result = -1;
+
+ q = extfs_get_path (vpath, &archive, FL_NONE);
+ if (q == NULL)
+ goto cleanup;
+ entry = extfs_find_entry (VFS_SUPER (archive)->root, q, FL_NONE);
+ if (entry == NULL)
+ goto cleanup;
+ entry = extfs_resolve_symlinks (entry);
+ if (entry == NULL)
+ goto cleanup;
+ if (!S_ISDIR (entry->ino->st.st_mode))
+ {
+ VFS_CLASS (vfs_path_get_last_path_vfs (vpath))->verrno = ENOTDIR;
+ goto cleanup;
+ }
+
+ if (extfs_cmd (" rmdir ", archive, entry, ""))
+ {
+ my_errno = EIO;
+ goto cleanup;
+ }
+ vfs_s_free_entry (VFS_SUPER (archive)->me, entry);
+ result = 0;
+ cleanup:
+ return result;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_chdir (const vfs_path_t * vpath)
+{
+ void *data;
+
+ my_errno = ENOTDIR;
+ data = extfs_opendir (vpath);
+ if (data == NULL)
+ return (-1);
+ extfs_closedir (data);
+ my_errno = 0;
+ return 0;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static off_t
+extfs_lseek (void *fh, off_t offset, int whence)
+{
+ vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
+
+ return lseek (file->handle, offset, whence);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static vfsid
+extfs_getid (const vfs_path_t * vpath)
+{
+ struct extfs_super_t *archive = NULL;
+ const char *p;
+
+ p = extfs_get_path (vpath, &archive, FL_NO_OPEN);
+ return (p == NULL ? NULL : (vfsid) archive);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static vfs_path_t *
+extfs_getlocalcopy (const vfs_path_t * vpath)
+{
+ vfs_file_handler_t *fh;
+ vfs_path_t *p;
+
+ fh = VFS_FILE_HANDLER (extfs_open (vpath, O_RDONLY, 0));
+ if (fh == NULL)
+ return NULL;
+ if (fh->ino->localname == NULL)
+ {
+ extfs_close ((void *) fh);
+ return NULL;
+ }
+ p = vfs_path_from_str (fh->ino->localname);
+ VFS_FILE_HANDLER_SUPER (fh)->fd_usage++;
+ extfs_close ((void *) fh);
+ return p;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
+{
+ vfs_file_handler_t *fh;
+
+ fh = VFS_FILE_HANDLER (extfs_open (vpath, O_RDONLY, 0));
+ if (fh == NULL)
+ return 0;
+
+ if (strcmp (fh->ino->localname, vfs_path_get_last_path_str (local)) == 0)
+ {
+ VFS_FILE_HANDLER_SUPER (fh)->fd_usage--;
+ if (has_changed)
+ fh->changed = TRUE;
+ extfs_close ((void *) fh);
+ return 0;
+ }
+ else
+ {
+ /* Should not happen */
+ extfs_close ((void *) fh);
+ return 0;
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static gboolean
+extfs_get_plugins (const char *where, gboolean silent)
+{
+ char *dirname;
+ GDir *dir;
+ const char *filename;
+
+ dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
+ dir = g_dir_open (dirname, 0, NULL);
+
+ /* We may not use vfs_die() message or message or similar,
+ * UI is not initialized at this time and message would not
+ * appear on screen. */
+ if (dir == NULL)
+ {
+ if (!silent)
+ fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
+ g_free (dirname);
+ return FALSE;
+ }
+
+ if (extfs_plugins == NULL)
+ extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
+
+ while ((filename = g_dir_read_name (dir)) != NULL)
+ {
+ char fullname[MC_MAXPATHLEN];
+ struct stat s;
+
+ g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
+
+ if ((stat (fullname, &s) == 0) && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
+ && is_exe (s.st_mode))
+ {
+ int f;
+
+ f = open (fullname, O_RDONLY);
+
+ if (f >= 0)
+ {
+ size_t len, i;
+ extfs_plugin_info_t info;
+ gboolean found = FALSE;
+
+ close (f);
+
+ /* Handle those with a trailing '+', those flag that the
+ * file system does not require an archive to work
+ */
+ len = strlen (filename);
+ info.need_archive = (filename[len - 1] != '+');
+ info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
+ info.prefix = g_strndup (filename, len);
+
+ /* prepare to compare file names without trailing '+' */
+ if (!info.need_archive)
+ info.prefix[len - 1] = '\0';
+
+ /* don't overload already found plugin */
+ for (i = 0; i < extfs_plugins->len && !found; i++)
+ {
+ extfs_plugin_info_t *p;
+
+ p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
+
+ /* 2 files with same names cannot be in a directory */
+ found = strcmp (info.path, p->path) != 0
+ && strcmp (info.prefix, p->prefix) == 0;
+ }
+
+ if (found)
+ {
+ g_free (info.path);
+ g_free (info.prefix);
+ }
+ else
+ {
+ /* restore file name */
+ if (!info.need_archive)
+ info.prefix[len - 1] = '+';
+ g_array_append_val (extfs_plugins, info);
+ }
+ }
+ }
+ }
+
+ g_dir_close (dir);
+ g_free (dirname);
+
+ return TRUE;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_init (struct vfs_class *me)
+{
+ gboolean d1, d2;
+
+ (void) me;
+
+ /* 1st: scan user directory */
+ d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */
+ /* 2nd: scan system dir */
+ d2 = extfs_get_plugins (LIBEXECDIR, d1);
+
+ return (d1 || d2 ? 1 : 0);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+extfs_done (struct vfs_class *me)
+{
+ size_t i;
+
+ while (VFS_SUBCLASS (me)->supers != NULL)
+ me->free ((vfsid) VFS_SUBCLASS (me)->supers->data);
+
+ if (extfs_plugins == NULL)
+ return;
+
+ for (i = 0; i < extfs_plugins->len; i++)
+ {
+ extfs_plugin_info_t *info;
+
+ info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
+ g_free (info->path);
+ g_free (info->prefix);
+ }
+
+ g_array_free (extfs_plugins, TRUE);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static int
+extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
+{
+ (void) arg;
+
+ if (ctlop == VFS_SETCTL_RUN)
+ {
+ extfs_run (vpath);
+ return 1;
+ }
+ return 0;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/*** public functions ****************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+
+void
+vfs_init_extfs (void)
+{
+ vfs_init_subclass (&extfs_subclass, "extfs", VFSF_UNKNOWN, NULL);
+ vfs_extfs_ops->init = extfs_init;
+ vfs_extfs_ops->done = extfs_done;
+ vfs_extfs_ops->fill_names = extfs_fill_names;
+ vfs_extfs_ops->which = extfs_which;
+ vfs_extfs_ops->open = extfs_open;
+ vfs_extfs_ops->close = extfs_close;
+ vfs_extfs_ops->read = extfs_read;
+ vfs_extfs_ops->write = extfs_write;
+ vfs_extfs_ops->opendir = extfs_opendir;
+ vfs_extfs_ops->readdir = extfs_readdir;
+ vfs_extfs_ops->closedir = extfs_closedir;
+ vfs_extfs_ops->stat = extfs_stat;
+ vfs_extfs_ops->lstat = extfs_lstat;
+ vfs_extfs_ops->fstat = extfs_fstat;
+ vfs_extfs_ops->chmod = extfs_chmod;
+ vfs_extfs_ops->chown = extfs_chown;
+ vfs_extfs_ops->readlink = extfs_readlink;
+ vfs_extfs_ops->unlink = extfs_unlink;
+ vfs_extfs_ops->chdir = extfs_chdir;
+ vfs_extfs_ops->ferrno = extfs_errno;
+ vfs_extfs_ops->lseek = extfs_lseek;
+ vfs_extfs_ops->getid = extfs_getid;
+ vfs_extfs_ops->getlocalcopy = extfs_getlocalcopy;
+ vfs_extfs_ops->ungetlocalcopy = extfs_ungetlocalcopy;
+ vfs_extfs_ops->mkdir = extfs_mkdir;
+ vfs_extfs_ops->rmdir = extfs_rmdir;
+ vfs_extfs_ops->setctl = extfs_setctl;
+ extfs_subclass.free_inode = extfs_free_inode;
+ extfs_subclass.free_archive = extfs_free_archive;
+ vfs_register_class (vfs_extfs_ops);
+}
+
+/* --------------------------------------------------------------------------------------------- */
diff --git a/src/vfs/extfs/extfs.h b/src/vfs/extfs/extfs.h
new file mode 100644
index 0000000..c576dc0
--- /dev/null
+++ b/src/vfs/extfs/extfs.h
@@ -0,0 +1,18 @@
+#ifndef MC__VFS_EXTFS_H
+#define MC__VFS_EXTFS_H
+
+/*** typedefs(not structures) and defined constants **********************************************/
+
+/*** enums ***************************************************************************************/
+
+/*** structures declarations (and typedefs of structures)*****************************************/
+
+/*** global variables defined in .c file *********************************************************/
+
+/*** declarations of public functions ************************************************************/
+
+void vfs_init_extfs (void);
+
+/*** inline functions ****************************************************************************/
+
+#endif /* MC__VFS_CPIO_H */
diff --git a/src/vfs/extfs/helpers/Makefile.am b/src/vfs/extfs/helpers/Makefile.am
new file mode 100644
index 0000000..f1ea0ac
--- /dev/null
+++ b/src/vfs/extfs/helpers/Makefile.am
@@ -0,0 +1,76 @@
+extfsdir = $(libexecdir)/@PACKAGE@/extfs.d
+
+# Files to install and distribute other than extfs scripts
+EXTFS_MISC = README README.extfs
+
+# Scripts hat don't need adaptation to the local system
+EXTFS_CONST = bpp changesetfs gitfs+ patchsetfs rpm trpm u7z uc1541
+
+# Scripts that need adaptation to the local system - source files
+EXTFS_IN = \
+ a+.in \
+ apt+.in \
+ audio.in \
+ deb.in \
+ deba.in \
+ debd.in \
+ dpkg+.in \
+ iso9660.in \
+ hp48+.in \
+ lslR.in \
+ mailfs.in \
+ patchfs.in \
+ rpms+.in \
+ s3+.in \
+ uace.in \
+ ualz.in \
+ uar.in \
+ uarc.in \
+ uarj.in \
+ ucab.in \
+ uha.in \
+ ulha.in \
+ ulib.in \
+ unar.in \
+ urar.in \
+ uwim.in \
+ uzip.in \
+ uzoo.in
+
+# Scripts that need adaptation to the local system - files to install
+EXTFS_OUT = \
+ a+ \
+ apt+ \
+ audio \
+ deb \
+ deba \
+ debd \
+ dpkg+ \
+ iso9660 \
+ hp48+ \
+ lslR \
+ mailfs \
+ patchfs \
+ rpms+ \
+ s3+ \
+ uace \
+ ualz \
+ uar \
+ uarc \
+ uarj \
+ ucab \
+ uha \
+ ulha \
+ ulib \
+ unar \
+ urar \
+ uwim \
+ uzip \
+ uzoo
+
+if ENABLE_VFS_EXTFS
+extfs_DATA = $(EXTFS_MISC)
+extfs_SCRIPTS = $(EXTFS_CONST) $(EXTFS_OUT)
+endif
+
+EXTRA_DIST = $(EXTFS_MISC) $(EXTFS_CONST)
diff --git a/src/vfs/extfs/helpers/Makefile.in b/src/vfs/extfs/helpers/Makefile.in
new file mode 100644
index 0000000..0a240fb
--- /dev/null
+++ b/src/vfs/extfs/helpers/Makefile.in
@@ -0,0 +1,812 @@
+# 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/vfs/extfs/helpers
+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/ax_check_pcre2.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 = a+ apt+ audio deb deba debd dpkg+ iso9660 hp48+ \
+ lslR mailfs patchfs rpms+ s3+ uace ualz uar uarc uarj ucab uha \
+ ulha ulib unar urar uwim uzip uzoo
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(extfsdir)" "$(DESTDIR)$(extfsdir)"
+SCRIPTS = $(extfs_SCRIPTS)
+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 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(extfs_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/a+.in \
+ $(srcdir)/apt+.in $(srcdir)/audio.in $(srcdir)/deb.in \
+ $(srcdir)/deba.in $(srcdir)/debd.in $(srcdir)/dpkg+.in \
+ $(srcdir)/hp48+.in $(srcdir)/iso9660.in $(srcdir)/lslR.in \
+ $(srcdir)/mailfs.in $(srcdir)/patchfs.in $(srcdir)/rpms+.in \
+ $(srcdir)/s3+.in $(srcdir)/uace.in $(srcdir)/ualz.in \
+ $(srcdir)/uar.in $(srcdir)/uarc.in $(srcdir)/uarj.in \
+ $(srcdir)/ucab.in $(srcdir)/uha.in $(srcdir)/ulha.in \
+ $(srcdir)/ulib.in $(srcdir)/unar.in $(srcdir)/urar.in \
+ $(srcdir)/uwim.in $(srcdir)/uzip.in $(srcdir)/uzoo.in README
+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_CFLAGS = @PCRE_CFLAGS@
+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@
+extfsdir = $(libexecdir)/@PACKAGE@/extfs.d
+
+# Files to install and distribute other than extfs scripts
+EXTFS_MISC = README README.extfs
+
+# Scripts hat don't need adaptation to the local system
+EXTFS_CONST = bpp changesetfs gitfs+ patchsetfs rpm trpm u7z uc1541
+
+# Scripts that need adaptation to the local system - source files
+EXTFS_IN = \
+ a+.in \
+ apt+.in \
+ audio.in \
+ deb.in \
+ deba.in \
+ debd.in \
+ dpkg+.in \
+ iso9660.in \
+ hp48+.in \
+ lslR.in \
+ mailfs.in \
+ patchfs.in \
+ rpms+.in \
+ s3+.in \
+ uace.in \
+ ualz.in \
+ uar.in \
+ uarc.in \
+ uarj.in \
+ ucab.in \
+ uha.in \
+ ulha.in \
+ ulib.in \
+ unar.in \
+ urar.in \
+ uwim.in \
+ uzip.in \
+ uzoo.in
+
+
+# Scripts that need adaptation to the local system - files to install
+EXTFS_OUT = \
+ a+ \
+ apt+ \
+ audio \
+ deb \
+ deba \
+ debd \
+ dpkg+ \
+ iso9660 \
+ hp48+ \
+ lslR \
+ mailfs \
+ patchfs \
+ rpms+ \
+ s3+ \
+ uace \
+ ualz \
+ uar \
+ uarc \
+ uarj \
+ ucab \
+ uha \
+ ulha \
+ ulib \
+ unar \
+ urar \
+ uwim \
+ uzip \
+ uzoo
+
+@ENABLE_VFS_EXTFS_TRUE@extfs_DATA = $(EXTFS_MISC)
+@ENABLE_VFS_EXTFS_TRUE@extfs_SCRIPTS = $(EXTFS_CONST) $(EXTFS_OUT)
+EXTRA_DIST = $(EXTFS_MISC) $(EXTFS_CONST)
+all: all-am
+
+.SUFFIXES:
+$(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/vfs/extfs/helpers/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/vfs/extfs/helpers/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):
+a+: $(top_builddir)/config.status $(srcdir)/a+.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+apt+: $(top_builddir)/config.status $(srcdir)/apt+.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+audio: $(top_builddir)/config.status $(srcdir)/audio.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+deb: $(top_builddir)/config.status $(srcdir)/deb.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+deba: $(top_builddir)/config.status $(srcdir)/deba.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+debd: $(top_builddir)/config.status $(srcdir)/debd.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+dpkg+: $(top_builddir)/config.status $(srcdir)/dpkg+.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+iso9660: $(top_builddir)/config.status $(srcdir)/iso9660.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+hp48+: $(top_builddir)/config.status $(srcdir)/hp48+.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+lslR: $(top_builddir)/config.status $(srcdir)/lslR.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+mailfs: $(top_builddir)/config.status $(srcdir)/mailfs.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+patchfs: $(top_builddir)/config.status $(srcdir)/patchfs.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+rpms+: $(top_builddir)/config.status $(srcdir)/rpms+.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+s3+: $(top_builddir)/config.status $(srcdir)/s3+.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+uace: $(top_builddir)/config.status $(srcdir)/uace.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+ualz: $(top_builddir)/config.status $(srcdir)/ualz.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+uar: $(top_builddir)/config.status $(srcdir)/uar.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+uarc: $(top_builddir)/config.status $(srcdir)/uarc.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+uarj: $(top_builddir)/config.status $(srcdir)/uarj.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+ucab: $(top_builddir)/config.status $(srcdir)/ucab.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+uha: $(top_builddir)/config.status $(srcdir)/uha.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+ulha: $(top_builddir)/config.status $(srcdir)/ulha.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+ulib: $(top_builddir)/config.status $(srcdir)/ulib.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+unar: $(top_builddir)/config.status $(srcdir)/unar.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+urar: $(top_builddir)/config.status $(srcdir)/urar.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+uwim: $(top_builddir)/config.status $(srcdir)/uwim.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+uzip: $(top_builddir)/config.status $(srcdir)/uzip.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+uzoo: $(top_builddir)/config.status $(srcdir)/uzoo.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-extfsSCRIPTS: $(extfs_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ @list='$(extfs_SCRIPTS)'; test -n "$(extfsdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(extfsdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(extfsdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e 'h;s|.*|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(extfsdir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(extfsdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-extfsSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(extfs_SCRIPTS)'; test -n "$(extfsdir)" || exit 0; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 's,.*/,,;$(transform)'`; \
+ dir='$(DESTDIR)$(extfsdir)'; $(am__uninstall_files_from_dir)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-extfsDATA: $(extfs_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(extfs_DATA)'; test -n "$(extfsdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(extfsdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(extfsdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(extfsdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(extfsdir)" || exit $$?; \
+ done
+
+uninstall-extfsDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(extfs_DATA)'; test -n "$(extfsdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(extfsdir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+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 $(SCRIPTS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(extfsdir)" "$(DESTDIR)$(extfsdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+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 mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-extfsDATA install-extfsSCRIPTS
+
+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 Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-extfsDATA uninstall-extfsSCRIPTS
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool 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-extfsDATA \
+ install-extfsSCRIPTS 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-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \
+ uninstall-am uninstall-extfsDATA uninstall-extfsSCRIPTS
+
+.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/vfs/extfs/helpers/README b/src/vfs/extfs/helpers/README
new file mode 100644
index 0000000..64da3d6
--- /dev/null
+++ b/src/vfs/extfs/helpers/README
@@ -0,0 +1,200 @@
+ Writing scripts for Midnight Commander's external vfs
+
+IMPORTANT NOTE: There may be some bugs left in extfs. Enjoy.
+
+Starting with version 3.1, the Midnight Commander comes with so called
+extfs, which is one of the virtual filesystems. This system makes it
+possible to create new virtual filesystems for the GNU MC very easily.
+
+To handle requests, create a shell/perl/python/etc script/program
+(with executable permissions) in $(libexecdir)/mc/extfs.d
+or in ~/.local/share/mc/extfs.d/.
+
+(Note: $(libexecdir) should be substituted for actual libexecdir path
+stored when configured or compiled, like /usr/local/libexec or /usr/libexec).
+
+Assign a vfs suffix. For example, if you have .zip file, and would like
+to see what's inside it, path will be
+
+/anypath/my.zip/uzip://some_path/...
+
+In this example, .zip is suffix, but I call vfs 'uzip'. Why? Well,
+what this vfs essentially does is UNzip. UN is too long, so I chose
+U. Note that sometime in future filesystem like zip may exist: It will
+take whole tree and create .zip file from it. So /usr/zip:// will be
+zipfile containing whole /usr tree.
+
+If your vfs does not require file to work on, add '+' to the end of name.
+Note, that trailing '+' in file name is not a part of vfs name, it is
+just an vfs attribute. So you have not use it in vfs commands:
+
+cd rpms://
+
+is correct command, and
+
+cd rpms+://
+
+is incorrect command.
+
+
+* Commands that should be implemented by your shell script
+----------------------------------------------------------
+
+Return zero from your script upon completion of the command, otherwise
+nonzero for failure or in case of an unsupported command.
+
+$libdir/extfs/prefix command [arguments]
+
+* Command: list archivename
+
+This command should list the complete archive content in the following format
+(a little modified ls -l listing):
+
+AAAAAAA NNN OOOOOOOO GGGGGGGG SSSSSSSS DATETIME [PATH/]FILENAME [-> [PATH/]FILENAME[/]]]
+
+where (things in [] are optional):
+
+AAAAAAA is the permission string like in ls -l
+NNN is the number of links
+OOOOOOOO is the owner (either UID or name)
+GGGGGGGG is the group (either GID or name)
+SSSSSSSS is the file size
+FILENAME is the filename
+PATH is the path from the archive's root without the leading slash (/)
+DATETIME has one of the following formats:
+ Mon DD hh:mm[:ss], Mon DD YYYY, MM-DD-YYYY hh:mm[:ss]
+
+ where Mon is a three letter English month name, DD is day
+ 01-31 (can be 1-31, if following Mon), MM is month 01-12,
+ YYYY is four digit year, hh is hours, mm is minutes,
+ and ss is optional seconds.
+
+If the -> [PATH/]FILENAME part is present, it means:
+
+If permissions start with an l (ell), then it is the name that symlink
+points to. (If this PATH starts with a MC vfs prefix, then it is a symlink
+somewhere to the other virtual filesystem (if you want to specify path from
+the local root, use local:/path_name instead of /path_name, since /path_name
+means from root of the archive listed).
+
+If permissions do not start with l, but number of links is greater than one,
+then it says that this file should be a hardlinked with the other file.
+
+The result of list command must not contain "." and ".." items.
+
+* Command: copyout archivename storedfilename extractto
+
+This should extract from archive archivename the file called
+storedfilename (possibly with path if not located in archive's root
+[this is wrong. current extfs strips paths! -- pavel@ucw.cz])
+to file extractto.
+
+* Command: copyin archivename storedfilename sourcefile
+
+This should add to the archivename the sourcefile with the name
+storedfilename inside the archive.
+
+Important note: archivename in the above examples may not have the
+extension you are expecting to have, like it may happen that
+archivename will be something like /tmp/f43513254 or just
+anything. Some archivers do not like it, so you'll have to find some
+workaround.
+
+* Command: rm archivename storedfilename
+
+This should remove storedfilename from archivename.
+
+* Command: mkdir archivename dirname
+
+This should create a new directory called dirname inside archivename.
+
+* Command: rmdir archivename dirname
+
+This should remove an existing directory dirname. If the directory is
+not empty, mc will recursively delete it (possibly prompting).
+
+* Command: run
+
+Undocumented :-)
+
+---------------------------------------------------------
+
+Don't forget to mark this file executable (chmod 755 ThisFile, for example)
+
+For skeleton structure of executable, look at some of filesystems
+similar to yours.
+
+---------------------------------------------------------
+
+In constructing these routines, errors will be made, and mc will not display
+a malformed printing line. That can lead the programmer down many false
+trails in search of the bug. Since this routine is an executable shell script
+it can be run from the command line independently of mc, and its output will
+show on the console or can be redirected to a file.
+
+* Putting it to use
+----------------------------------------------------------
+The file .mc.ext in a home directory, and in mc's user directory (commonly
+/etc/mc), contains instructions for operations on files depending
+on filename extensions. It is well documented in other files in this
+distribution, so here are just a few notes specifically on use of the
+Virtual File System you just built.
+
+There are entries in .mc.ext defining a few operations that can be done on a
+file from an mc panel. Typically they are annotated with a hash mark and a
+file extension like this:
+
+# zip
+
+There must be a way to find the file by extension, so the next line does
+that. In essence it says "identify the string ".zip" or (|) ".ZIP" at the
+end ($) of a filename":
+
+regex/\.(zip|ZIP)$
+
+The operations themselves follow that. They must be indented by at least a
+space, and a tab works as well. In particular, the Open operation will
+now use your new virtual file system by cd'ing to it like this:
+
+ Open=%cd zip:%d/%p
+
+This is the line used when a file is highlighted in a panel and the user
+presses <Enter> or <Return>. The contents of the archive should show just
+as if they were in a real directory, and can be manipulated as such.
+The rest of the entry pertains to use of the F3 View key:
+
+ View=%view{ascii} unzip -v %f
+
+And perhaps an optional icon for X:
+
+ Icon=zip.xpm
+
+And perhaps an operation to extract the contents of the file, called from
+a menu selection:
+
+ Extract=unzip %f '*'
+
+This is just an example. The current entry for .zip files has a menu selection
+of 'Unzip' which could be used in place of 'Extract'. What goes here depends
+on what items you have in, or add to, the menu system, and that's another
+subject. The sum of this is the .mc.ext entry:
+
+# zip
+regex/\.(zip|ZIP)$
+ Open=%cd %p/uzip://
+ View=%view{ascii} unzip -v %f
+ Icon=zip.xpm
+ Extract=unzip %f '*'
+
+Add an entry like this to the .mc.ext file in a user's home directory, If you
+want others to have it, add it to the mc.ext file in the mc system directory,
+often /etc/mc/mc.ext. Notice this file is not prepended with a dot.
+
+Once all this is done, and things are in their proper places, exit mc if you
+were using it, and restart it so it picks up the new information.
+
+That's all there is to it. The hardest part is making a listing function
+that sorts the output of a system listing command and turns it into a form
+that mc can use. Currently awk (or gawk) is used because nearly all systems
+have it. If another scripting language is available, like perl, that could
+also be used.
diff --git a/src/vfs/extfs/helpers/README.extfs b/src/vfs/extfs/helpers/README.extfs
new file mode 100644
index 0000000..ce7d086
--- /dev/null
+++ b/src/vfs/extfs/helpers/README.extfs
@@ -0,0 +1,78 @@
+# Each external VFS type must be registered in extfs.d directory if you want to use it.
+# Trailing plus means that the filesystem is not tied to a certain file.
+
+# Popular PC archivers
+uzip
+uzoo
+ulha
+urar
+uha
+u7z
+ualz
+# FIXME: for arj usage you need a special patch to unarj (see unarj.diff)
+uarj
+uarc
+uace
+
+# For cab files
+ucab
+
+# ar is used for static libraries
+uar
+
+# Packages from popular Linux distributions
+rpm
+deb
+
+# a+ - mtools filesystem
+a+
+
+# For browsing lslR listings (found on many ftp sites)
+lslR
+
+# Hewlett Packard calculator
+hp48+
+
+# Commodore 64/128 d64/D64 files
+uc1541
+
+# Break patches into chunks
+patchfs
+
+# Represents a mailbox as a directory
+mailfs
+
+# List all installed RPM packages on the system
+rpms+
+trpm
+
+# dpkg frontend
+dpkg+
+debd
+
+# apt frontend
+apt+
+deba
+
+# Simple filesystem for audio cdroms. Use /dev/cdrom#audio (or /#audio)
+audio
+
+# Package of Bad Penguin (an Italian GNU/Linux distribution)
+bpp
+
+# ISO image
+iso9660
+
+# Amazon S3
+s3+
+
+# git frontend
+gitfs - browse the git repo
+changesetfs - list of versions of current file
+patchsetfs - list of patches of current file
+
+# Gputils lib archives.
+ulib
+
+# PAK Archive
+unar
diff --git a/src/vfs/extfs/helpers/a+.in b/src/vfs/extfs/helpers/a+.in
new file mode 100644
index 0000000..fe446f4
--- /dev/null
+++ b/src/vfs/extfs/helpers/a+.in
@@ -0,0 +1,126 @@
+#! @PERL@
+#
+# External filesystem for mc, using mtools
+# Written Ludek Brukner <lubr@barco.cz>, 1997
+# Much improved by Tom Perkins <968794022@noid.net>, 2000
+#
+# WARNING - This software is ALPHA - Absolutely NO WARRANTY
+#
+
+# These mtools components must be in PATH for this to work
+
+use warnings;
+
+sub quote {
+ $_ = shift(@_);
+ s/([^\w\/.+-])/\\$1/g;
+ return($_);
+}
+
+$mmd = "mmd";
+$mrd = "mrd";
+$mdel = "mdel";
+$mdir = "mdir -a";
+$mcopy = "mcopy -noQ";
+
+$0 =~ s|.*/||;
+$qdisk = quote($0);
+
+$ENV{MTOOLS_DATE_STRING} = "mm-dd-yyyy";
+$ENV{MTOOLS_TWENTY_FOUR_HOUR_CLOCK} = "1";
+
+SWITCH: for ( $ARGV[0] ) {
+ /list/ && do {
+ @dirs = get_dirs("");
+ while ($dir = shift(@dirs)) {
+ push @dirs, get_dirs("$dir/");
+ } exit 0; };
+ /mkdir/ && do {
+ shift; shift;
+ exit 1 if scalar(@ARGV) != 1;
+ $qname = quote($ARGV[0]);
+ system("$mmd $qdisk:/$qname >/dev/null");
+ exit 0; };
+ /rmdir/ && do {
+ shift; shift;
+ exit 1 if scalar(@ARGV) != 1;
+ $qname = quote($ARGV[0]);
+ system("$mrd $qdisk:/$qname >/dev/null");
+ exit 0; };
+ /rm/ && do {
+ shift; shift;
+ exit 1 if scalar(@ARGV) != 1;
+ $qname = quote($ARGV[0]);
+ system("$mdel $qdisk:/$qname >/dev/null");
+ exit 0; };
+ /copyout/ && do {
+ shift; shift;
+ exit 1 if scalar(@ARGV) != 2;
+ ( $qsrc, $qdest ) = @ARGV;
+ $qsrc = quote($qsrc);
+ $qdest = quote($qdest);
+ system("$mcopy $qdisk:/$qsrc $qdest >/dev/null");
+ exit 0; };
+ /copyin/ && do {
+ shift; shift;
+ exit 1 if scalar(@ARGV) != 2;
+ ( $qdest, $qsrc ) = @ARGV;
+ $qsrc = quote($qsrc);
+ $qdest = quote($qdest);
+ system("$mcopy $qsrc $qdisk:/$qdest >/dev/null");
+ exit 0; };
+ /.*/ && do { # an unfamiliar command
+ exit 1; };
+}
+
+sub get_dirs {
+ my ($path, $name, $size, $date, $time, $longname, @lst, @rv);
+ $path = shift(@_);
+ my $qpath = quote($path);
+ @rv = ();
+
+ open(FILE,"$mdir $qdisk:/$qpath |");
+ while ( <FILE> ) {
+ chomp();
+ /^ / && next; # ignore `non-file' lines
+ m{^Directory for $0:/}i && next; # ignore `non-file' lines
+ /^$/ && next; # ignore empty lines
+ /^\.\.?/ && next; # ignore `.' and `..'
+
+ $name = substr($_,0,12);
+ $name =~ s/^([^ ]*) +([^ ]+)[ \t]*$/$1.$2/;
+ $name =~ s/[ .]+$//;
+
+ $_ = substr($_,12);
+ s/^[ ]+//;
+
+ ($size,$date,$time,$longname) = split(/[ \t]+/, $_, 4);
+
+ defined $time || next;
+
+ # process "am" and "pm". Should not be needed if
+ # MTOOLS_TWENTY_FOUR_HOUR_CLOCK is respected.
+ @lst = split(/([:ap])/, $time);
+ $lst[0] += 12 if (defined $lst[3] && $lst[3] eq "p");
+
+ $time = sprintf("%02d:%02d", $lst[0], $lst[2]);
+ @lst = split(/-/, $date);
+ $lst[2] %= 100 if ($lst[2] > 100);
+ $date = sprintf ("%02d-%02d-%02d", @lst);
+
+ $name = $path . lc(($longname) ? $longname : $name);
+
+ if ($size =~ /DIR/) {
+ printf("drwxr-xr-x 1 %-8d %-8d %8d %s %s %s\n",
+ 0, 0, 0, $date, $time, $name);
+ push @rv, $name;
+ } else {
+ printf("-rw-r--r-- 1 %-8d %-8d %8d %s %s %s\n",
+ 0, 0, $size, $date, $time, $name);
+ }
+ }
+ close(FILE);
+ return @rv;
+}
+
+1;
diff --git a/src/vfs/extfs/helpers/apt+.in b/src/vfs/extfs/helpers/apt+.in
new file mode 100644
index 0000000..60011e6
--- /dev/null
+++ b/src/vfs/extfs/helpers/apt+.in
@@ -0,0 +1,359 @@
+#! @PERL@
+#
+# 1999 (c) Piotr Roszatycki <dexter@debian.org>
+# This software is under GNU license
+# last modification: 1999-12-08
+#
+# apt
+
+sub quote {
+ $_ = shift(@_);
+ s/([^\w\/.+-])/\\$1/g;
+ return($_);
+}
+
+sub bt
+{
+ my ($dt) = @_;
+ my (@time);
+ @time = localtime($dt);
+ $bt = sprintf "%02d-%02d-%d %02d:%02d", $time[4] + 1, $time[3],
+ $time[5] + 1900, $time[2], $time[1];
+ return $bt;
+}
+
+
+sub ft
+{
+ my ($f) = @_;
+ return "d" if -d $f;
+ return "l" if -l $f;
+ return "p" if -p $f;
+ return "S" if -S $f;
+ return "b" if -b $f;
+ return "c" if -c $f;
+ return "-";
+}
+
+sub fm
+{
+ my ($n) = @_;
+ my ($m);
+
+ if( $n & 0400 ) {
+ $m .= "r";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 0200 ) {
+ $m .= "w";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 04000 ) {
+ $m .= "s";
+ } elsif( $n & 0100 ) {
+ $m .= "x";
+ } else {
+ $m .= "-";
+ }
+
+ if( $n & 0040 ) {
+ $m .= "r";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 0020 ) {
+ $m .= "w";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 02000 ) {
+ $m .= "s";
+ } elsif( $n & 0010 ) {
+ $m .= "x";
+ } else {
+ $m .= "-";
+ }
+
+ if( $n & 0004 ) {
+ $m .= "r";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 0002 ) {
+ $m .= "w";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 01000 ) {
+ $m .= "t";
+ } elsif( $n & 0001 ) {
+ $m .= "x";
+ } else {
+ $m .= "-";
+ }
+
+ return $m;
+}
+
+sub ls {
+ my ($file,$path,$mode) = @_;
+
+ if (-f $file) {
+ my @stat = stat(_);
+ # mode, nlink, uid, gid, size, mtime, filename
+ printf "%s %d %d %d %d %s %s\n", $mode || ft($file).fm($stat[2] & 07777),
+ $stat[3], $stat[4], $stat[5], $stat[7], bt($stat[9]), $path;
+ }
+}
+
+$DATE=bt(time());
+
+sub list
+{
+ my ($pkg, $fn, $dn, $sz, $bt);
+
+ my($check,$stats,$config);
+ chop($check = `apt-get -q check 2>/dev/null`);
+ chop($available = `apt-cache dumpavail 2>/dev/null`);
+ chop($stats = `apt-cache stats 2>/dev/null`);
+ chop($config = `apt-config dump 2>&1`);
+ $sz = length($check);
+ print "-r--r--r-- 1 root root $sz $DATE CHECK\n";
+ $sz = length($available);
+ print "-r--r--r-- 1 root root $sz $DATE AVAILABLE\n";
+ $sz = length($stats);
+ print "-r--r--r-- 1 root root $sz $DATE STATS\n";
+ $sz = length($config);
+ print "-r--r--r-- 1 root root $sz $DATE CONFIG\n";
+ $sz = length($pressupdate);
+ print "-r-xr--r-- 1 root root $sz $DATE UPDATE\n";
+ $sz = length($pressupgrade);
+ print "-r-xr--r-- 1 root root $sz $DATE UPGRADE\n";
+ print "-r-xr--r-- 1 root root $sz $DATE DIST-UPGRADE\n";
+
+ ls("/etc/apt/sources.list","sources.list");
+ ls('/etc/apt/apt.conf','apt.conf') if (-f '/etc/apt/apt.conf');
+
+ print "drwxr-xr-x 1 root root 0 $DATE all\n";
+
+ if ( open(PIPEIN, "find /var/cache/apt/archives -type f |") ) {
+ while(<PIPEIN>) {
+ chop;
+ next if /\/lock$/;
+ my $file = $_;
+ s%/var/cache/apt/archives/%CACHE/%;
+ ls($file, $_);
+ }
+ close PIPEIN;
+ }
+
+ my %sects = ();
+ my %debd = ();
+ my %deba = ();
+
+ open STAT, "/var/lib/dpkg/status"
+ or exit 1;
+ while( <STAT> ) {
+ chop;
+ if( /^([\w-]*): (.*)/ ) {
+ $pkg = $2 if( lc($1) eq 'package' );
+ $debd{$pkg}{lc($1)} = $2;
+ }
+ }
+ close STAT;
+
+ foreach $pkg (sort keys %debd) {
+ next if $debd{$pkg}{status} =~ /not-installed/;
+ $fn = $debd{$pkg}{package}. "_". $debd{$pkg}{version};
+ $dn = $debd{$pkg}{section};
+ if( ! $dn ) {
+ $dn = "unknown";
+ } elsif( $dn =~ /^(non-us)$/i ) {
+ $dn .= "/main";
+ } elsif( $dn !~ /\// ) {
+ $dn = "main/". $dn;
+ }
+ unless( $sects{$dn} ) {
+ my $sub = $dn;
+ while( $sub =~ s!^(.*)/[^/]*$!$1! ) {
+ unless( $sects{$sub} ) {
+ print "drwxr-xr-x 1 root root 0 $DATE $sub/\n";
+ $sects{$sub} = 1;
+ }
+ }
+ print "drwxr-xr-x 1 root root 0 $DATE $dn/\n";
+ $sects{$dn} = 1;
+ }
+ $sz = $debd{$pkg}{'status'} =~ /config-files/ ? 0 : $debd{$pkg}{'installed-size'} * 1024;
+ @stat = stat("/var/lib/dpkg/info/".$debd{$pkg}{package}.".list");
+ $bt = bt($stat[9]);
+ print "-rw-r--r-- 1 root root $sz $bt $dn/$fn.debd\n";
+ print "lrwxrwxrwx 1 root root $sz $bt all/$fn.debd -> ../$dn/$fn.debd\n";
+ }
+
+ open STAT, "apt-cache dumpavail |"
+ or exit 1;
+ while( <STAT> ) {
+ chop;
+ if( /^([\w-]*): (.*)/ ) {
+ $pkg = $2 if( lc($1) eq 'package' );
+ $deba{$pkg}{lc($1)} = $2;
+ }
+ }
+ close STAT;
+
+ foreach $pkg (sort keys %deba) {
+ next if $deba{$pkg}{version} eq $debd{$pkg}{version};
+ $fn = $deba{$pkg}{package}. "_". $deba{$pkg}{version};
+ $dn = $deba{$pkg}{section};
+ if( ! $dn ) {
+ $dn = "unknown";
+ } elsif( $dn =~ /^(non-us)$/i ) {
+ $dn .= "/main";
+ } elsif( $dn !~ /\// ) {
+ $dn = "main/". $dn;
+ }
+ unless( $sects{$dn} ) {
+ my $sub = $dn;
+ while( $sub =~ s!^(.*)/[^/]*$!$1! ) {
+ unless( $sects{$sub} ) {
+ print "drwxr-xr-x 1 root root 0 $DATE $sub/\n";
+ $sects{$sub} = 1;
+ }
+ }
+ print "drwxr-xr-x 1 root root 0 $DATE $dn/\n";
+ $sects{$dn} = 1;
+ }
+ $sz = $deba{$pkg}{'status'} =~ /config-files/ ? 0 : $deba{$pkg}{'installed-size'} * 1024;
+ print "-rw-r--r-- 1 root root $sz $DATE $dn/$fn.deba\n";
+ print "lrwxrwxrwx 1 root root $sz $DATE all/$fn.deba -> ../$dn/$fn.deba\n";
+ }
+}
+
+sub copyout
+{
+ my($archive,$filename) = @_;
+ my $qarchive = quote($archive);
+ my $qfilename = quote($filename);
+ if( $archive eq 'CHECK' ) {
+ system("apt-get -q check > $qfilename");
+ } elsif( $archive eq 'AVAILABLE' ) {
+ system("apt-cache dumpavail > $qfilename");
+ } elsif( $archive eq 'STATS' ) {
+ system("apt-cache stats > $qfilename");
+ } elsif( $archive eq 'CONFIG' ) {
+ system("(apt-config dump 2>&1) > $qfilename");
+ } elsif( $archive eq 'UPDATE' ) {
+ open O, ">$filename";
+ print O $pressupdate;
+ close O;
+ } elsif( $archive eq 'UPGRADE' || $archive eq 'DIST-UPGRADE' ) {
+ open O, ">$filename";
+ print O $pressupgrade;
+ close O;
+ } elsif( $archive eq 'apt.conf' ) {
+ system("cp /etc/apt/apt.conf $qfilename");
+ } elsif( $archive eq 'sources.list' ) {
+ system("cp /etc/apt/sources.list $qfilename");
+ } elsif( $archive =~ /^CACHE\// ) {
+ $archive =~ s%^CACHE/%/var/cache/apt/archives/%;
+ system("cp $qarchive $qfilename");
+ } else {
+ open O, ">$filename";
+ print O $archive, "\n";
+ close O;
+ }
+}
+
+sub copyin
+{
+ my($archive,$filename) = @_;
+ my $qarchive = quote($archive);
+ my $qfilename = quote($filename);
+ if( $archive =~ /\.deb$/ ) {
+ system("dpkg -i $qfilename>/dev/null");
+ } elsif( $archive eq 'apt.conf' ) {
+ system("cp $qfilename /etc/apt/apt.conf");
+ } elsif( $archive eq 'sources.list' ) {
+ system("cp $qfilename /etc/apt/sources.list");
+ } elsif( $archive =~ /^CACHE\// ) {
+ $qarchive =~ s%^CACHE/%/var/cache/apt/archives/%;
+ system("cp $qfilename $qarchive");
+ } else {
+ die "extfs: cannot create regular file \`$archive\': Permission denied\n";
+ }
+}
+
+sub run
+{
+ my($archive,$filename) = @_;
+ if( $archive eq 'UPDATE' ) {
+ system("apt-get update");
+ } elsif( $archive eq 'UPGRADE' ) {
+ system("apt-get upgrade -u");
+ } elsif( $archive eq 'DIST-UPGRADE' ) {
+ system("apt-get dist-upgrade -u");
+ } else {
+ die "extfs: $archive: command not found\n";
+ }
+}
+
+sub rm
+{
+ my($archive) = @_;
+ my $qarchive = quote($archive);
+ if( $archive =~ /^CACHE\// ) {
+ $qarchive =~ s%^CACHE/%/var/cache/apt/archives/%;
+ system("rm -f $qarchive");
+ } elsif( $archive eq 'apt.conf' ) {
+ system("rm -f /etc/apt/apt.conf");
+ } elsif( $archive eq 'sources.list' ) {
+ system("rm -f /etc/apt/sources.list");
+ } elsif( $archive =~ /\.debd?$/ ) {
+ # uncommented and changed to use dpkg - alpha
+ my $qname = $qarchive;
+ $qname =~ s%.*/%%g;
+ $qname =~ s%_.*%%g;
+ system("dpkg --remove $qname >/dev/null");
+ die("extfs: $archive: Operation not permitted\n") if $? != 0;
+ } else {
+ die "extfs: $archive: Operation not permitted\n";
+ }
+}
+
+
+$pressupdate=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you don't want to retrieve new lists of packages.
+ ==========================================================================
+
+This is not a real file. It is a way to retrieve new lists of packages.
+To update this information go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressupgrade=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to perform an upgrade.
+ ===================================================================
+
+This is not a real file. It is a way to perform an upgrade.
+To upgrade this information go back to the panel and press Enter on this file.
+
+EOInstall
+
+
+# override any locale for dates
+$ENV{"LC_ALL"}="C";
+
+if ($ARGV[0] eq "list") { list(); exit(0); }
+elsif ($ARGV[0] eq "copyout") { copyout($ARGV[2], $ARGV[3]); exit(0); }
+elsif ($ARGV[0] eq "copyin") { copyin($ARGV[2], $ARGV[3]); exit(0); }
+elsif ($ARGV[0] eq "run") { run($ARGV[2]); exit(0); }
+elsif ($ARGV[0] eq "rm") { rm($ARGV[2]); exit(0); }
+exit(1);
+
diff --git a/src/vfs/extfs/helpers/audio.in b/src/vfs/extfs/helpers/audio.in
new file mode 100755
index 0000000..05c8c65
--- /dev/null
+++ b/src/vfs/extfs/helpers/audio.in
@@ -0,0 +1,53 @@
+#! /bin/sh
+#
+# Written by Pavel Machek
+# CDDB support by Adam Byrtek
+#
+# (C) 2000 The Free Software Foundation.
+#
+
+set -e
+
+CDDB_SERVER="http://freedb.freedb.org"
+CDDB_HANDSHAKE="hello=user+localhost+mc+1.0&proto=1"
+CDDB_TIMEOUT=20 # in seconds
+
+audiofs_list()
+{
+ DATE=`date +"%b %d %H:%M"`
+ echo "-r--r--r-- 1 0 0 0 $DATE CDDB"
+ cdparanoia -Q -d "$1" 2>&1 | grep '^[ 0-9][ 0-9][ 0-9]\.' | while read A B C
+ do
+ A=`echo "$A" | sed -e 's/\.//' -e 's/^\(.\)$/0\1/'`
+ SIZE=`expr 44 + $B \* 2352`
+ echo "-r--r--r-- 1 0 0 $SIZE $DATE track-${A}.wav"
+ done
+}
+
+audiofs_copyout()
+{
+ if [ x"$2" = x"CDDB" ]; then
+ DISCID=`cd-discid "$1" | tr " " "+"`
+ if [ -z "$DISCID" ]; then
+ exit 1
+ fi
+ RESPONSE=`wget -q -T $CDDB_TIMEOUT -O - "$CDDB_SERVER/~cddb/cddb.cgi?cmd=cddb+query+$DISCID&$CDDB_HANDSHAKE" | tee "$3" | @AWK@ '/^200/ { print $2,$3; }'`
+ wget -q -T $CDDB_TIMEOUT -O - "$CDDB_SERVER/~cddb/cddb.cgi?cmd=cddb+read+$RESPONSE&$CDDB_HANDSHAKE" | grep -v "^#" >> "$3"
+ else
+ TRACK=`echo "$2" | sed 's/track-0*//' | sed 's/\.wav//'`
+ cdparanoia -q -d "$1" "$TRACK" "$3" >/dev/null
+ fi
+}
+
+if [ ! -b "$2" ]
+then
+ BASE="/dev/cdrom"
+else
+ BASE="$2"
+fi
+
+case "$1" in
+ list) audiofs_list "$BASE"; exit 0;;
+ copyout) audiofs_copyout "$BASE" "$3" "$4"; exit 0;;
+esac
+exit 1
diff --git a/src/vfs/extfs/helpers/bpp b/src/vfs/extfs/helpers/bpp
new file mode 100755
index 0000000..f71fe7e
--- /dev/null
+++ b/src/vfs/extfs/helpers/bpp
@@ -0,0 +1,50 @@
+#! /bin/sh
+#
+# Written by Marco Ciampa 2000
+# (a simple cut & paste from rpm vfs)
+# (C) 1996 The Free Software Foundation.
+#
+# Package of a new italian distribution: Bad Penguin
+# http://www.badpenguin.org/
+
+# override any locale for dates
+unset LC_ALL
+LC_TIME=C
+export LC_TIME
+
+mcbppfs_list ()
+{
+ FILEPREF="-r--r--r-- 1 root root "
+ FIEXPREF="-r-xr-xr-x 1 root root "
+ DATE=`date +"%b %d %H:%M"`
+ set x `ls -l "$1"`
+ size=$6
+ echo "$FILEPREF $size $DATE CONTENTS.tar.gz"
+ echo "$FIEXPREF 35 $DATE INSTALL"
+ echo "$FIEXPREF 35 $DATE UPGRADE"
+}
+
+mcbppfs_copyout ()
+{
+ case "$2" in
+ CONTENTS.tar.gz) cat "$1" > "$3"; exit 0;;
+ INSTALL) echo "# Run this to install this package" > "$3"; exit 0;;
+ UPGRADE) echo "# Run this to upgrade this package" > "$3"; exit 0;;
+ esac
+}
+
+mcbppfs_run ()
+{
+ case "$2" in
+ INSTALL) echo "Installing \"$1\""; package-setup --install "$1"; exit 0;;
+ UPGRADE) echo "Upgrading \"$1\""; package-setup --update "$1"; exit 0;;
+ esac
+}
+
+umask 077
+case "$1" in
+ list) mcbppfs_list "$2"; exit 0;;
+ copyout) mcbppfs_copyout "$2" "$3" "$4"; exit 0;;
+ run) mcbppfs_run "$2" "$3"; exit 1;;
+esac
+exit 1
diff --git a/src/vfs/extfs/helpers/changesetfs b/src/vfs/extfs/helpers/changesetfs
new file mode 100755
index 0000000..eebdf8c
--- /dev/null
+++ b/src/vfs/extfs/helpers/changesetfs
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+LANG=C
+export LANG
+LC_TIME=C
+export LC_TIME
+
+# --- GIT -----------------------------------------------------------------------
+
+found_git_dir()
+{
+ work_dir=$1
+ while [ -n "$work_dir" -a "$work_dir" != "/" ]; do
+ [ -d "${work_dir}/.git" ] && {
+ echo "${work_dir}/.git/"
+ return
+ }
+ work_dir=`dirname "$work_dir"`
+ done
+ echo ''
+}
+
+changesetfs_list_git()
+{
+ WORK_DIR=$1; shift
+ fname=$1; shift
+ USER=$1; shift
+ DATE=$1; shift
+
+ GIT_DIR=`found_git_dir "$WORK_DIR"`
+ [ -z "$GIT_DIR" ] && GIT_DIR=$WORK_DIR
+ curr_year=`date +"%Y"`
+
+ git --git-dir="$GIT_DIR" log --abbrev=7 --pretty="format:%at %h %an" -- "$fname" | while read TIMESTAMP chset author
+ do
+ year=`date -d @"$TIMESTAMP" +"%Y"`
+ [ "$year" = "$curr_year" ] && {
+ DATE=`date -d @"$TIMESTAMP" +"%b %d %H:%M"`
+ } || {
+ DATE=`date -d @"$TIMESTAMP" +"%b %d %Y"`
+ }
+ NAME="$chset $author"
+ echo "-rw-rw-rw- 1 $USER 0 0 $DATE $NAME `basename $fname`"
+ done
+}
+
+changesetfs_copyout_git()
+{
+ WORK_DIR=$1; shift
+ fname=$1; shift
+ orig_fname=$1;shift
+ output_fname=$1;shift
+
+ chset=`echo "$orig_fname"| cut -f 1 -d " "`
+
+ GIT_DIR=`found_git_dir "$WORK_DIR"`
+ [ -z "$GIT_DIR" ] && GIT_DIR=$WORK_DIR
+
+ filecommit=`git --git-dir="$GIT_DIR" show --raw --pretty=tformat:%h "$chset" -- "$fname"| \
+ tail -n1 | \
+ sed 's@^::[0-9]*\s*[0-9]*\s*[0-9]*\s*@@' | \
+ sed 's@^:[0-9]*\s*[0-9]*\s*@@' | \
+ cut -d'.' -f 1`
+ git --git-dir="$GIT_DIR" show "$filecommit" > "$output_fname"
+}
+
+# --- COMMON --------------------------------------------------------------------
+
+changesetfs_list()
+{
+ VCS_type=$1; shift
+ WORK_DIR=$1; shift
+ fname=$1; shift
+
+ DATE=`date +"%b %d %H:%M"`
+ USER=`whoami`
+
+ case "$VCS_type" in
+ git) changesetfs_list_git "$WORK_DIR" "$fname" "$USER" "$DATE" ;;
+ esac
+}
+
+changesetfs_copyout()
+{
+ VCS_type=$1; shift
+ WORK_DIR=$1; shift
+ fname=$1; shift
+
+ case "$VCS_type" in
+ git) changesetfs_copyout_git "$WORK_DIR" "$fname" "$@" ;;
+ esac
+
+}
+
+# --- MAIN ----------------------------------------------------------------------
+
+command=$1; shift
+tmp_file=$1; shift
+
+WORK_DIR=`head -n1 $tmp_file`
+fname=`tail -n2 $tmp_file | head -n1`
+VCS_type=`tail -n1 $tmp_file`
+
+case "$command" in
+ list) changesetfs_list "$VCS_type" "$WORK_DIR" "$fname" ;;
+ copyout) changesetfs_copyout "$VCS_type" "$WORK_DIR" "$fname" "$@" ;;
+ *) exit 1 ;;
+esac
+exit 0
diff --git a/src/vfs/extfs/helpers/deb.in b/src/vfs/extfs/helpers/deb.in
new file mode 100644
index 0000000..abc98aa
--- /dev/null
+++ b/src/vfs/extfs/helpers/deb.in
@@ -0,0 +1,203 @@
+#! @PERL@
+#
+# Written by Fernando Alegre <alegre@debian.org> 1996
+#
+# Applied patch by Dimitri Maziuk <emaziuk@curtin.edu.au> 1997
+# (to handle new tar format)
+#
+# Modified by Fernando Alegre <alegre@debian.org> 1997
+# (to handle both new and old tar formats)
+#
+# Modified by Patrik Rak <prak@post.cz> 1998
+# (add by Michael Bramer Debian-mc-maintainer <grisu@debian.org>)
+# (to allow access to package control files)
+#
+# Modified by Martin Bialasinski <martinb@debian.org> 1999
+# (deal with change in tar format)
+#
+#
+# Copyright (C) 1997 Free Software Foundation
+#
+
+sub quote {
+ $_ = shift(@_);
+ s/([^\w\/.+-])/\\$1/g;
+ return($_);
+}
+
+sub mcdebfs_list
+{
+#
+# CAVEAT: Hard links are listed as if they were symlinks
+# Empty directories do not appear at all
+#
+ local($archivename)=@_;
+ local $qarchivename = quote($archivename);
+ chop($date=`LC_ALL=C date "+%b %d %H:%M"`);
+ chop($info_size=`dpkg -I $qarchivename | wc -c`);
+ $install_size=length($pressinstall);
+
+ print "dr-xr-xr-x 1 root root 0 $date CONTENTS\n";
+ print "dr-xr-xr-x 1 root root 0 $date DEBIAN\n";
+ print "-r--r--r-- 1 root root $info_size $date INFO\n";
+ print "-r-xr--r-- 1 root root $install_size $date INSTALL\n";
+
+ if ( open(PIPEIN, "LC_ALL=C dpkg-deb -c $qarchivename |") )
+ {
+ while(<PIPEIN>)
+ {
+ @_ = split;
+
+ $perm=$_[0]; $owgr=$_[1]; $size=$_[2];
+ if($_[3] =~ /^\d\d\d\d\-/) { # New tar format
+
+ ($year,$mon,$day) = split(/-/,$_[3]);
+ $month = ("Gee","Jan","Feb","Mar","Apr","May","Jun",
+ "Jul","Aug","Sep","Oct","Nov","Dec")[$mon] || "Gee";
+ $time=$_[4];
+ $pathindex=5;
+ }
+ else {
+ $mstring='GeeJanFebMarAprMayJunJulAugSepOctNovDec';
+ $month=$_[3];
+ $mon=index($mstring,$month) / 3;
+ $day=$_[4];
+ $time=$_[5];
+ $year=$_[6];
+ $pathindex=7;
+ }
+
+ $path=$_[$pathindex++];
+ # remove leading ./
+ $path=~s/^\.\///;
+ next if ($path eq '');
+ $arrow=$_[$pathindex++];
+ $link=$_[$pathindex++];
+ $link2=$_[$pathindex++];
+
+ $owgr=~s!/! !;
+ if($arrow eq 'link')
+ {
+# report hard links as soft links
+ $arrow='->'; $link="/$link2";
+ substr($perm, 0, 1) = "l";
+ }
+ if($arrow ne '')
+ {
+ $arrow=' ' . $arrow;
+ $link= ' ' . $link;
+ }
+ $now=`date "+%Y %m"`;
+ ($thisyear, $thismon) = split(/ /, $now);
+ # show time for files younger than 6 months
+ # but not for files with dates in the future
+ if ($year * 12 + $mon > $thisyear * 12 + $thismon - 6 &&
+ $year * 12 + $mon <= $thisyear * 12 + $thismon) {
+ print "$perm 1 $owgr $size $month $day $time CONTENTS/$path$arrow$link\n";
+ } else {
+ print "$perm 1 $owgr $size $month $day $year CONTENTS/$path$arrow$link\n";
+ }
+ }
+ }
+ if ( open(PIPEIN, "LC_ALL=C dpkg-deb -I $qarchivename |") )
+ {
+ while(<PIPEIN>)
+ {
+ @_ = split;
+ $size=$_[0];
+ last if $size =~ /:/;
+ next if $size !~ /\d+/;
+ if($_[4] eq '*')
+ {
+ $perm='-r-xr-xr-x';
+ $name=$_[5];
+ }
+ else
+ {
+ $perm='-r--r--r--';
+ $name=$_[4];
+ }
+ print "$perm 1 root root $size $date DEBIAN/$name\n";
+ }
+ }
+}
+
+sub mcdebfs_copyout
+{
+ local($archive,$filename,$destfile)=@_;
+ local $qarchive = quote($archive);
+ local $qfilename = quote($filename);
+ local $qdestfile = quote($destfile);
+
+ if($filename eq "INFO")
+ {
+ system("dpkg-deb -I $qarchive > $qdestfile");
+ }
+ elsif($filename =~ /^DEBIAN/)
+ {
+ $qfilename=~s!^DEBIAN/!!;
+ system("dpkg-deb -I $qarchive $qfilename > $qdestfile");
+ }
+ elsif($filename eq "INSTALL")
+ {
+ if ( open(FILEOUT,">$destfile") )
+ {
+ print FILEOUT $pressinstall;
+ close FILEOUT;
+ system("chmod a+x $qdestfile");
+ }
+ }
+ else
+ {
+ # files can be prepended with ./ or not, depending on the version of tar
+ $qfilename=~s!^CONTENTS/!!;
+ system("dpkg-deb --fsys-tarfile $qarchive | tar xOf - $qfilename ./$qfilename > $qdestfile 2>/dev/null");
+ }
+}
+
+sub mcdebfs_run
+{
+ local($archive,$filename)=@_;
+ local $qarchive = quote($archive);
+ if($filename eq "INSTALL")
+ {
+ print "Installing $archive\n";
+ system("dpkg -i $qarchive");
+ }
+ else
+ {
+ use File::Temp qw(mkdtemp);
+ my $template = "/tmp/mcdebfs.run.XXXXXX";
+ $template="$ENV{MC_TMPDIR}/mcdebfs.XXXXXX" if ($ENV{MC_TMPDIR});
+ $tmpdir = mkdtemp($template);
+ $tmpcmd="$tmpdir/run";
+ &mcdebfs_copyout($archive, $filename, $tmpcmd);
+ system("chmod u+x $tmpcmd");
+ system($tmpcmd);
+ unlink($tmpcmd);
+ rmdir($tmpdir);
+ }
+}
+
+$pressinstall=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to reinstall everything...
+
+This is not a real file. It is a way to install the package you are browsing.
+
+To install this package go back to the panel and press Enter on this file.
+
+In Debian systems, a package is automatically upgraded when you install a new
+version of it. There is no special upgrade option. Install always works.
+
+EOInstall
+
+umask 077;
+
+if($ARGV[0] eq "list") { shift; &mcdebfs_list(@ARGV); exit 0; }
+elsif($ARGV[0] eq "copyout") { shift; &mcdebfs_copyout(@ARGV); exit 0; }
+elsif($ARGV[0] eq "run") { shift; &mcdebfs_run(@ARGV); exit 0; }
+
+exit 1;
+
diff --git a/src/vfs/extfs/helpers/deba.in b/src/vfs/extfs/helpers/deba.in
new file mode 100644
index 0000000..3d1a552
--- /dev/null
+++ b/src/vfs/extfs/helpers/deba.in
@@ -0,0 +1,107 @@
+#! @PERL@
+#
+# 1999 (c) Piotr Roszatycki <dexter@debian.org>
+# This software is under GNU license
+# last modification: 1999-12-08
+#
+# deba
+
+sub quote {
+ $_ = shift(@_);
+ s/([^\w\/.+-])/\\$1/g;
+ return($_);
+}
+
+sub list
+{
+ my($qarchive)=@_;
+ $qarchive = quote($qarchive);
+ chop($date=`LC_ALL=C date "+%m-%d-%Y %H:%M"`);
+ chop($info_size=`apt-cache show $qarchive | wc -c`);
+ $install_size=length($pressinstall);
+ $upgrade_size=length($pressupgrade);
+
+ print "-r--r--r-- 1 root root $info_size $date INFO\n";
+
+ chop($debd = `dpkg -s $qarchive | grep -i ^Version | sed 's/^version: //i'`);
+ chop($deba = `apt-cache show $qarchive | grep -i ^Version | sed 's/^version: //i'`);
+ if( ! $debd ) {
+ print "-r-xr--r-- 1 root root $install_size $date INSTALL\n";
+ } elsif( $debd ne $deba ) {
+ print "-r-xr--r-- 1 root root $upgrade_size $date UPGRADE\n";
+ }
+}
+
+sub copyout
+{
+ my($archive,$filename,$destfile)=@_;
+ my $qarchive = quote($archive);
+ my $qdestfile = quote($destfile);
+ if($filename eq "INFO") {
+ system("apt-cache show $qarchive > $qdestfile");
+ } elsif($filename eq "INSTALL") {
+ if ( open(FILEOUT, "> $destfile") ) {
+ print FILEOUT $pressinstall;
+ close FILEOUT;
+ system("chmod a+x $qdestfile");
+ }
+ } elsif($filename eq "UPGRADE") {
+ if ( open(FILEOUT, ">, $destfile") ) {
+ print FILEOUT $pressupgrade;
+ close FILEOUT;
+ system("chmod a+x $qdestfile");
+ }
+ } else {
+ die "extfs: $filename: No such file or directory\n";
+ }
+}
+
+sub run
+{
+ my($archive,$filename)=@_;
+ my $qarchive = quote($archive);
+ if($filename eq "INSTALL") {
+ system("apt-get install $qarchive");
+ } elsif($filename eq "UPGRADE") {
+ system("apt-get install $qarchive");
+ } else {
+ die "extfs: $filename: Permission denied\n";
+ }
+}
+
+$pressinstall=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to install this package...
+
+This is not a real file. It is a way to install the package you are browsing.
+
+To install this package go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressupgrade=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to upgrade this package...
+
+This is not a real file. It is a way to upgrade the package you are browsing.
+
+To upgrade this package go back to the panel and press Enter on this file.
+
+EOInstall
+
+
+umask 077;
+
+chop($name = `if [ -f "$ARGV[1]" ]; then cat $ARGV[1]; else echo $ARGV[1]; fi`);
+$name =~ s%.*/([0-9a-z.-]*)_.*%$1%;
+
+exit 1 unless $name;
+
+if($ARGV[0] eq "list") { &list($name); exit 0; }
+elsif($ARGV[0] eq "copyout") { &copyout($name,$ARGV[2],$ARGV[3]); exit 0; }
+elsif($ARGV[0] eq "run") { &run($name,$ARGV[2]); exit 0; }
+
+exit 1;
+
diff --git a/src/vfs/extfs/helpers/debd.in b/src/vfs/extfs/helpers/debd.in
new file mode 100644
index 0000000..858dadd
--- /dev/null
+++ b/src/vfs/extfs/helpers/debd.in
@@ -0,0 +1,362 @@
+#! @PERL@
+#
+# 1999 (c) Piotr Roszatycki <dexter@debian.org>
+# This software is under GNU license
+# last modification: 1999-12-08
+#
+# debd
+
+sub quote {
+ $_ = shift(@_);
+ s/([^\w\/.+-])/\\$1/g;
+ return($_);
+}
+
+sub bt
+{
+ my ($dt) = @_;
+ my (@time);
+ @time = localtime($dt);
+ $bt = sprintf "%02d-%02d-%d %02d:%02d", $time[4] + 1, $time[3],
+ $time[5] + 1900, $time[2], $time[1];
+ return $bt;
+}
+
+
+sub ft
+{
+ my ($f) = @_;
+ return "d" if -d $f;
+ return "l" if -l $f;
+ return "p" if -p $f;
+ return "S" if -S $f;
+ return "b" if -b $f;
+ return "c" if -c $f;
+ return "-";
+}
+
+sub fm
+{
+ my ($n) = @_;
+ my ($m);
+
+ if( $n & 0400 ) {
+ $m .= "r";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 0200 ) {
+ $m .= "w";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 04000 ) {
+ $m .= "s";
+ } elsif( $n & 0100 ) {
+ $m .= "x";
+ } else {
+ $m .= "-";
+ }
+
+ if( $n & 0040 ) {
+ $m .= "r";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 0020 ) {
+ $m .= "w";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 02000 ) {
+ $m .= "s";
+ } elsif( $n & 0010 ) {
+ $m .= "x";
+ } else {
+ $m .= "-";
+ }
+
+ if( $n & 0004 ) {
+ $m .= "r";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 0002 ) {
+ $m .= "w";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 01000 ) {
+ $m .= "t";
+ } elsif( $n & 0001 ) {
+ $m .= "x";
+ } else {
+ $m .= "-";
+ }
+
+ return $m;
+}
+
+sub ls {
+ my ($file) = @_;
+ my @stat = stat($file);
+ # mode, nlink, uid, gid, size, mtime, filename
+ printf "%s%s %d %d %d %d %s CONTENTS%s\n", ft($file), fm($stat[2] & 07777),
+ $stat[3], $stat[4], $stat[5], $stat[7], bt($stat[9]), $file;
+}
+
+sub list
+{
+ my($archive)=@_;
+ my $qarchive = quote($archive);
+ chop($date=`LC_ALL=C date "+%m-%d-%Y %H:%M"`);
+ chop($info_size=`dpkg -s $qarchive | wc -c`);
+ $repack_size=length($pressrepack);
+ $reinstall_size=length($pressreinstall);
+ $remove_size=length($pressremove);
+ $purge_size=length($presspurge);
+ $reconfigure_size=length($pressreconfigure);
+ $reinstall_size=length($pressreinstall);
+ $select_size=length($pressselect);
+ $unselect_size=length($pressunselect);
+
+ print "dr-xr-xr-x 1 root root 0 $date CONTENTS\n";
+ print "dr-xr-xr-x 1 root root 0 $date DEBIAN\n";
+ print "-r--r--r-- 1 root root $info_size $date INFO\n";
+ print "-r-xr--r-- 1 root root $purge_size $date DPKG-PURGE\n";
+
+ chop($status = `dpkg -s $qarchive | grep ^Status`);
+ if( $status =~ /deinstall/ ) {
+ print "-r-xr--r-- 1 root root $select_size $date DPKG-SELECT\n";
+ } elsif( $status =~ /install/ ) {
+ print "-r-xr--r-- 1 root root $unselect_size $date DPKG-UNSELECT\n";
+ }
+ if( $status !~ /config-files/ ) {
+ if ( -x "/usr/bin/dpkg-repack" ) {
+ print "-r-xr--r-- 1 root root $repack_size $date DPKG-REPACK\n";
+ }
+ print "-r-xr--r-- 1 root root $remove_size $date DPKG-REMOVE\n";
+ if ( -x "/usr/bin/apt-get" ) {
+ print "-r-xr--r-- 1 root root $remove_size $date APT-REMOVE\n";
+ print "-r-xr--r-- 1 root root $reinstall_size $date APT-REINSTALL\n";
+ print "-r-xr--r-- 1 root root $purge_size $date APT-PURGE\n";
+ }
+ }
+ if( -x "/usr/bin/dpkg-reconfigure" && -x "/var/lib/dpkg/info/$archive.config" ) {
+ print "-r-xr--r-- 1 root root $reconfigure_size $date DPKG-RECONFIGURE\n";
+ }
+
+
+
+ if ( open(PIPEIN, "LC_TIME=C LANG=C ls -l /var/lib/dpkg/info/$qarchive.* |") ) {
+ while(<PIPEIN>) {
+ chop;
+ next if /\.list$/;
+ s%/var/lib/dpkg/info/$archive.%DEBIAN/%;
+ print $_, "\n";
+ }
+ close PIPEIN;
+ }
+
+ if ( open(LIST, "/var/lib/dpkg/info/$archive.list") ) {
+ while(<LIST>) {
+ chop;
+ ls($_);
+ }
+ close LIST;
+ }
+}
+
+sub copyout
+{
+ my($archive,$filename,$destfile)=@_;
+ my $qarchive = quote($archive);
+ my $qfilename = quote($filename);
+ my $qdestfile = quote($destfile);
+
+ if($filename eq "INFO") {
+ system("dpkg -s $qarchive > $qdestfile");
+ } elsif($filename eq "DPKG-REPACK") {
+ if ( open(FILEOUT,">$destfile") ) {
+ print FILEOUT $pressrepack;
+ close FILEOUT;
+ system("chmod a+x $qdestfile");
+ }
+ } elsif($filename =~ /^DEBIAN/) {
+ $qfilename=~s!^DEBIAN/!!;
+ system("cat /var/lib/dpkg/info/$qarchive.$qfilename > $qdestfile");
+ } elsif($filename eq "DPKG-REMOVE" || $filename eq "APT-REMOVE") {
+ if ( open(FILEOUT,">$destfile") ) {
+ print FILEOUT $pressremove;
+ close FILEOUT;
+ system("chmod a+x $qdestfile");
+ }
+ } elsif($filename eq "DPKG-PURGE" || $filename eq "APT-PURGE") {
+ if ( open(FILEOUT,">$destfile") ) {
+ print FILEOUT $presspurge;
+ close FILEOUT;
+ system("chmod a+x $qdestfile");
+ }
+ } elsif($filename eq "DPKG-RECONFIGURE") {
+ if ( open(FILEOUT,">$destfile") ) {
+ print FILEOUT $pressreconfigure;
+ close FILEOUT;
+ system("chmod a+x $qdestfile");
+ }
+ } elsif($filename eq "APT-REINSTALL") {
+ if ( open(FILEOUT,">$destfile") ) {
+ print FILEOUT $pressreinstall;
+ close FILEOUT;
+ system("chmod a+x $destfile");
+ }
+ } elsif($filename eq "DPKG-SELECT") {
+ if ( open(FILEOUT,">$destfile") ) {
+ print FILEOUT $pressselect;
+ close FILEOUT;
+ system("chmod a+x $destfile");
+ }
+ } elsif($filename eq "DPKG-UNSELECT") {
+ if ( open(FILEOUT,">$destfile") ) {
+ print FILEOUT $pressunselect;
+ close FILEOUT;
+ system("chmod a+x $qdestfile");
+ }
+ } else {
+ $qfilename=~s!^CONTENTS!!;
+ system("cat $qfilename > $qdestfile");
+ }
+}
+
+sub run
+{
+ my($archive,$filename)=@_;
+ my $qarchive = quote($archive);
+ my $qfilename = quote($filename);
+ if($filename eq "DPKG-REMOVE") {
+ system("dpkg --remove $qarchive");
+ } elsif($filename eq "APT-REMOVE") {
+ system("apt-get remove $qarchive");
+ } elsif($filename eq "DPKG-PURGE") {
+ system("dpkg --purge $qarchive");
+ } elsif($filename eq "APT-PURGE") {
+ system("apt-get --purge remove $qarchive");
+ } elsif($filename eq "DPKG-REPACK") {
+ system("dpkg-repack $qarchive");
+ } elsif($filename eq "DPKG-SELECT") {
+ system("echo $aqrchive install | dpkg --set-selections");
+ } elsif($filename eq "DPKG-UNSELECT") {
+ system("echo $qarchive deinstall | dpkg --set-selections");
+ } elsif($filename eq "APT-REINSTALL") {
+ system("apt-get -u --reinstall install $qarchive");
+ } elsif($filename eq "DPKG-RECONFIGURE") {
+ system("dpkg-reconfigure $qarchive");
+ } elsif($filename=~/^DEBIAN/) {
+ $qfilename=~s!^DEBIAN!!;
+ system("/var/lib/dpkg/info/$qarchive.$qfilename");
+ } else {
+ $qfilename=~s!^CONTENTS!!;
+ system($qfilename);
+ }
+}
+
+$pressrepack=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to repack this package...
+
+This is not a real file. It is a way to repack the package you are browsing.
+
+To repack this package go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressreinstall=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to reinstall this package...
+
+This is not a real file. It is a way to reinstall the package you are browsing.
+
+To reinstall this package go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressremove=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to remove this package...
+
+This is not a real file. It is a way to remove the package you are browsing.
+
+To remove this package go back to the panel and press Enter on this file.
+
+EOInstall
+
+$presspurge=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to purge this package...
+
+This is not a real file. It is a way to purge the package you are browsing.
+
+To purge this package go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressreconfigure=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to reconfigure this package...
+
+This is not a real file. It is a way to reconfigure the package you are browsing.
+
+To reconfigure this package go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressreinstall=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to reinstall this package...
+
+This is not a real file. It is a way to reinstall the package you are browsing.
+
+To reinstall this package go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressselect=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to select this package...
+
+This is not a real file. It is a way to select the package you are browsing.
+
+To select this package go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressunselect=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to unselect this package...
+
+This is not a real file. It is a way to unselect the package you are browsing.
+
+To unselect this package go back to the panel and press Enter on this file.
+
+EOInstall
+
+umask 077;
+
+chop($name = `if [ -f "$ARGV[1]" ]; then cat $ARGV[1]; else echo $ARGV[1]; fi`);
+$name =~ s%.*/([0-9a-z.-]*)_.*%$1%;
+
+exit 1 unless $name;
+
+if($ARGV[0] eq "list") { &list($name); exit 0; }
+elsif($ARGV[0] eq "copyout") { &copyout($name,$ARGV[2],$ARGV[3]); exit 0; }
+elsif($ARGV[0] eq "run") { &run($name,$ARGV[2]); exit 0; }
+
+exit 1;
+
diff --git a/src/vfs/extfs/helpers/dpkg+.in b/src/vfs/extfs/helpers/dpkg+.in
new file mode 100644
index 0000000..048862e
--- /dev/null
+++ b/src/vfs/extfs/helpers/dpkg+.in
@@ -0,0 +1,337 @@
+#! @PERL@
+#
+# 1999 (c) Piotr Roszatycki <dexter@debian.org>
+# This software is under GNU license
+# last modification: 1999-12-08
+#
+# dpkg
+
+sub quote {
+ $_ = shift(@_);
+ s/([^\w\/.+-])/\\$1/g;
+ return($_);
+}
+
+sub bt
+{
+ my ($dt) = @_;
+ my (@time);
+ @time = localtime($dt);
+ $bt = sprintf "%02d-%02d-%d %02d:%02d", $time[4] + 1, $time[3],
+ $time[5] + 1900, $time[2], $time[1];
+ return $bt;
+}
+
+
+sub ft
+{
+ my ($f) = @_;
+ return "d" if -d $f;
+ return "l" if -l $f;
+ return "p" if -p $f;
+ return "S" if -S $f;
+ return "b" if -b $f;
+ return "c" if -c $f;
+ return "-";
+}
+
+sub fm
+{
+ my ($n) = @_;
+ my ($m);
+
+ if( $n & 0400 ) {
+ $m .= "r";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 0200 ) {
+ $m .= "w";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 04000 ) {
+ $m .= "s";
+ } elsif( $n & 0100 ) {
+ $m .= "x";
+ } else {
+ $m .= "-";
+ }
+
+ if( $n & 0040 ) {
+ $m .= "r";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 0020 ) {
+ $m .= "w";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 02000 ) {
+ $m .= "s";
+ } elsif( $n & 0010 ) {
+ $m .= "x";
+ } else {
+ $m .= "-";
+ }
+
+ if( $n & 0004 ) {
+ $m .= "r";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 0002 ) {
+ $m .= "w";
+ } else {
+ $m .= "-";
+ }
+ if( $n & 01000 ) {
+ $m .= "t";
+ } elsif( $n & 0001 ) {
+ $m .= "x";
+ } else {
+ $m .= "-";
+ }
+
+ return $m;
+}
+
+sub ls {
+ my ($file,$path,$mode) = @_;
+
+ if (-f $file) {
+ my @stat = stat(_);
+ # mode, nlink, uid, gid, size, mtime, filename
+ printf "%s %d %d %d %d %s %s\n", $mode || ft($file).fm($stat[2] & 07777),
+ $stat[3], $stat[4], $stat[5], $stat[7], bt($stat[9]), $path;
+ }
+}
+
+$DATE=bt(time());
+
+sub list
+{
+ my ($pkg, $fn, $dn, $sz, $bt);
+ my %debs = ();
+ my %sects = ();
+
+ my($diversions,$architecture);
+ chop($diversions = `dpkg-divert --list 2>/dev/null`);
+ chop($architecture = `dpkg-architecture 2>/dev/null`);
+ chop($list = `dpkg -l '*' 2>/dev/null`);
+ chop($getselections = `dpkg --get-selections 2>/dev/null`);
+ chop($audit = `dpkg --audit 2>/dev/null`);
+ $sz = length($diversions);
+ print "-r--r--r-- 1 root root $sz $DATE DIVERSIONS\n";
+ $sz = length($architecture);
+ print "-r--r--r-- 1 root root $sz $DATE ARCHITECTURE\n";
+ $sz = length($list);
+ print "-r--r--r-- 1 root root $sz $DATE LIST\n";
+ $sz = length($getselections);
+ print "-r--r--r-- 1 root root $sz $DATE GET-SELECTIONS\n";
+ $sz = length($audit);
+ print "-r--r--r-- 1 root root $sz $DATE AUDIT\n";
+ $sz = length($pressconfigure);
+ print "-r-xr--r-- 1 root root $sz $DATE CONFIGURE\n";
+ $sz = length($pressremove);
+ print "-r-xr--r-- 1 root root $sz $DATE REMOVE\n";
+ $sz = length($pressclearavail);
+ print "-r-xr--r-- 1 root root $sz $DATE CLEAR-AVAIL\n";
+ $sz = length($pressforgetoldunavail);
+ print "-r-xr--r-- 1 root root $sz $DATE FORGET-OLD-UNAVAIL\n";
+ ls("/var/lib/dpkg/status","STATUS","-r--r--r--");
+ # ls("/var/lib/dpkg/available","AVAILABLE","-r--r--r--");
+
+ print "drwxr-xr-x 1 root root 0 $DATE all\n";
+
+ open STAT, "/var/lib/dpkg/status"
+ or exit 1;
+ while( <STAT> ) {
+ chop;
+ if( /^([\w-]*): (.*)/ ) {
+ $pkg = $2 if( lc($1) eq 'package' );
+ $debs{$pkg}{lc($1)} = $2;
+ }
+ }
+ close STAT;
+
+ foreach $pkg (sort keys %debs) {
+ next if $debs{$pkg}{status} =~ /not-installed/;
+ $fn = $debs{$pkg}{package}. "_". $debs{$pkg}{version};
+ $dn = $debs{$pkg}{section};
+ if( ! $dn ) {
+ $dn = "unknown";
+ } elsif( $dn =~ /^(non-us)$/i ) {
+ $dn .= "/main";
+ } elsif( $dn !~ /\// ) {
+ $dn = "main/". $dn;
+ }
+ unless( $sects{$dn} ) {
+ my $sub = $dn;
+ while( $sub =~ s!^(.*)/[^/]*$!$1! ) {
+ unless( $sects{$sub} ) {
+ print "drwxr-xr-x 1 root root 0 $DATE $sub/\n";
+ $sects{$sub} = 1;
+ }
+ }
+ print "drwxr-xr-x 1 root root 0 $DATE $dn/\n";
+ $sects{$dn} = 1;
+ }
+ $sz = $debs{$pkg}{'status'} =~ /config-files/ ? 0 : $debs{$pkg}{'installed-size'} * 1024;
+ @stat = stat("/var/lib/dpkg/info/".$debs{$pkg}{package}.".list");
+ $bt = bt($stat[9]);
+ print "-rw-r--r-- 1 root root $sz $bt $dn/$fn.debd\n";
+ print "lrwxrwxrwx 1 root root $sz $bt all/$fn.debd -> ../$dn/$fn.debd\n";
+ }
+}
+
+sub copyout
+{
+ my($archive,$filename) = @_;
+ my $qfilename = quote($filename);
+ if( $archive eq 'DIVERSIONS' ) {
+ system("dpkg-divert --list > $qfilename 2>/dev/null");
+ } elsif( $archive eq 'ARCHITECTURE' ) {
+ system("dpkg-architecture > $qfilename 2>/dev/null");
+ } elsif( $archive eq 'LIST' ) {
+ system("dpkg -l '*' > $qfilename 2>/dev/null");
+ } elsif( $archive eq 'AUDIT' ) {
+ system("dpkg --audit > $qfilename 2>/dev/null");
+ } elsif( $archive eq 'GET-SELECTIONS' ) {
+ system("dpkg --get-selections > $qfilename 2>/dev/null");
+ } elsif( $archive eq 'STATUS' ) {
+ system("cp /var/lib/dpkg/status $qfilename");
+ } elsif( $archive eq 'AVAILABLE' ) {
+ system("cp /var/lib/dpkg/available $qfilename");
+ } elsif( $archive eq 'CONFIGURE' ) {
+ open O, ">$filename";
+ print O $pressconfigure;
+ close O;
+ } elsif( $archive eq 'REMOVE' ) {
+ open O, ">$filename";
+ print O $pressremove;
+ close O;
+ } elsif( $archive eq 'CLEAR-AVAIL' ) {
+ open O, ">$filename";
+ print O $pressclearavail;
+ close O;
+ } elsif( $archive eq 'FORGET-OLD-UNAVAIL' ) {
+ open O, ">$filename";
+ print O $pressforgetoldunavail;
+ close O;
+ } else {
+ open O, ">$filename";
+ print O $archive, "\n";
+ close O;
+ }
+}
+
+# too noisy but less dangerouse
+sub copyin
+{
+ my($archive,$filename) = @_;
+ my $qfilename = quote($filename);
+ if( $archive =~ /\.deb$/ ) {
+ system("dpkg -i $qfilename>/dev/null");
+ } else {
+ die "extfs: cannot create regular file \`$archive\': Permission denied\n";
+ }
+}
+
+sub run
+{
+ my($archive,$filename) = @_;
+ if( $archive eq 'CONFIGURE' ) {
+ system("dpkg --pending --configure");
+ } elsif( $archive eq 'REMOVE' ) {
+ system("dpkg --pending --remove");
+ } elsif( $archive eq 'CLEAR-AVAIL' ) {
+ system("dpkg --clear-avail");
+ } elsif( $archive eq 'FORGET-OLD-UNAVAIL' ) {
+ system("dpkg --forget-old-unavail");
+ } else {
+ die "extfs: $filename: command not found\n";
+ }
+}
+
+# Disabled - too dangerous and too noisy
+sub rm_disabled
+{
+ my($archive) = @_;
+ if( $archive =~ /\.debd?$/ ) {
+ my $qname = quote($archive);
+ $qname =~ s%.*/%%g;
+ $qname =~ s%_.*%%g;
+ system("if dpkg -s $qname | grep ^Status | grep -qs config-files; \
+ then dpkg --purge $qname>/dev/null; \
+ else dpkg --remove $qname>/dev/null; fi");
+ die("extfs: $archive: Operation not permitted\n") if $? != 0;
+ } else {
+ die "extfs: $archive: Operation not permitted\n";
+ }
+}
+
+
+$pressconfigure=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to configure all
+ non configured packages.
+
+This is not a real file. It is a way to configure all non configured packages.
+
+To configure packages go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressremove=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to remove all
+ unselected packages.
+
+This is not a real file. It is a way to remove all unselected packages.
+
+To remove packages go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressforgetoldunavail=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to forget about
+ uninstalled unavailable packages.
+
+This is not a real file. It is a way to forget about uninstalled
+unavailable packages.
+
+To forget this information go back to the panel and press Enter on this file.
+
+EOInstall
+
+$pressclearavail=<<EOInstall;
+
+ WARNING
+ Don\'t use this method if you are not willing to erase the existing
+ information about what packages are available.
+
+This is not a real file. It is a way to erase the existing information
+about what packages are available.
+
+To clear this information go back to the panel and press Enter on this file.
+
+EOInstall
+
+
+
+# override any locale for dates
+$ENV{"LC_ALL"}="C";
+
+if ($ARGV[0] eq "list") { list(); exit(0); }
+elsif ($ARGV[0] eq "copyout") { copyout($ARGV[2], $ARGV[3]); exit(0); }
+elsif ($ARGV[0] eq "copyin") { copyin($ARGV[2], $ARGV[3]); exit(0); }
+elsif ($ARGV[0] eq "run") { run($ARGV[2],$ARGV[3]); exit(0); }
+#elsif ($ARGV[0] eq "rm") { rm($ARGV[2]); exit(0); }
+exit(1);
+
diff --git a/src/vfs/extfs/helpers/gitfs+ b/src/vfs/extfs/helpers/gitfs+
new file mode 100755
index 0000000..66861fb
--- /dev/null
+++ b/src/vfs/extfs/helpers/gitfs+
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+LANG=C
+export LANG
+LC_TIME=C
+export LC_TIME
+
+umask 077
+prefix='[git]'
+
+gitfs_list()
+{
+ DATE=`date +"%b %d %H:%M"`
+ GIT_DIR="$2/.git"
+ user=`whoami`
+ git ls-files -v -c -m -d | sort -k 2 | uniq -f 1 | while read status fname
+ do
+ [ "$status" = "H" ] && status=" "
+ [ "$status" = "C" ] && status="*"
+ echo "-r--r--r-- 1 $user 0 0 $DATE `dirname $fname`/$prefix$status`basename $fname`"
+ done
+}
+
+gitfs_copyout()
+{
+ printf "%s\n" "$2" > "$4"
+ b=`echo "$prefix"| wc -c`
+ b=`expr "$b" + 1`
+ # remove prefix from file name
+ echo "`dirname "$3"`/`basename "$3" | tail -c+"$b"`" >> "$4"
+ echo "git" >> "$4"
+}
+
+case "$1" in
+ list) gitfs_list "$@" ;;
+ copyout) gitfs_copyout "$@" ;;
+ *) exit 1 ;;
+esac
+exit 0
diff --git a/src/vfs/extfs/helpers/hp48+.in b/src/vfs/extfs/helpers/hp48+.in
new file mode 100644
index 0000000..17c03ab
--- /dev/null
+++ b/src/vfs/extfs/helpers/hp48+.in
@@ -0,0 +1,132 @@
+#!/bin/sh
+#
+# Written by Christofer Edvardsen <ce@earthling.net>, Feb 1998
+#
+# This script makes it possible to view and copy files to/from a hp48
+# (tested with a HP48G and the emulator x48)
+#
+# To use the hp48 external filesystem:
+# - read the relevant parts of your HP48 manual
+# - install kermit
+# - connect the HP48 to your computer or start x48
+# - below change the line which reflects the serial device you use
+# - configure your HP48 (<left shift> - i/o - iopar):
+# port: wire
+# baud: 9600
+# transfer format: binary (fast transfers) or
+# ascii (editable on the pc)
+# - start the server on the HP48: <left shift> - i/o - srvr - serve
+# or the shortcut <right shift> - <right arrow>
+# - on MC's commandline enter "cd hp48://"
+#
+# Make sure you have kermit installed and that it's using the right serial
+# device by changing /dev/ttyXX on the next line
+AWK=@AWK@
+KERMIT=${MC_TEST_EXTFS_LIST_CMD:-"kermit -l /dev/ttyS1 -b 9600"}
+
+NOW=`date +"%m-%d-%Y %H:%M"`
+
+hp48_cmd()
+{
+$KERMIT -C "SET EXIT WARNING OFF,REMOTE $1,QUIT"
+}
+
+hp48_cd()
+{
+(echo SET EXIT WARNING OFF;echo REMOTE HOST HOME
+for HP48_DIR in `echo "$1" | tr '/' ' '`;do
+ if [ "x$HP48_DIR" != "x." ];then echo REMOTE HOST "$HP48_DIR"; fi
+done
+echo QUIT)| $KERMIT -B >/dev/null
+}
+
+#
+# Parses the reply to the DIRECTORY command.
+#
+# Here's an example reply (taken from [1][2]):
+#
+# { HOME } 105617
+# STRAY 185.5 Directory 29225
+# YEN 30.5 Program 53391
+# JYTLIGHT 21848.5 String 62692
+# IOPAR 37.5 List 61074
+#
+# The meaning of the fields (according to [3][4]):
+#
+# { Current_directory } Free_space
+# Object_name Object_size_bytes Object_type Object_CRC
+# ...
+#
+# [1] http://newarea48.tripod.com/kermit.html
+# [2] http://www.hpmuseum.org/forum/thread-4684.html
+# [3] https://groups.google.com/d/msg/comp.sys.hp48/bYTCu9K3k20/YWQfF--W3EEJ
+# [4] http://www.columbia.edu/kermit/hp48.html (also has a link to the HP's user manual).
+#
+hp48_parser()
+{
+HP48_DIRS=
+
+read -r INPUT
+while [ "x$INPUT" != "xEOF" ]
+do
+ set -- $INPUT
+
+ obj_name=$1
+ obj_size=$2
+ obj_type=$3
+
+ obj_size=`echo $obj_size | $AWK '{ print int($0) }'` # Truncates floats to ints; anything else to "0".
+
+ if [ "$obj_size" != "0" ]; then # Skips the 1st reply line (purportedly there aren't zero-size files b/c, according to resource [4], the size is "including name").
+ case "$obj_type" in
+ Directory)
+ HP48_DIRS="$HP48_DIRS $obj_name"
+ printf "%crwxr-xr-x 1 %-8d %-8d %8d %s %s\n" 'd' \
+ 0 0 $obj_size "$NOW" "$HP48_CDIR/$obj_name"
+ ;;
+ *)
+ printf "%crw-r--r-- 1 %-8d %-8d %8d %s %s\n" '-' \
+ 0 0 $obj_size "$NOW" "$HP48_CDIR/$obj_name"
+ ;;
+ esac
+ fi
+
+ read -r INPUT
+done
+
+for HP48_DIR in $HP48_DIRS;
+do
+ HP48_PDIR="$HP48_CDIR"
+ HP48_CDIR="$HP48_CDIR/$HP48_DIR"; hp48_cmd "HOST $HP48_DIR" >/dev/null
+ hp48_list
+ HP48_CDIR="$HP48_PDIR"; hp48_cmd "HOST UPDIR" >/dev/null
+done
+}
+
+hp48_list()
+{
+# It's hard to see why this "EOF" thing is needed. The loop above can be changed to "while read -r obj_name ...". @TODO.
+{ hp48_cmd "DIRECTORY"; echo; echo EOF; } | hp48_parser
+}
+
+# override any locale for dates
+LC_ALL=C
+export LC_ALL
+
+case "$1" in
+list) HP48_CDIR=
+ hp48_cmd "HOST HOME" >/dev/null
+ hp48_list
+ exit 0;;
+copyout)
+ cd "`dirname "$4"`"
+ hp48_cd "`dirname "$3"`"
+ $KERMIT -B -g "`basename "$3"`" -a "$4" >/dev/null
+ exit 0;;
+copyin)
+ cd "`dirname "$4"`"
+ hp48_cd "`dirname "$3"`"
+ $KERMIT -B -s "$4" -a "`basename "$3"`" >/dev/null
+ exit 0;;
+esac
+exit 1
diff --git a/src/vfs/extfs/helpers/iso9660.in b/src/vfs/extfs/helpers/iso9660.in
new file mode 100644
index 0000000..5a6f1d5
--- /dev/null
+++ b/src/vfs/extfs/helpers/iso9660.in
@@ -0,0 +1,235 @@
+#! /bin/sh
+# Midnight Commander - ISO9660 VFS for MC
+# based on lslR by Tomas Novak <tnovak@ipex.cz> April 2000
+#
+# Copyright (C) 2000, 2003
+# The Free Software Foundation, Inc.
+#
+# Written by:
+# Michael Shigorin <mike@altlinux.org>,
+# Grigory Milev <week@altlinux.org>,
+# Kachalov Anton <mouse@linux.ru.net>, 2003
+# Victor Ananjevsky <ananasik@gmail.com>, 2013
+# slava zanko <slavazanko@gmail.com>, 2013
+#
+# 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 section (source functions, for example) *******************
+
+#*** file scope functions **********************************************
+
+XORRISO=$(which xorriso 2>/dev/null)
+
+xorriso_list() {
+ if test -z "$XORRISO"; then
+ return 1
+ fi
+ local dir attr ln usr gr sz dt1 dt2 dt3 nm len name lsl r
+ dir="${2:-/}"
+ lsl=$( $XORRISO -abort_on FATAL -dev stdio:"$1" -cd "$dir" -lsl 2> /dev/null )
+ r=$?
+ test $r -gt 0 && return $r
+
+ echo "$lsl" | grep "^[-d]" | \
+ while read attr ln usr gr sz dt1 dt2 dt3 nm ; do
+ len=$((${#nm} - 1))
+ name=$(printf -- "$nm" | cut -c2-$len) # remove quotes
+
+ if test $(printf -- "$attr" | cut -c1-1) != "d"; then
+ printf -- "%s %s %s %s %s %s %s %s %s/%s\n" "$attr" "$ln" "$usr" "$gr" "$sz" "$dt1" "$dt2" "$dt3" "$dir" "$name"
+ else
+ xorriso_list "$1" "$dir/$name"
+ fi
+ done
+}
+
+xorriso_copyout() {
+ if test -z "$XORRISO"; then
+ return 1
+ fi
+ $XORRISO -dev stdio:"$1" -osirrox on -extract "$2" "$3" >/dev/null 2>&1
+}
+
+xorriso_copyin() {
+ if test -z "$XORRISO"; then
+ return 1
+ fi
+ $XORRISO -dev stdio:"$1" -cpr "$3" "$2" >/dev/null 2>&1
+}
+
+xorriso_mkdir() {
+ if test -z "$XORRISO"; then
+ return 1
+ fi
+ $XORRISO -dev stdio:"$1" -mkdir "$2" >/dev/null 2>&1
+}
+
+xorriso_rmdir() {
+ if test -z "$XORRISO"; then
+ return 1
+ fi
+ $XORRISO -dev stdio:"$1" -rmdir "$2" >/dev/null 2>&1
+}
+
+xorriso_rm() {
+ if test -z "$XORRISO"; then
+ return 1
+ fi
+ $XORRISO -dev stdio:"$1" -rm "$2" >/dev/null 2>&1
+}
+
+# tested to comply with isoinfo 2.0's output
+test_iso () {
+ ISOINFO=$(which isoinfo 2>/dev/null)
+ if test -z "$ISOINFO"; then
+ echo "isoinfo not found" >&2
+ return 1
+ fi
+
+ CHARSET=$(locale charmap 2>/dev/null)
+ if test -z "$CHARSET"; then
+ CHARSET=$(locale 2>/dev/null | grep LC_CTYPE | sed -n -e 's/.*\.\(.*\)"$/\1/p')
+ fi
+ if test -n "$CHARSET"; then
+ CHARSET=$(echo "$CHARSET" | tr '[A-Z]' '[a-z]' | sed -e 's/^iso-/iso/')
+ $ISOINFO -j $CHARSET -i /dev/null 2>&1 | grep "Iconv not yet supported\|Unknown charset" >/dev/null && CHARSET=
+ fi
+ if test -n "$CHARSET"; then
+ JOLIET_OPT="-j $CHARSET -J"
+ else
+ JOLIET_OPT="-J"
+ fi
+
+ ISOINFO_D_I="$($ISOINFO -d -i "$1" 2>/dev/null)"
+ ISOINFO="$ISOINFO -R"
+
+ echo "$ISOINFO_D_I" | grep "UCS level 1\|NO Joliet" > /dev/null || ISOINFO="$ISOINFO $JOLIET_OPT"
+
+ if [ $(echo "$ISOINFO_D_I" | grep "Joliet with UCS level 3 found" | wc -l) = 1 \
+ -a $(echo "$ISOINFO_D_I" | grep "NO Rock Ridge" | wc -l) = 1 ] ; then
+ SEMICOLON="YES"
+ fi
+}
+
+mcisofs_list () {
+ local lsl r
+
+ # left as a reminder to implement compressed image support =)
+ case "$1" in
+ *.lz) MYCAT="lzip -dc";;
+ *.lz4) MYCAT="lz4 -dc";;
+ *.lzma) MYCAT="lzma -dc";;
+ *.xz) MYCAT="xz -dc";;
+ *.zst) MYCAT="zstd -dc";;
+ *.bz2) MYCAT="bzip2 -dc";;
+ *.gz) MYCAT="gzip -dc";;
+ *.z) MYCAT="gzip -dc";;
+ *.Z) MYCAT="gzip -dc";;
+ *) MYCAT="cat";;
+ esac
+
+ lsl=$($ISOINFO -l -i "$1" 2>/dev/null)
+ r=$?
+ test $r -gt 0 && return $r
+
+ echo "$lsl" | @AWK@ -v SEMICOLON=$SEMICOLON '
+BEGIN {
+ dir="";
+ # Pattern to match 8 first fields.
+ rx = "[^ ]+[ ]+";
+ rx = "^" rx rx rx rx rx rx rx rx;
+ irx = "^\\[ *-?[0-9]* *[0-9]+\\] +";
+}
+/^$/ { next }
+/^d---------/ { next }
+/^Directory listing of [^ ].*$/ {
+ dir=substr($0, 23);
+ next;
+}
+{ $11 != "" } {
+ name=$0
+ sub(rx, "", name)
+ attr=substr($0, 1, length($0)-length(name))
+ # strip inodes and extra dir entries; fix perms
+ sub(irx, "", name)
+ sub("^---------- 0 0 0", "-r--r--r-- 1 root root", attr)
+ sub(" $", "", name)
+ # for Joliet UCS level 3
+ if (SEMICOLON == "YES") sub(";1$", "", name);
+ ## sub(";[0-9]+$", "", name) ## would break copyout
+ # skip . and ..
+ if (name == ".") next;
+ if (name == "..") next;
+ printf "%s%s%s\n", attr, dir, name
+}'
+}
+
+mcisofs_copyout () {
+ if [ "x$SEMICOLON" = "xYES" ]; then
+ $ISOINFO -i "$1" -x "/$2;1" 2>/dev/null > "$3"
+ else
+ $ISOINFO -i "$1" -x "/$2" 2>/dev/null > "$3"
+ fi
+}
+
+#*** main code *********************************************************
+
+LC_ALL=C
+
+cmd="$1"
+shift
+
+case "$cmd" in
+ list)
+ xorriso_list "$@" || {
+ test_iso "$@" || exit 1
+ mcisofs_list "$@" || exit 1
+ }
+ exit 0
+ ;;
+ rm)
+ xorriso_rm "$@" || {
+ exit 1
+ }
+ exit 0
+ ;;
+ rmdir)
+ xorriso_rmdir "$@" || {
+ exit 1
+ }
+ exit 0
+ ;;
+ mkdir)
+ xorriso_mkdir "$@" || {
+ exit 1
+ }
+ exit 0
+ ;;
+ copyin)
+ xorriso_copyin "$@" || {
+ exit 1
+ }
+ exit 0
+ ;;
+ copyout)
+ xorriso_copyout "$@" || {
+ test_iso "$@" || exit 1
+ mcisofs_copyout "$@" || exit 1
+ }
+ exit 0
+ ;;
+esac
+exit 1
diff --git a/src/vfs/extfs/helpers/lslR.in b/src/vfs/extfs/helpers/lslR.in
new file mode 100644
index 0000000..69b663b
--- /dev/null
+++ b/src/vfs/extfs/helpers/lslR.in
@@ -0,0 +1,74 @@
+#! /bin/sh
+
+# Based on previous version of lslR
+# Modified by Tomas Novak <tnovak@ipex.cz> April 2000
+# (to allow spaces in filenames)
+#
+# It's assumed that lslR was generated in C locale.
+LC_ALL=C
+export LC_ALL=C
+
+AWK=@AWK@
+
+mclslRfs_list () {
+case "$1" in
+ *.lz) MYCAT="lzip -dc";;
+ *.lz4) MYCAT="lz4 -dc";;
+ *.lzma) MYCAT="lzma -dc";;
+ *.xz) MYCAT="xz -dc";;
+ *.zst) MYCAT="zstd -dc";;
+ *.bz2) MYCAT="bzip2 -dc";;
+ *.gz) MYCAT="gzip -dc";;
+ *.z) MYCAT="gzip -dc";;
+ *.Z) MYCAT="gzip -dc";;
+ *) MYCAT="cat";;
+esac
+
+MYCAT=${MC_TEST_EXTFS_LIST_CMD:-$MYCAT} # Let the test framework hook in.
+
+$MYCAT "$1" | $AWK '
+BEGIN {
+ dir="";
+ empty=1;
+ rx = "[^ ]+[ ]+";
+ # Pattern to match 7 first fields.
+ rx7 = "^" rx rx rx rx rx rx "[^ ]+[ ]";
+ # Pattern to match 8 first fields.
+ rx8 = "^" rx rx rx rx rx rx rx "[^ ]+[ ]";
+}
+/^total\ [0-9]*$/ { next }
+/^$/ { empty=1; next }
+empty==1 && /:$/ {
+ empty=0
+ if ($0 ~ /^\//) dir=substr($0, 2);
+ else dir=$0;
+ if (dir ~ /\/:$/) sub(/:$/, "", dir);
+ else sub(/:$/, "/", dir);
+ if (dir ~ /^[ ]/) dir="./"dir;
+ next;
+}
+( NF > 7 ) {
+ empty=0
+ # gensub() is not portable.
+ name=$0
+ i=index($6, "-")
+ if (i) {
+ sub(rx7, "", name)
+ NF = 7
+ $6=substr($6,i+1)"-"substr($6,1,i-1)
+ }
+ else {
+ sub(rx8, "", name)
+ NF = 8
+ }
+ printf "%s %s%s\n", $0, dir, name
+}
+ {
+ empty=0
+}'
+}
+
+case "$1" in
+ list) mclslRfs_list "$2"; exit 0;;
+esac
+exit 1
diff --git a/src/vfs/extfs/helpers/mailfs.in b/src/vfs/extfs/helpers/mailfs.in
new file mode 100644
index 0000000..5bb373b
--- /dev/null
+++ b/src/vfs/extfs/helpers/mailfs.in
@@ -0,0 +1,219 @@
+#! @PERL@
+
+use bytes;
+use warnings;
+
+# MC extfs for (possibly compressed) Berkeley style mailbox files
+# Peter Daum <gator@cs.tu-berlin.de> (Jan 1998, mc-4.1.24)
+
+$zcat="zcat"; # gunzip to stdout
+$bzcat="bzip2 -dc"; # bunzip2 to stdout
+$lzipcat="lzip -dc"; # unlzip to stdout
+$lz4cat="lz4 -dc"; # unlz4 to stdout
+$lzcat="lzma -dc"; # unlzma to stdout
+$xzcat="xz -dc"; # unxz to stdout
+$zstdcat="zstd -dc"; # unzstd to stdout
+$file="file"; # "file" command
+$TZ='GMT'; # default timezone (for Date module)
+
+if (eval "require Date::Parse") {
+ import Date::Parse;
+ $parse_date=
+ sub {
+ local $ftime = str2time($_[0],$TZ);
+ $_ = localtime($ftime);
+ /^(...) (...) ([ \d]\d) (\d\d:\d\d):\d\d (\d\d\d\d)$/;
+ if ($ftime + 6 * 30 * 24 * 60 * 60 < $now ||
+ $ftime + 60 * 60 > $now) {
+ return "$2 $3 $5";
+ } else {
+ return "$2 $3 $4";
+ }
+ }
+} elsif (eval "require Date::Manip") {
+ import Date::Manip;
+ $parse_date=
+ sub {
+ return UnixDate($_[0], "%l"); # "ls -l" format
+ }
+} else { # use "light" version
+ $parse_date= sub {
+ local $mstring='GeeJanFebMarAprMayJunJulAugSepOctNovDec';
+ # assumes something like: Mon, 5 Jan 1998 16:08:19 +0200 (GMT+0200)
+ # if you have mails with another date format, add it here
+ if (/(\d\d?) ([A-Z][a-z][a-z]) (\d\d\d\d) (\d\d?):(\d\d)/) {
+ $day = $1;
+ $month = $2;
+ $mon = index($mstring,$month) / 3;
+ $year = $3;
+ $hour = $4;
+ $min = $5;
+ # pass time not year for files younger than roughly 6 months
+ # but not for files with dates more than 1-2 hours in the future
+ if ($year * 12 + $mon > $thisyear * 12 + $thismon - 7 &&
+ $year * 12 + $mon <= $thisyear * 12 + $thismon &&
+ ! (($year * 12 + $mon) * 31 + $day ==
+ ($thisyear * 12 + $thismon) * 31 + $thisday &&
+ $hour > $thishour + 2)) {
+ return "$month $day $hour:$min";
+ } else {
+ return "$month $day $year";
+ }
+ }
+ # Y2K bug.
+ # Date: Mon, 27 Mar 100 16:30:47 +0000 (GMT)
+ if (/(\d\d?) ([A-Z][a-z][a-z]) (1?\d\d) (\d\d?):(\d\d)/) {
+ $day = $1;
+ $month = $2;
+ $mon = index($mstring,$month) / 3;
+ $year = 1900 + $3;
+ $hour = $4;
+ $min = $5;
+ if ($year < 1970) {
+ $year += 100;
+ }
+ if ($year * 12 + $mon > $thisyear * 12 + $thismon - 7 &&
+ $year * 12 + $mon <= $thisyear * 12 + $thismon &&
+ ! (($year * 12 + $mon) * 31 + $day ==
+ ($thisyear * 12 + $thismon) * 31 + $thisday &&
+ $hour > $thishour + 2)) {
+ return "$month $day $hour:$min";
+ } else {
+ return "$month $day $year";
+ }
+ }
+ # AOLMail(SM).
+ # Date: Sat Jul 01 10:06:06 2000
+ if (/([A-Z][a-z][a-z]) (\d\d?) (\d\d?):(\d\d)(:\d\d)? (\d\d\d\d)/) {
+ $month = $1;
+ $mon = index($mstring,$month) / 3;
+ $day = $2;
+ $hour = $3;
+ $min = $4;
+ $year = $6;
+ if ($year * 12 + $mon > $thisyear * 12 + $thismon - 7 &&
+ $year * 12 + $mon <= $thisyear * 12 + $thismon &&
+ ! (($year * 12 + $mon) * 31 + $day ==
+ ($thisyear * 12 + $thismon) * 31 + $thisday &&
+ $hour > $thishour + 2)) {
+ return "$month $day $hour:$min";
+ } else {
+ return "$month $day $year";
+ }
+ }
+ # Fallback
+ return $fallback;
+ }
+}
+
+sub process_header {
+ while (<IN>) {
+ $size+=length;
+ s/\r$//;
+ last if /^$/;
+ die "unexpected EOF\n" if eof;
+ if (/^date:\s(.*)$/i) {
+ $date=&$parse_date($1);
+ } elsif (/^subject:\s(.*)$/i) {
+ $subj=lc($1);
+ $subj=~ s/^(re:\s?)+//gi; # no leading Re:
+ $subj=~ tr/a-zA-Z0-9//cd; # strip all "special" characters
+ } elsif (/^from:\s.*?(\w+)\@/i) {
+ $from=$1;
+ } elsif (/^to:\s.*?(\w+)\@/i) {
+ $to=lc($1);
+ }
+ }
+}
+
+sub print_dir_line {
+ $from=$to if ($from eq $user); # otherwise, it would look pretty boring
+ $date=localtime(time) if (!defined $date);
+ printf "-r-------- 1 $< $< %d %s %3.3d_%.25s\n",
+ $size, $date, $msg_nr, "${from}_${subj}";
+
+}
+
+sub mailfs_list {
+ my $blank = 1;
+ $user=$ENV{USER}||getlogin||getpwuid($<) || "nobody";
+
+ while(<IN>) {
+ s/\r$//;
+ if($blank && /^from\s+\w+(\.\w+)*@/i) { # Start of header
+ print_dir_line unless (!$msg_nr);
+ $size=length;
+ $msg_nr++;
+ ($from,$to,$subj,$date)=("none","none","none", "01-01-80");
+ process_header;
+ $line=$blank=0;
+ } else {
+ $size+=length;
+ $line++;
+ $blank= /^$/;
+ }
+ }
+ print_dir_line unless (!$msg_nr);
+ exit 0;
+}
+
+sub mailfs_copyout {
+ my($source,$dest)=@_;
+ exit 1 unless (open STDOUT, ">$dest");
+ ($nr)= ($source =~ /^(\d+)/); # extract message number from "filename"
+
+ my $blank = 1;
+ while(<IN>) {
+ s/\r$//;
+ if($blank && /^from\s+\w+(\.\w+)*@/i) {
+ $msg_nr++;
+ exit(0) if ($msg_nr > $nr);
+ $blank= 0;
+ } else {
+ $blank= /^$/;
+ }
+ print if ($msg_nr == $nr);
+ }
+}
+
+# main {
+exit 1 unless ($#ARGV >= 1);
+$msg_nr=0;
+$cmd=shift;
+$mbox_name=shift;
+my $mbox_qname = quotemeta ($mbox_name);
+$_=`$file $mbox_qname`;
+
+if (/gzip/) {
+ exit 1 unless (open IN, "$zcat $mbox_qname|");
+} elsif (/bzip/) {
+ exit 1 unless (open IN, "$bzcat $mbox_qname|");
+} elsif (/lzip/) {
+ exit 1 unless (open IN, "$lzipcat $mbox_qname|");
+} elsif (/lz4/) {
+ exit 1 unless (open IN, "$lz4cat $mbox_qname|");
+} elsif (/lzma/) {
+ exit 1 unless (open IN, "$lzcat $mbox_qname|");
+} elsif (/xz/) {
+ exit 1 unless (open IN, "$xzcat $mbox_qname|");
+} elsif (/zst/) {
+ exit 1 unless (open IN, "$zstdcat $mbox_qname|");
+} else {
+ exit 1 unless (open IN, "<$mbox_name");
+}
+
+umask 077;
+
+if($cmd eq "list") {
+ $now = time;
+ $_ = localtime($now);
+ /^... (... [ \d]\d \d\d:\d\d):\d\d \d\d\d\d$/;
+ $fallback = $1;
+ $nowstring=`date "+%Y %m %d %H"`;
+ ($thisyear, $thismon, $thisday, $thishour) = split(/ /, $nowstring);
+ &mailfs_list;
+ exit 0;
+}
+elsif($cmd eq "copyout") { &mailfs_copyout(@ARGV); exit 0; }
+
+exit 1;
diff --git a/src/vfs/extfs/helpers/patchfs.in b/src/vfs/extfs/helpers/patchfs.in
new file mode 100644
index 0000000..ee1e651
--- /dev/null
+++ b/src/vfs/extfs/helpers/patchfs.in
@@ -0,0 +1,427 @@
+#! @PERL@
+#
+# Written by Adam Byrtek <alpha@debian.org>, 2002
+# Rewritten by David Sterba <dave@jikos.cz>, 2009
+#
+# Extfs to handle patches in context and unified diff format.
+# Known issues: When name of file to patch is modified during editing,
+# hunk is duplicated on copyin. It is unavoidable.
+
+use bytes;
+use strict;
+use warnings;
+use POSIX;
+use File::Temp 'tempfile';
+
+# standard binaries
+my $lzip = 'lzip';
+my $lz4 = 'lz4';
+my $lzma = 'lzma';
+my $xz = 'xz';
+my $zstd = 'zstd';
+my $bzip = 'bzip2';
+my $gzip = 'gzip';
+my $fileutil = 'file -b';
+
+# date parsing requires Date::Parse from TimeDate module
+my $parsedates = eval 'require Date::Parse';
+
+# regular expressions
+my $unified_header=qr/^--- .*\t.*\n\+\+\+ .*\t.*\n$/;
+my $unified_extract=qr/^--- ([^\t]+).*\n\+\+\+ ([^\t]+)\s*(.*)\n/;
+my $unified_header2=qr/^--- .*\n\+\+\+ .*\n$/;
+my $unified_extract2=qr/^--- ([^\s]+).*\n\+\+\+ ([^\s]+)\s*(.*)\n/;
+my $unified_contents=qr/^([+\-\\ \n]|@@ .* @@)/;
+my $unified_hunk=qr/@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@.*\n/;
+
+my $context_header=qr/^\*\*\* .*\t.*\n--- .*\t.*\n$/;
+my $context_extract=qr/^\*\*\* ([^\t]+).*\n--- ([^\t]+)\s*(.*)\n/;
+my $context_header2=qr/^\*\*\* .*\n--- .*\n$/;
+my $context_extract2=qr/^\*\*\* ([^\s]+).*\n--- ([^\s]+)\s*(.*)\n/;
+my $context_contents=qr/^([!+\-\\ \n]|-{3} .* -{4}|\*{3} .* \*{4}|\*{15})/;
+
+my $ls_extract_id=qr/^[^\s]+\s+[^\s]+\s+([^\s]+)\s+([^\s]+)/;
+my $basename=qr|^(.*/)*([^/]+)$|;
+
+sub patchfs_canonicalize_path ($) {
+ my ($fname) = @_;
+ $fname =~ s,/+,/,g;
+ $fname =~ s,(^|/)(?:\.?\./)+,$1,;
+ return $fname;
+}
+
+# output unix date in a mc-readable format
+sub timef
+{
+ my @time=localtime($_[0]);
+ return sprintf '%02d-%02d-%02d %02d:%02d', $time[4]+1, $time[3],
+ $time[5]+1900, $time[2], $time[1];
+}
+
+# parse given string as a date and return unix time
+sub datetime
+{
+ # in case of problems fall back to 0 in unix time
+ # note: str2time interprets some wrong values (eg. " ") as 'today'
+ if ($parsedates && defined (my $t=str2time($_[0]))) {
+ return timef($t);
+ }
+ return timef(time);
+}
+
+# print message on stderr and exit
+sub error
+{
+ print STDERR $_[0], "\n";
+ exit 1;
+}
+
+# (compressed) input
+sub myin
+{
+ my ($qfname)=(quotemeta $_[0]);
+
+ $_=`$fileutil $qfname`;
+ if (/^'*lz4/) {
+ return "$lz4 -dc $qfname";
+ } elsif (/^'*lzip/) {
+ return "$lzip -dc $qfname";
+ } elsif (/^'*lzma/) {
+ return "$lzma -dc $qfname";
+ } elsif (/^'*xz/) {
+ return "$xz -dc $qfname";
+ } elsif (/^'*zst/) {
+ return "$zstd -dc $qfname";
+ } elsif (/^'*bzip/) {
+ return "$bzip -dc $qfname";
+ } elsif (/^'*gzip/) {
+ return "$gzip -dc $qfname";
+ } else {
+ return "cat $qfname";
+ }
+}
+
+# (compressed) output
+sub myout
+{
+ my ($qfname,$append)=(quotemeta $_[0],$_[1]);
+ my ($sep) = $append ? '>>' : '>';
+
+ $_=`$fileutil $qfname`;
+ if (/^'*lz4/) {
+ return "$lz4 -c $sep $qfname";
+ } elsif (/^'*lzip/) {
+ return "$lzip -c $sep $qfname";
+ } elsif (/^'*lzma/) {
+ return "$lzma -c $sep $qfname";
+ } elsif (/^'*xz/) {
+ return "$xz -c $sep $qfname";
+ } elsif (/^'*zst/) {
+ return "$zstd -c $sep $qfname";
+ } elsif (/^'*bzip/) {
+ return "$bzip -c $sep $qfname";
+ } elsif (/^'*gzip/) {
+ return "$gzip -c $sep $qfname";
+ } else {
+ return "cat $sep $qfname";
+ }
+}
+
+# select diff filename conforming with rules found in diff.info
+sub diff_filename
+{
+ my ($fsrc,$fdst)= @_;
+ # TODO: can remove these two calls later
+ $fsrc = patchfs_canonicalize_path ($fsrc);
+ $fdst = patchfs_canonicalize_path ($fdst);
+ if (!$fdst && !$fsrc) {
+ error 'Index: not yet implemented';
+ } elsif (!$fsrc || $fsrc eq '/dev/null') {
+ return ($fdst,'PATCH-CREATE/');
+ } elsif (!$fdst || $fdst eq '/dev/null') {
+ return ($fsrc,'PATCH-REMOVE/');
+ } elsif (($fdst eq '/dev/null') && ($fsrc eq '/dev/null')) {
+ error 'Malformed diff, missing a sane filename';
+ } else {
+ # fewest path name components
+ if ($fdst=~s|/|/|g < $fsrc=~s|/|/|g) {
+ return ($fdst,'');
+ } elsif ($fdst=~s|/|/|g > $fsrc=~s|/|/|g) {
+ return ($fsrc,'');
+ } else {
+ # shorter base name
+ if (($fdst=~/$basename/o,length $2) < ($fsrc=~/$basename/o,length $2)) {
+ return ($fdst,'');
+ } elsif (($fdst=~/$basename/o,length $2) > ($fsrc=~/$basename/o,length $2)) {
+ return ($fsrc,'');
+ } else {
+ # shortest names
+ if (length $fdst < length $fsrc) {
+ return ($fdst,'');
+ } else {
+ return ($fsrc,'');
+ }
+ }
+ }
+ }
+}
+
+# IN: diff "archive" name
+# IN: file handle for output; STDIN for list, tempfile else
+# IN: filename to watch (for: copyout, rm), '' for: list
+# IN: remove the file?
+# true - ... and print out the rest
+# false - ie. copyout mode, print just the file
+sub parse($$$$)
+{
+ my $archive=shift;
+ my $fh=shift;
+ my $file=shift;
+ my $rmmod=shift;
+ my ($state,$fsize,$time);
+ my ($f,$fsrc,$fdst,$prefix);
+ my ($unified,$context);
+ my ($skipread, $filetoprint, $filefound);
+ my ($h_add,$h_del,$h_ctx); # hunk line counts
+ my ($h_r1,$h_r2); # hunk ranges
+ my @outsrc; # if desired ...
+ my @outdst;
+ my $line;
+ my %fmap_size=();
+ my %fmap_time=();
+
+ import Date::Parse if ($parsedates && $file eq '');
+
+ $line=1;
+ $state=0; $fsize=0; $f='';
+ $filefound=0;
+ while ($skipread || ($line++,$_=<I>)) {
+ $skipread=0;
+ if($state == 0) { # expecting comments
+ $unified=$context=0;
+ $unified=1 if (/^--- /);
+ $context=1 if (/^\*\*\* /);
+ if (!$unified && !$context) {
+ $filefound=0 if($file ne '' && $filetoprint);
+ # shortcut for rmmod xor filefound
+ # - in rmmod we print if not found
+ # - in copyout (!rmmod) we print if found
+ print $fh $_ if($rmmod != $filefound);
+ next;
+ }
+
+ if($file eq '' && $filetoprint) {
+ $fmap_size{"$prefix$f"}+=$fsize;
+ $fmap_time{"$prefix$f"}=$time;
+ }
+
+ # start of new file
+ $_ .=<I>; # steal next line, both formats
+ $line++;
+ if($unified) {
+ if(/$unified_header/o) {
+ ($fsrc,$fdst,$time) = /$unified_extract/o;
+ } elsif(/$unified_header2/o) {
+ ($fsrc,$fdst,$time) = /$unified_extract2/o;
+ } else {
+ error "Can't parse unified diff header";
+ }
+ } elsif($context) {
+ if(/$context_header/o) {
+ ($fsrc,$fdst,$time) = /$context_extract/o;
+ } elsif(/$context_header2/o) {
+ ($fsrc,$fdst,$time) = /$context_extract2/o;
+ } else {
+ error "Can't parse context diff header";
+ }
+ } else {
+ error "Unrecognized diff header";
+ }
+ $fsrc=patchfs_canonicalize_path($fsrc);
+ $fdst=patchfs_canonicalize_path($fdst);
+ if(wantarray) {
+ push @outsrc,$fsrc;
+ push @outdst,$fdst;
+ }
+ ($f,$prefix)=diff_filename($fsrc,$fdst);
+ $filefound=($f eq $file);
+
+ $f="$f.diff";
+ $filetoprint=1;
+ $fsize=length;
+ print $fh $_ if($rmmod != $filefound);
+
+ $state=1;
+ } elsif($state == 1) { # expecting diff hunk headers, end of file or comments
+ if($unified) {
+ my ($a,$b,$c,$d);
+ ($a,$b,$h_r1,$c,$d,$h_r2)=/$unified_hunk/o;
+ if(!defined($a) || !defined($c)) {
+ # hunk header does not come, a comment inside
+ # or maybe a new file, state 0 will decide
+ $skipread=1;
+ $state=0;
+ next;
+ }
+ $fsize+=length;
+ print $fh $_ if($rmmod != $filefound);
+ $h_r1=1 if(!defined($b));
+ $h_r2=1 if(!defined($d));
+ $h_add=$h_del=$h_ctx=0;
+ $state=2;
+ } elsif($context) {
+ if(!/$context_contents/o) {
+ $skipread=1;
+ $state=0;
+ next;
+ }
+ print $fh $_ if($rmmod != $filefound);
+ $fsize+=length;
+ }
+ } elsif($state == 2) { # expecting hunk contents
+ if($h_del + $h_ctx == $h_r1 && $h_add + $h_ctx == $h_r2) {
+ # hooray, end of hunk
+ # we optimistically ended with a hunk before but
+ # the line has been read already
+ $skipread=1;
+ $state=1;
+ next;
+ }
+ print $fh $_ if($rmmod != $filefound);
+ $fsize+=length;
+ my ($first)= /^(.)/;
+ if(ord($first) == ord('+')) { $h_add++; }
+ elsif(ord($first) == ord('-')) { $h_del++; }
+ elsif(ord($first) == ord(' ')) { $h_ctx++; }
+ elsif(ord($first) == ord('\\')) { 0; }
+ elsif(ord($first) == ord('@')) { error "Malformed hunk, header came too early"; }
+ else { error "$archive:$line: Unrecognized character '$first' in hunk"; }
+ }
+ }
+ if($file eq '' && $filetoprint) {
+ $fmap_size{"$prefix$f"}+=$fsize;
+ $fmap_time{"$prefix$f"}=$time;
+ }
+
+ # use uid and gid from file
+ my $qarchive = quotemeta $archive;
+ my ($uid,$gid)=(`ls -l $qarchive`=~/$ls_extract_id/o);
+
+ # flush all file names with cumulative file size
+ while(my ($fn, $fs) = each %fmap_size) {
+ printf $fh "-rw-r--r-- 1 %s %s %d %s %s\n", $uid, $gid, $fs, datetime($fmap_time{$fn}), $fn;
+ }
+
+ close($fh) if($file ne '');
+ return \(@outsrc, @outdst) if wantarray;
+}
+
+# list files affected by patch
+sub list($) {
+ parse($_[0], *STDOUT, '', 0);
+ close(I);
+}
+
+# extract diff from patch
+# IN: diff file to find
+# IN: output file name
+sub copyout($$) {
+ my ($file,$out)=@_;
+
+ $file=~s/^(PATCH-(CREATE|REMOVE)\/)?(.*)\.diff$/$3/;
+ $file = patchfs_canonicalize_path ($file);
+
+ open(FH, ">$out") or error("Cannot open output file");
+ parse('', *FH, $file, 0);
+}
+
+# remove diff(s) from patch
+# IN: archive
+# IN: file to delete
+sub rm($$) {
+ my $archive=shift;
+ my ($tmp,$tmpname)=tempfile();
+
+ @_=map {scalar(s/^(PATCH-(CREATE|REMOVE)\/)?(.*)\.diff$/$3/,$_)} @_;
+
+ # just the first file for now
+ parse($archive, $tmp, $_[0], 1);
+ close I;
+
+ # replace archive
+ system("cat \Q$tmpname\E | " . myout($archive,0))==0
+ or error "Can't write to archive";
+ system("rm -f -- \Q$tmpname\E");
+}
+
+# append diff to archive
+# IN: diff archive name
+# IN: newly created file name in archive
+# IN: the real source file
+sub copyin($$$) {
+ # TODO: seems to be tricky. what to do?
+ # copyin of file which is already there may:
+ # * delete the original and copy only the new
+ # * just append the new hunks to the same file
+ # problems: may not be a valid diff, unmerged hunks
+ # * try to merge the two together
+ # ... but we do not want write patchutils again, right?
+ error "Copying files into diff not supported";
+ return;
+
+ my ($archive,$name,$src)=@_;
+
+ # in case we are appending another diff, we have
+ # to delete/merge all the files
+ open(DEVNULL, ">/dev/null");
+ open I, myin($src).'|';
+ my ($srclist,$dstlist)=parse($archive, *DEVNULL, '', 0);
+ close(I);
+ close(DEVNULL);
+ foreach(@$srclist) {
+ print("SRC: del $_\n");
+ }
+ foreach(@$dstlist) {
+ print("DST: del $_\n");
+ }
+ return;
+
+ # remove overwritten file
+ open I, myin($archive).'|';
+ rm ($archive, $name);
+ close I;
+
+ my $cmd1=myin("$src.diff");
+ my $cmd2=myout($archive,1);
+ system("$cmd1 | $cmd2")==0
+ or error "Can't write to archive";
+}
+
+my $fin = $ARGV[1];
+
+# resolve symlink
+while (-l $fin) {
+ $fin = readlink $fin;
+}
+
+if ($ARGV[0] eq 'list') {
+ open I, myin($fin).'|';
+ list ($fin);
+ exit 0;
+} elsif ($ARGV[0] eq 'copyout') {
+ open I, myin($fin)."|";
+ copyout ($ARGV[2], $ARGV[3]);
+ exit 0;
+} elsif ($ARGV[0] eq 'rm') {
+ open I, myin($fin)."|";
+ rm ($fin, $ARGV[2]);
+ exit 0;
+} elsif ($ARGV[0] eq 'rmdir') {
+ exit 0;
+} elsif ($ARGV[0] eq 'mkdir') {
+ exit 0;
+} elsif ($ARGV[0] eq 'copyin') {
+ copyin ($fin, $ARGV[2], $ARGV[3]);
+ exit 0;
+}
+exit 1;
diff --git a/src/vfs/extfs/helpers/patchsetfs b/src/vfs/extfs/helpers/patchsetfs
new file mode 100755
index 0000000..9bbe9f9
--- /dev/null
+++ b/src/vfs/extfs/helpers/patchsetfs
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+LANG=C
+export LANG
+LC_TIME=C
+export LC_TIME
+
+# --- GIT -----------------------------------------------------------------------
+
+found_git_dir()
+{
+ work_dir=$1
+ while [ -n "$work_dir" -a "$work_dir" != "/" ]; do
+ [ -d "${work_dir}/.git" ] && {
+ echo "${work_dir}/.git/"
+ return
+ }
+ work_dir=`dirname "$work_dir"`
+ done
+ echo ''
+}
+
+patchsetfs_list_git()
+{
+ WORK_DIR=$1; shift
+ fname=$1; shift
+ USER=$1; shift
+ DATE=$1; shift
+
+ GIT_DIR=`found_git_dir "$WORK_DIR"`
+ [ -z "$GIT_DIR" ] && GIT_DIR=$WORK_DIR
+ curr_year=`date +"%Y"`
+
+ git --git-dir="$GIT_DIR" log --abbrev=7 --pretty="format:%at %h %an" -- "$fname" | while read TIMESTAMP chset author
+ do
+ year=`date -d @"$TIMESTAMP" +"%Y"`
+ [ "$year" = "$curr_year" ] && {
+ DATE=`date -d @"$TIMESTAMP" +"%b %d %H:%M"`
+ } || {
+ DATE=`date -d @"$TIMESTAMP" +"%b %d %Y"`
+ }
+ NAME="$chset $author"
+ echo "-rw-rw-rw- 1 $USER 0 0 $DATE $NAME.diff"
+ done
+}
+
+patchsetfs_copyout_git()
+{
+ WORK_DIR=$1; shift
+ fname=$1; shift
+ orig_fname=$1;shift
+ output_fname=$1;shift
+
+ chset=`echo "$orig_fname"| cut -f 1 -d " "`
+
+ GIT_DIR=`found_git_dir "$WORK_DIR"`
+ [ -z "$GIT_DIR" ] && GIT_DIR=$WORK_DIR
+
+ git --git-dir="$GIT_DIR" show "$chset" -- "$fname" > "$output_fname"
+}
+
+# --- COMMON --------------------------------------------------------------------
+
+patchsetfs_list()
+{
+ VCS_type=$1; shift
+ WORK_DIR=$1; shift
+ fname=$1; shift
+
+ DATE=`date +"%b %d %H:%M"`
+ USER=`whoami`
+
+ case "$VCS_type" in
+ git) patchsetfs_list_git "$WORK_DIR" "$fname" "$USER" "$DATE" ;;
+ esac
+}
+
+patchsetfs_copyout()
+{
+ VCS_type=$1; shift
+ WORK_DIR=$1; shift
+ fname=$1; shift
+
+ case "$VCS_type" in
+ git) patchsetfs_copyout_git "$WORK_DIR" "$fname" "$@" ;;
+ esac
+
+}
+
+# --- MAIN ----------------------------------------------------------------------
+
+command=$1; shift
+tmp_file=$1; shift
+
+WORK_DIR=`head -n1 $tmp_file`
+fname=`tail -n2 $tmp_file | head -n1`
+VCS_type=`tail -n1 $tmp_file`
+
+case "$command" in
+ list) patchsetfs_list "$VCS_type" "$WORK_DIR" "$fname" ;;
+ copyout) patchsetfs_copyout "$VCS_type" "$WORK_DIR" "$fname" "$@" ;;
+ *) exit 1 ;;
+esac
+exit 0
diff --git a/src/vfs/extfs/helpers/rpm b/src/vfs/extfs/helpers/rpm
new file mode 100755
index 0000000..8fa9188
--- /dev/null
+++ b/src/vfs/extfs/helpers/rpm
@@ -0,0 +1,349 @@
+#! /bin/sh
+# VFS-wrapper for RPM (and src.rpm) files
+#
+# Copyright (C) 1996-2004,2009
+# Free Software Foundation, Inc.
+#
+# Written by
+# Erik Troan <ewt@redhat.com> 1996
+# Jakub Jelinek <jj@sunsite.mff.cuni.cz> 1996, 2004
+# Tomasz KÅ‚oczko <kloczek@rudy.mif.pg.gda.pl> 1997
+# Wojtek Pilorz <wpilorz@bdk.lublin.pl>
+# 1997: minor changes
+# Michele Marziani <marziani@fe.infn.it>
+# 1997: minor changes
+# Marc Merlin <marcsoft@merlins.org> 1998
+# 1998: bug files
+# Michal Svec <rebel@penguin.cz> 2000
+# 2000: locale bugfix
+# Andrew V. Samoilov <sav@bcs.zp.ua>
+# 2004: Whitespace(s) & single quote(s) in filename workaround
+# https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=64007
+# Slava Zanko <slavazanko@gmail.com>
+# 2009: Totally rewritten.
+# Alexander Chumachenko <ledest@gmail.com>
+# 2013: add dependency version output
+# Denis Silakov <denis.silakov@rosalab.ru>
+# 2013: tar payload support.
+# Arkadiusz Miśkiewicz <arekm@maven.pl>
+# 2013: improve support for EPOCH
+# add support for PREINPROG/POSTINPROG/PREUNPROG/POSTUNPROG
+# add support for VERIFYSCRIPTPROG
+# add support for TRIGGERSCRIPTS/TRIGGERSCRIPTPROG
+# Jiri Tyr <jiri.tyr@gmail.com>
+# 2016: add support for PRETRANS/PRETRANSPROG/POSTTRANS/POSTTRANSPROG
+#
+# This file is part of the Midnight Commander.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# override any locale for dates
+unset LC_ALL
+LC_TIME=C
+export LC_TIME
+
+if rpmbuild --version >/dev/null 2>&1; then
+ RPMBUILD="rpmbuild"
+else
+ RPMBUILD="rpm"
+fi
+
+if rpm --nosignature --version >/dev/null 2>&1; then
+ RPM="rpm --nosignature"
+ RPMBUILD="$RPMBUILD --nosignature"
+else
+ RPM="rpm"
+fi
+RPM_QUERY_FMT="$RPM -qp --qf"
+RPM2CPIO="rpm2cpio"
+
+SED="sed"
+
+param=$1; shift
+rpm_filename=$1; shift
+
+FILEPREF="-r--r--r-- 1 root root "
+
+mcrpmfs_getDesription()
+{
+ $RPM -qip "${rpm_filename}"
+}
+
+mcrpmfs_getAllNeededTags()
+{
+ $RPM_QUERY_FMT \
+"|NAME=%{NAME}"\
+"|VERSION=%{VERSION}"\
+"|RELEASE=%{RELEASE}"\
+"|DISTRIBUTION=%{DISTRIBUTION}"\
+"|VENDOR=%{VENDOR}"\
+"|DESCRIPTION=%{DESCRIPTION}"\
+"|SUMMARY=%{SUMMARY}"\
+"|URL=%{URL}"\
+"|EPOCH=%{EPOCH}"\
+"|LICENSE=%{LICENSE}"\
+"|REQUIRES=%{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}"\
+"|OBSOLETES=%{OBSOLETES}"\
+"|PROVIDES=%{PROVIDES} %{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}"\
+"|CONFLICTS=%{CONFLICTS}"\
+"|PACKAGER=%{PACKAGER}" \
+ "${rpm_filename}" \
+ | tr '\n' ' ' # The newlines in DESCRIPTION mess with the sed script in mcrpmfs_getOneTag().
+}
+
+mcrpmfs_getRawOneTag()
+{
+ $RPM_QUERY_FMT "$1" "${rpm_filename}"
+}
+
+mcrpmfs_getOneTag()
+{
+ # 'echo' can't be used for arbitrary data (see commit message).
+ printf "%s" "$AllTAGS" | $SED "s/.*|${1}=//" | cut -d '|' -f 1
+}
+
+mcrpmfs_printOneMetaInfo()
+{
+ if test "$3" = "raw"; then
+ metaInfo=`mcrpmfs_getRawOneTag "%{$2}"`
+ else
+ metaInfo=`mcrpmfs_getOneTag "$2"`
+ fi
+
+ if test -n "${metaInfo}" -a "${metaInfo}" != "(none)"; then
+ echo "${FILEPREF} 0 ${DATE} ${1}"
+ return 0
+ fi
+ return 1
+}
+
+mcrpmfs_list_fastRPM ()
+{
+ echo "$FILEPREF 0 $DATE INFO/DISTRIBUTION"
+ echo "$FILEPREF 0 $DATE INFO/VENDOR"
+ echo "$FILEPREF 0 $DATE INFO/DESCRIPTION"
+ echo "$FILEPREF 0 $DATE INFO/SUMMARY"
+ echo "dr-xr-xr-x 1 root root 0 $DATE INFO/SCRIPTS"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PRETRANS"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTTRANS"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREIN"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTIN"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREUN"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTUN"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/VERIFYSCRIPT"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/TRIGGERSCRIPTS"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/ALL"
+ echo "$FILEPREF 0 $DATE INFO/PACKAGER"
+ echo "$FILEPREF 0 $DATE INFO/URL"
+ echo "$FILEPREF 0 $DATE INFO/EPOCH"
+ echo "$FILEPREF 0 $DATE INFO/LICENSE"
+ echo "$FILEPREF 0 $DATE INFO/REQUIRES"
+ echo "$FILEPREF 0 $DATE INFO/OBSOLETES"
+ echo "$FILEPREF 0 $DATE INFO/PROVIDES"
+ echo "$FILEPREF 0 $DATE INFO/ENHANCES"
+ echo "$FILEPREF 0 $DATE INFO/SUGGESTS"
+ echo "$FILEPREF 0 $DATE INFO/RECOMMENDS"
+ echo "$FILEPREF 0 $DATE INFO/SUPPLEMENTS"
+ echo "$FILEPREF 0 $DATE INFO/CONFLICTS"
+ echo "$FILEPREF 0 $DATE INFO/CHANGELOG"
+}
+
+mcrpmfs_list_fullRPM ()
+{
+ mcrpmfs_printOneMetaInfo "INFO/DISTRIBUTION" "DISTRIBUTION"
+ mcrpmfs_printOneMetaInfo "INFO/VENDOR" "VENDOR"
+ mcrpmfs_printOneMetaInfo "INFO/DESCRIPTION" "DESCRIPTION"
+ mcrpmfs_printOneMetaInfo "INFO/SUMMARY" "SUMMARY"
+
+ if test "`mcrpmfs_getRawOneTag \"%{RPMTAG_PRETRANS}%{RPMTAG_POSTTRANS}%{RPMTAG_PREIN}%{RPMTAG_POSTIN}%{RPMTAG_PREUN}%{RPMTAG_POSTUN}%{VERIFYSCRIPT}%{TRIGGERSCRIPTS}\"`" != "(none)(none)(none)(none)(none)(none)(none)(none)"; then
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PRETRANS" "RPMTAG_PRETRANS" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTTRANS" "RPMTAG_POSTTRANS" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PREIN" "RPMTAG_PREIN" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTIN" "RPMTAG_POSTIN" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PREUN" "RPMTAG_PREUN" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTUN" "RPMTAG_POSTUN" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/VERIFYSCRIPT" "VERIFYSCRIPT" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/TRIGGERSCRIPTS" "TRIGGERSCRIPTS" "raw"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/ALL"
+ fi
+
+ if test "`mcrpmfs_getRawOneTag \"%{RPMTAG_PRETRANSPROG}%{RPMTAG_POSTTRANSPROG}%{RPMTAG_PREINPROG}%{RPMTAG_POSTINPROG}%{RPMTAG_PREUNPROG}%{RPMTAG_POSTUNPROG}%{VERIFYSCRIPTPROG}%{TRIGGERSCRIPTPROG}\"`" != "(none)(none)(none)(none)(none)(none)(none)(none)"; then
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PRETRANSPROG" "RPMTAG_PRETRANSPROG" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTTRANSPROG" "RPMTAG_POSTTRANSPROG" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PREINPROG" "RPMTAG_PREINPROG" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTINPROG" "RPMTAG_POSTINPROG" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/PREUNPROG" "RPMTAG_PREUNPROG" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/POSTUNPROG" "RPMTAG_POSTUNPROG" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/VERIFYSCRIPTPROG" "VERIFYSCRIPTPROG" "raw"
+ mcrpmfs_printOneMetaInfo "INFO/SCRIPTS/TRIGGERSCRIPTPROG" "TRIGGERSCRIPTPROG" "raw"
+ fi
+
+ mcrpmfs_printOneMetaInfo "INFO/PACKAGER" "PACKAGER"
+ mcrpmfs_printOneMetaInfo "INFO/URL" "URL"
+ mcrpmfs_printOneMetaInfo "INFO/EPOCH" "EPOCH"
+ mcrpmfs_printOneMetaInfo "INFO/LICENSE" "LICENSE"
+
+ mcrpmfs_printOneMetaInfo "INFO/REQUIRES" "REQUIRES"
+ mcrpmfs_printOneMetaInfo "INFO/OBSOLETES" "OBSOLETES"
+ mcrpmfs_printOneMetaInfo "INFO/PROVIDES" "PROVIDES"
+ mcrpmfs_printOneMetaInfo "INFO/CONFLICTS" "CONFLICTS"
+ mcrpmfs_printOneMetaInfo "INFO/CHANGELOG" "CHANGELOGTEXT" "raw"
+}
+
+mcrpmfs_list ()
+{
+ # set MCFASTRPM_DFLT to 1 for faster rpm files handling by default, to 0 for
+ # slower handling
+ MCFASTRPM_DFLT=0
+ if test -z "$MCFASTRPM"; then
+ MCFASTRPM=$MCFASTRPM_DFLT
+ fi
+
+ DESC=`mcrpmfs_getDesription 2>/dev/null` || {
+ echo "$FILEPREF 0 "`date +"%b %d %H:%M"`" ERROR"
+ exit 1
+ }
+ DATE=`mcrpmfs_getRawOneTag "%{BUILDTIME:date}\n" | cut -c 5-11,21-24`
+ PAYLOAD=`mcrpmfs_getRawOneTag "%{PAYLOADFORMAT}\n" | sed s/ustar/tar/`
+
+ HEADERSIZE=`printf '%s\n' "$DESC" | wc -c` # 'echo' can't be used for arbitrary data (see commit message).
+ printf '%s %s %s HEADER\n' "${FILEPREF}" "${HEADERSIZE}" "${DATE}"
+ echo "-r-xr-xr-x 1 root root 0 $DATE INSTALL"
+ case "${rpm_filename}" in
+ *.src.rpm)
+ echo "-r-xr-xr-x 1 root root 0 $DATE REBUILD"
+ ;;
+ *)
+ echo "-r-xr-xr-x 1 root root 0 $DATE UPGRADE"
+ ;;
+ esac
+
+ echo "dr-xr-xr-x 3 root root 0 $DATE INFO"
+ if [ `mcrpmfs_getRawOneTag "%{EPOCH}"` = "(none)" ]; then
+ echo "$FILEPREF 0 $DATE INFO/NAME-VERSION-RELEASE"
+ else
+ echo "$FILEPREF 0 $DATE INFO/NAME-EPOCH:VERSION-RELEASE"
+ fi
+ echo "$FILEPREF 0 $DATE INFO/GROUP"
+ echo "$FILEPREF 0 $DATE INFO/BUILDHOST"
+ echo "$FILEPREF 0 $DATE INFO/SOURCERPM"
+ echo "$FILEPREF 0 $DATE INFO/BUILDTIME"
+ echo "$FILEPREF 0 $DATE INFO/RPMVERSION"
+ echo "$FILEPREF 0 $DATE INFO/OS"
+ echo "$FILEPREF 0 $DATE INFO/SIZE"
+
+ if test "$MCFASTRPM" = 0 ; then
+ mcrpmfs_list_fullRPM
+ else
+ mcrpmfs_list_fastRPM
+ fi
+
+ echo "$FILEPREF 0 $DATE CONTENTS.$PAYLOAD"
+}
+
+mcrpmfs_copyout ()
+{
+ case "$1" in
+ HEADER) mcrpmfs_getDesription > "$2"; exit 0;;
+ INSTALL)
+ echo "# Run this to install this RPM package" > "$2"
+ exit 0
+ ;;
+ UPGRADE)
+ echo "# Run this to upgrade this RPM package" > "$2"
+ exit 0
+ ;;
+ REBUILD)
+ echo "# Run this to rebuild this RPM package" > "$2"
+ exit 0
+ ;;
+ ERROR) mcrpmfs_getDesription > /dev/null 2> "$2"; exit 0;;
+ INFO/NAME-VERSION-RELEASE)
+ echo `mcrpmfs_getOneTag "NAME"`-`mcrpmfs_getOneTag "VERSION"`-`mcrpmfs_getOneTag "RELEASE"` > "$2"
+ exit 0
+ ;;
+ INFO/NAME-EPOCH:VERSION-RELEASE)
+ echo `mcrpmfs_getOneTag "NAME"`-`mcrpmfs_getOneTag "EPOCH"`:`mcrpmfs_getOneTag "VERSION"`-`mcrpmfs_getOneTag "RELEASE"` > "$2"
+ exit 0
+ ;;
+ INFO/RELEASE) mcrpmfs_getOneTag "RELEASE" > "$2"; exit 0;;
+ INFO/GROUP) mcrpmfs_getRawOneTag "%{GROUP}\n" > "$2"; exit 0;;
+ INFO/DISTRIBUTION) mcrpmfs_getOneTag "DISTRIBUTION" > "$2"; exit 0;;
+ INFO/VENDOR) mcrpmfs_getOneTag "VENDOR" > "$2"; exit 0;;
+ INFO/BUILDHOST) mcrpmfs_getRawOneTag "%{BUILDHOST}\n" > "$2"; exit 0;;
+ INFO/SOURCERPM) mcrpmfs_getRawOneTag "%{SOURCERPM}\n" > "$2"; exit 0;;
+ INFO/DESCRIPTION) mcrpmfs_getRawOneTag "%{DESCRIPTION}\n" > "$2"; exit 0;;
+ INFO/PACKAGER) mcrpmfs_getOneTag "PACKAGER" > "$2"; exit 0;;
+ INFO/URL) mcrpmfs_getOneTag "URL" >"$2"; exit 0;;
+ INFO/BUILDTIME) mcrpmfs_getRawOneTag "%{BUILDTIME:date}\n" >"$2"; exit 0;;
+ INFO/EPOCH) mcrpmfs_getOneTag "EPOCH" >"$2"; exit 0;;
+ INFO/LICENSE) mcrpmfs_getOneTag "LICENSE" >"$2"; exit 0;;
+ INFO/RPMVERSION) mcrpmfs_getRawOneTag "%{RPMVERSION}\n" >"$2"; exit 0;;
+ INFO/REQUIRES) mcrpmfs_getRawOneTag "[%{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}\n]" >"$2"; exit 0;;
+ INFO/ENHANCES) mcrpmfs_getRawOneTag "[%|ENHANCESFLAGS:depflag_strong?{}:{%{ENHANCESNAME} %{ENHANCESFLAGS:depflags} %{ENHANCESVERSION}\n}|]" "$f" >"$3"; exit 0;;
+ INFO/SUGGESTS) mcrpmfs_getRawOneTag "[%|SUGGESTSFLAGS:depflag_strong?{}:{%{SUGGESTSNAME} %{SUGGESTSFLAGS:depflags} %{SUGGESTSVERSION}\n}|]" "$f" >"$3"; exit 0;;
+ INFO/RECOMMENDS) mcrpmfs_getRawOneTag "[%|SUGGESTSFLAGS:depflag_strong?{%{SUGGESTSNAME} %{SUGGESTSFLAGS:depflags} %{SUGGESTSVERSION}\n}|]" "$f" >"$3"; exit 0;;
+ INFO/SUPPLEMENTS) mcrpmfs_getRawOneTag "[%|ENHANCESFLAGS:depflag_strong?{%{ENHANCESNAME} %{ENHANCESFLAGS:depflags} %{ENHANCESVERSION}\n}|]" "$f" >"$3"; exit 0;;
+ INFO/PROVIDES) mcrpmfs_getRawOneTag "[%{PROVIDES} %{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}\n]" >"$2"; exit 0;;
+ INFO/SCRIPTS/PRETRANS) mcrpmfs_getRawOneTag "%{RPMTAG_PRETRANS}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/PRETRANSPROG) mcrpmfs_getRawOneTag "%{RPMTAG_PRETRANSPROG}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/POSTTRANS) mcrpmfs_getRawOneTag "%{RPMTAG_POSTTRANS}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/POSTTRANSPROG) mcrpmfs_getRawOneTag "%{RPMTAG_POSTTRANSPROG}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/PREIN) mcrpmfs_getRawOneTag "%{RPMTAG_PREIN}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/PREINPROG) mcrpmfs_getRawOneTag "%{RPMTAG_PREINPROG}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/POSTIN) mcrpmfs_getRawOneTag "%{RPMTAG_POSTIN}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/POSTINPROG) mcrpmfs_getRawOneTag "%{RPMTAG_POSTINPROG}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/PREUN) mcrpmfs_getRawOneTag "%{RPMTAG_PREUN}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/PREUNPROG) mcrpmfs_getRawOneTag "%{RPMTAG_PREUNPROG}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/POSTUN) mcrpmfs_getRawOneTag "%{RPMTAG_POSTUN}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/POSTUNPROG) mcrpmfs_getRawOneTag "%{RPMTAG_POSTUNPROG}\n" >"$2"; exit 0;;
+ INFO/SCRIPTS/VERIFYSCRIPT) mcrpmfs_getRawOneTag "%{VERIFYSCRIPT}\n" > "$2"; exit 0;;
+ INFO/SCRIPTS/VERIFYSCRIPTPROG) mcrpmfs_getRawOneTag "%{VERIFYSCRIPTPROG}\n" > "$2"; exit 0;;
+ INFO/SCRIPTS/TRIGGERSCRIPTS) $RPM -qp --triggers "${rpm_filename}" > "$2"; exit 0;;
+ INFO/SCRIPTS/TRIGGERSCRIPTPROG) mcrpmfs_getRawOneTag "%{TRIGGERSCRIPTPROG}\n" > "$2"; exit 0;;
+ INFO/SCRIPTS/ALL) $RPM -qp --scripts "${rpm_filename}" > "$2"; exit 0;;
+ INFO/SUMMARY) mcrpmfs_getRawOneTag "%{SUMMARY}\n" > "$2"; exit 0;;
+ INFO/OS) mcrpmfs_getRawOneTag "%{OS}\n" > "$2"; exit 0;;
+ INFO/CHANGELOG) mcrpmfs_getRawOneTag "[* %{CHANGELOGTIME:date} %{CHANGELOGNAME}\n%{CHANGELOGTEXT}\n\n]\n" > "$2"; exit 0;;
+ INFO/SIZE) mcrpmfs_getRawOneTag "%{SIZE} bytes\n" > "$2"; exit 0;;
+ INFO/OBSOLETES) mcrpmfs_getRawOneTag "[%{OBSOLETENAME} %|OBSOLETEFLAGS?{%{OBSOLETEFLAGS:depflags} %{OBSOLETEVERSION}}:{}|\n]" > "$2"; exit 0;;
+ INFO/CONFLICTS) mcrpmfs_getRawOneTag "[%{CONFLICTNAME} %{CONFLICTFLAGS:depflags} %{CONFLICTVERSION}\n]" >"$2"; exit 0;;
+ CONTENTS.*) $RPM2CPIO "${rpm_filename}" > "$2"; exit 0;;
+ *)
+ ;;
+ esac
+}
+
+mcrpmfs_run ()
+{
+ case "$1" in
+ INSTALL) echo "Installing \"${rpm_filename}\""; $RPM -ivh "${rpm_filename}"; exit 0;;
+ UPGRADE) echo "Upgrading \"${rpm_filename}\""; $RPM -Uvh "${rpm_filename}"; exit 0;;
+ REBUILD) echo "Rebuilding \"${rpm_filename}\""; $RPMBUILD --rebuild "${rpm_filename}"; exit 0;;
+ esac
+}
+
+# Let the test framework override functions and variables.
+[ -n "$MC_TEST_RPM_REWRITE" ] && . "$MC_TEST_RPM_REWRITE"
+
+AllTAGS=`mcrpmfs_getAllNeededTags "$1"`
+
+umask 077
+case "${param}" in
+ list) mcrpmfs_list; exit 0;;
+ copyout) mcrpmfs_copyout "$1" "$2"; exit 0;;
+ run) mcrpmfs_run "$1"; exit 1;;
+esac
+exit 1
diff --git a/src/vfs/extfs/helpers/rpms+.in b/src/vfs/extfs/helpers/rpms+.in
new file mode 100644
index 0000000..9a8e7de
--- /dev/null
+++ b/src/vfs/extfs/helpers/rpms+.in
@@ -0,0 +1,66 @@
+#! @PERL@
+#
+# Written by Balazs Nagy (julian7@kva.hu) 1998
+# locale bugfix by Michal Svec (rebel@penguin.cz) 2000
+# (C) 1998 The Free Software Foundation.
+#
+#
+
+# override any locale for dates
+delete $ENV{"LC_ALL"};
+$ENV{"LC_TIME"}="C";
+
+#print $ENV{"LC_ALL"};
+#exit 0;
+
+sub gd
+{
+ my ($dt) = @_;
+ $dt =~ tr/ //s;
+ $dt =~ s/^\w+ (\w+) (\d+) (\d+:\d+):\d+ .+\n?$/$1 $2 $3/;
+ return $dt;
+}
+
+$DATE=gd(`date`);
+
+sub list
+{
+ my (@rpms, %files, $i, $fn, $dn, $sz, $bt);
+# @rpms = `rpm -qa --qf "\%{NAME}-\%{VERSION}-\%{RELEASE}:\%{GROUP}:\%{SIZE}:\%{BUILDTIME:date}\n"`;
+ @rpms = `rpm -qa --qf "\%{NAME}-\%{VERSION}:\%{GROUP}:\%{SIZE}:\%{BUILDTIME:date}\n"`;
+ print @trpms;
+ %files = ();
+ %sizes = ();
+ %dates = ();
+ for $i (@rpms) {
+ if ($i =~ /^([^:]+):([^:]+):([^:]+):(.+)$/) {
+ ($fn, $dn, $sz, $bt) = ($1, $2, $3, $4);
+ $dn =~ s/ /_/g;
+ if (defined $files{$dn}) {
+ push(@{$files{$dn}}, $fn);
+ } else {
+ @{$files{$dn}} = ($fn);
+ }
+ $sizes{$fn} = $sz;
+ $dates{$fn} = gd($bt);
+ }
+ }
+ for $i (sort keys %files) {
+ print "dr-xr-xr-x 1 root root 0 $DATE $i/\n";
+ for $fn (sort @{$files{$i}}) {
+ print "-r--r--r-- 1 root root $sizes{$fn} $dates{$fn} $i/$fn.trpm\n";
+ }
+ }
+}
+
+#open O, ">>/tmp/tt";
+#print O "RPMS: ";
+#for $i (@ARGV) {
+# print O "$i ";
+#}
+#print O "\n";
+#close O;
+
+if ($ARGV[0] eq "list") { list(); exit(0); }
+elsif ($ARGV[0] eq "copyout") { open O, ">$ARGV[3]"; print O $ARGV[2], "\n"; close O; exit(0); }
+exit(1);
diff --git a/src/vfs/extfs/helpers/s3+.in b/src/vfs/extfs/helpers/s3+.in
new file mode 100644
index 0000000..f5e4b90
--- /dev/null
+++ b/src/vfs/extfs/helpers/s3+.in
@@ -0,0 +1,490 @@
+#! @PYTHON@
+# -*- coding: utf-8 -*-
+
+#
+# Midnight Commander compatible EXTFS for accessing Amazon Web Services S3.
+# Written by Jakob Kemi <jakob.kemi@gmail.com> 2009
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+#
+# Notes:
+# This EXTFS exposes buckets as directories and keys as files
+# Due to EXTFS limitations all buckets & keys have to be read initially which might
+# take quite some time.
+# Tested on Debian with Python 2.4-2.6 and boto 1.4c and 1.6b
+# (Python 2.6 might need -W ignore::DeprecationWarning due to boto using
+# deprecated module Popen2)
+#
+#
+# Installation:
+# Make sure that boto <http://code.google.com/p/boto> (python-boto in Debian) is installed.
+# Preferably pytz (package python-tz in Debian) should be installed as well.
+#
+# Save as executable file /usr/libexec/mc/extfs/s3 (or wherever your mc expects to find extfs modules)
+#
+# Settings: (should be set via environment)
+# Required:
+# AWS_ACCESS_KEY_ID : Amazon AWS access key (required)
+# AWS_SECRET_ACCESS_KEY : Amazon AWS secret access key (required)
+# Optional:
+# MCVFS_EXTFS_S3_LOCATION : where to create new buckets: "EU" - default, "USWest", "APNortheast" etc.
+# MCVFS_EXTFS_S3_DEBUGFILE : write debug info to this file (no info by default)
+# MCVFS_EXTFS_S3_DEBUGLEVEL : debug messages level ("WARNING" - default, "DEBUG" - verbose)
+#
+#
+# Usage:
+# Open dialog "Quick cd" (<alt-c>) and type: s3:// <enter> (or simply type `cd s3://' in shell line)
+#
+#
+# History:
+#
+# 2015-07-22 Dmitry Koterov <dmitry.koterov@gmail.com>
+# - Support for non-ASCII characters in filenames (system encoding detection).
+#
+# 2015-05-21 Dmitry Koterov <dmitry.koterov@gmail.com>
+# - Resolve "Please use AWS4-HMAC-SHA256" error: enforce the new V4 authentication method.
+# It is required in many (if not all) locations nowadays.
+# - Now s3+ works with buckets in different regions: locations are auto-detected.
+# - Debug level specification support (MCVFS_EXTFS_S3_DEBUGLEVEL).
+#
+# 2009-02-07 Jakob Kemi <jakob.kemi@gmail.com>
+# - Updated instructions.
+# - Improved error reporting.
+#
+# 2009-02-06 Jakob Kemi <jakob.kemi@gmail.com>
+# - Threaded list command.
+# - Handle rm of empty "subdirectories" (as seen in mc).
+# - List most recent datetime and total size of keys as directory properties.
+# - List modification time in local time.
+#
+# 2009-02-05 Jakob Kemi <jakob.kemi@gmail.com>
+# - Initial version.
+#
+
+import sys
+import os
+import time
+import re
+import datetime
+
+
+import boto
+from boto.s3.connection import S3Connection
+from boto.exception import BotoServerError
+
+
+# Get settings from environment
+USER=os.getenv('USER','0')
+AWS_ACCESS_KEY_ID=os.getenv('AWS_ACCESS_KEY_ID')
+AWS_SECRET_ACCESS_KEY=os.getenv('AWS_SECRET_ACCESS_KEY')
+S3LOCATION=os.getenv('MCVFS_EXTFS_S3_LOCATION', 'EU')
+DEBUGFILE=os.getenv('MCVFS_EXTFS_S3_DEBUGFILE')
+DEBUGLEVEL=os.getenv('MCVFS_EXTFS_S3_DEBUGLEVEL', 'WARNING')
+
+if not AWS_ACCESS_KEY_ID or not AWS_SECRET_ACCESS_KEY:
+ sys.stderr.write('Missing AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY environment variables.\n')
+ sys.exit(1)
+
+# Setup logging
+if DEBUGFILE:
+ import logging
+ logging.basicConfig(
+ filename=DEBUGFILE,
+ level=logging.DEBUG,
+ format='%(asctime)s %(levelname)s %(message)s')
+ logging.getLogger('boto').setLevel(getattr(logging, DEBUGLEVEL))
+else:
+ class Void(object):
+ def __getattr__(self, attr):
+ return self
+ def __call__(self, *args, **kw):
+ return self
+ logging = Void()
+
+logger = logging.getLogger('s3extfs')
+
+
+def __fix_io_encoding(last_resort_default='UTF-8'):
+ """
+ The following code is needed to work with non-ASCII characters in filenames.
+ We're trying hard to detect the system encoding.
+ """
+ import codecs
+ import locale
+ for var in ('stdin', 'stdout', 'stderr'):
+ if getattr(sys, var).encoding is None:
+ enc = None
+ if enc is None:
+ try:
+ enc = locale.getpreferredencoding()
+ except:
+ pass
+ if enc is None:
+ try:
+ enc = sys.getfilesystemencoding()
+ except:
+ pass
+ if enc is None:
+ try:
+ enc = sys.stdout.encoding
+ except:
+ pass
+ if enc is None:
+ enc = last_resort_default
+ setattr(sys, var, codecs.getwriter(enc)(getattr(sys, var), 'strict'))
+__fix_io_encoding()
+
+
+def threadmap(fun, iterable, maxthreads=16):
+ """
+ Quick and dirty threaded version of builtin method map.
+ Propagates exception safely.
+ """
+ from threading import Thread
+ import Queue
+
+ items = list(iterable)
+ nitems = len(items)
+ if nitems < 2:
+ return map(fun, items)
+
+ # Create and fill input queue
+ input = Queue.Queue()
+ output = Queue.Queue()
+
+ for i,item in enumerate(items):
+ input.put( (i,item) )
+
+ class WorkThread(Thread):
+ """
+ Takes one item from input queue (thread terminates when input queue is empty),
+ performs fun, puts result in output queue
+ """
+ def run(self):
+ while True:
+ try:
+ (i,item) = input.get_nowait()
+ try:
+ result = fun(item)
+ output.put( (i,result) )
+ except:
+ output.put( (None,sys.exc_info()) )
+ except Queue.Empty:
+ return
+
+ # Start threads
+ for i in range( min(len(items), maxthreads) ):
+ t = WorkThread()
+ t.setDaemon(True)
+ t.start()
+
+ # Wait for all threads to finish & collate results
+ ret = []
+ for i in range(nitems):
+ try:
+ i,res = output.get()
+ if i == None:
+ raise res[0],res[1],res[2]
+ except Queue.Empty:
+ break
+ ret.append(res)
+
+ return ret
+
+logger.debug('started')
+
+if S3LOCATION.upper() == "EU":
+ S3LOCATION = "eu-central-1"
+if S3LOCATION.upper() == "US":
+ S3LOCATION = "us-east-1"
+for att in dir(boto.s3.connection.Location):
+ v = getattr(boto.s3.connection.Location, att)
+ if type(v) is str and att.lower() == S3LOCATION.lower():
+ S3LOCATION = v
+ break
+logger.debug('Using location %s for new buckets', S3LOCATION)
+
+
+def get_connection(location):
+ """
+ Creates a connection to the specified region.
+ """
+ os.environ['S3_USE_SIGV4'] = 'True' # only V4 method is supported in all locations.
+ return boto.s3.connect_to_region(
+ location,
+ aws_access_key_id=AWS_ACCESS_KEY_ID,
+ aws_secret_access_key=AWS_SECRET_ACCESS_KEY
+ )
+
+
+# Global S3 default connection.
+s3 = get_connection('us-east-1')
+
+
+def get_bucket(name):
+ """
+ Returns a bucket by its name, no matter what region is it in.
+ """
+ try:
+ b = s3.get_bucket(name, validate=False)
+ b.get_location() # just to raise an exception on error
+ return b
+ except boto.exception.S3ResponseError, e:
+ # Seems this is the only proper way to switch to the bucket's region.
+ # Requesting of the default region for "?location" does not work unfortunately.
+ m = re.search(r'<Region>(.*?)</Region>', e.body)
+ if m:
+ return get_connection(m.group(1)).get_bucket(name)
+ raise
+
+
+logger.debug('argv: ' + str(sys.argv))
+try:
+ cmd = sys.argv[1]
+ args = sys.argv[2:]
+except:
+ sys.stderr.write('This program should be called from within MC\n')
+ sys.exit(1)
+
+
+def handleServerError(msg):
+ e = sys.exc_info()
+ msg += ', reason: ' + e[1].reason
+ logger.error(msg, exc_info=e)
+ sys.stderr.write(msg+'\n')
+ sys.exit(1)
+
+#
+# Lists all S3 contents
+#
+if cmd == 'list':
+ if len(args) > 0:
+ path = args[0]
+ else:
+ path = ''
+
+ logger.info('list')
+
+ rs = s3.get_all_buckets()
+
+ # Import python timezones (pytz)
+ try:
+ import pytz
+ except:
+ logger.warning('Missing pytz module, timestamps will be off')
+ # A fallback UTC tz stub
+ class pytzutc(datetime.tzinfo):
+ def __init__(self):
+ datetime.tzinfo.__init__(self)
+ self.utc = self
+ self.zone = 'UTC'
+ def utcoffset(self, dt):
+ return datetime.timedelta(0)
+ def tzname(self, dt):
+ return "UTC"
+ def dst(self, dt):
+ return datetime.timedelta(0)
+ pytz = pytzutc()
+
+
+ # Find timezone
+ # (yes, timeZONE as in _geographic zone_ not EST/CEST or whatever crap we get from time.tzname)
+ # http://regebro.wordpress.com/2008/05/10/python-and-time-zones-part-2-the-beast-returns/
+ def getGuessedTimezone():
+ # 1. check TZ env. var
+ try:
+ tz = os.getenv('TZ', '')
+ return pytz.timezone(tz)
+ except:
+ pass
+ # 2. check if /etc/timezone exists (Debian at least)
+ try:
+ if os.path.isfile('/etc/timezone'):
+ tz = open('/etc/timezone', 'r').readline().strip()
+ return pytz.timezone(tz)
+ except:
+ pass
+ # 3. check if /etc/localtime is a _link_ to something useful
+ try:
+ if os.path.islink('/etc/localtime'):
+ link = os.readlink('/etc/localtime')
+ tz = '/'.join(link.split(os.path.sep)[-2:])
+ return pytz.timezone(tz)
+ except:
+ pass
+ # 4. use time.tzname which will probably be wrong by an hour 50% of the time.
+ try:
+ return pytz.timezone(time.tzname[0])
+ except:
+ pass
+ # 5. use plain UTC ...
+ return pytz.utc
+
+ tz=getGuessedTimezone()
+ logger.debug('Using timezone: ' + tz.zone)
+
+ # AWS time is on format: 2009-01-07T16:43:39.000Z
+ # we "want" MM-DD-YYYY hh:mm (in localtime)
+ expr = re.compile(r'^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.\d{3}Z$')
+ def convDate(awsdatetime):
+ m = expr.match(awsdatetime)
+ ye,mo,da,ho,mi,se = map(int,m.groups())
+
+ dt = datetime.datetime(ye,mo,da,ho,mi,se, tzinfo=pytz.utc)
+ return dt.astimezone(tz).strftime('%m-%d-%Y %H:%M')
+
+
+ def bucketList(b):
+ b = get_bucket(b.name) # get the bucket at its own region
+ totsz = 0
+ mostrecent = '1970-01-01T00:00:00.000Z'
+ ret = []
+ for k in b.list():
+ if k.name.endswith('/'):
+ # Sometimes someone create S3 keys which are ended with "/".
+ # Extfs cannot work with them as with files, and such keys may
+ # hide same-name directories, so we skip them.
+ continue
+ mostrecent = max(mostrecent, k.last_modified)
+ datetime = convDate(k.last_modified)
+ ret.append('%10s %3d %-8s %-8s %d %s %s\n' % (
+ '-rw-r--r--', 1, USER, USER, k.size, datetime, b.name+'/'+k.name)
+ )
+ totsz += k.size
+
+ datetime=convDate(mostrecent)
+ sys.stdout.write('%10s %3d %-8s %-8s %d %s %s\n' % (
+ 'drwxr-xr-x', 1, USER, USER, totsz, datetime, b.name)
+ )
+ for line in ret:
+ sys.stdout.write(line)
+
+ threadmap(bucketList, rs)
+
+#
+# Fetch file from S3
+#
+elif cmd == 'copyout':
+ archivename = args[0]
+ storedfilename = args[1]
+ extractto = args[2]
+
+ bucket,key = storedfilename.split('/', 1)
+ logger.info('copyout bucket: %s, key: %s'%(bucket, key))
+
+ try:
+ b = get_bucket(bucket)
+ k = b.get_key(key)
+
+ out = open(extractto, 'w')
+
+ k.open(mode='r')
+ for buf in k:
+ out.write(buf)
+ k.close()
+ out.close()
+ except BotoServerError:
+ handleServerError('Unable to fetch key "%s"'%(key))
+
+#
+# Upload file to S3
+#
+elif cmd == 'copyin':
+ archivename = args[0]
+ storedfilename = args[1]
+ sourcefile = args[2]
+
+ bucket,key = storedfilename.split('/', 1)
+ logger.info('copyin bucket: %s, key: %s'%(bucket, key))
+
+ try:
+ b = get_bucket(bucket)
+ k = b.new_key(key)
+ k.set_contents_from_file(fp=open(sourcefile,'r'))
+ except BotoServerError:
+ handleServerError('Unable to upload key "%s"' % (key))
+
+#
+# Remove file from S3
+#
+elif cmd == 'rm':
+ archivename = args[0]
+ storedfilename = args[1]
+
+ bucket,key = storedfilename.split('/', 1)
+ logger.info('rm bucket: %s, key: %s'%(bucket, key))
+
+ try:
+ b = get_bucket(bucket)
+ b.delete_key(key)
+ except BotoServerError:
+ handleServerError('Unable to remove key "%s"' % (key))
+
+#
+# Create directory
+#
+elif cmd == 'mkdir':
+ archivename = args[0]
+ dirname = args[1]
+
+ logger.info('mkdir dir: %s' %(dirname))
+ if '/' in dirname:
+ logger.warning('skipping mkdir')
+ pass
+ else:
+ bucket = dirname
+ try:
+ get_connection(S3LOCATION).create_bucket(bucket, location=S3LOCATION)
+ except BotoServerError:
+ handleServerError('Unable to create bucket "%s"' % (bucket))
+
+#
+# Remove directory
+#
+elif cmd == 'rmdir':
+ archivename = args[0]
+ dirname = args[1]
+
+ logger.info('rmdir dir: %s' %(dirname))
+ if '/' in dirname:
+ logger.warning('skipping rmdir')
+ pass
+ else:
+ bucket = dirname
+ try:
+ b = get_bucket(bucket)
+ b.connection.delete_bucket(b)
+ except BotoServerError:
+ handleServerError('Unable to delete bucket "%s"' % (bucket))
+
+#
+# Run from S3
+#
+elif cmd == 'run':
+ archivename = args[0]
+ storedfilename = args[1]
+ arguments = args[2:]
+
+ bucket,key = storedfilename.split('/', 1)
+ logger.info('run bucket: %s, key: %s'%(bucket, key))
+
+ os.execv(storedfilename, arguments)
+else:
+ logger.error('unhandled, bye')
+ sys.exit(1)
+
+logger.debug('command handled')
+sys.exit(0)
+
diff --git a/src/vfs/extfs/helpers/trpm b/src/vfs/extfs/helpers/trpm
new file mode 100755
index 0000000..d9a7930
--- /dev/null
+++ b/src/vfs/extfs/helpers/trpm
@@ -0,0 +1,176 @@
+#! /bin/sh
+#
+# Browse contents of an installed RPM package.
+# This filesystem works on the entries of the "rpms" filesystem.
+#
+# Written by Erik Troan (ewt@redhat.com) 1996
+# Jakub Jelinek (jj@sunsite.mff.cuni.cz) 1996
+# Tomasz K³oczko (kloczek@rudy.mif.pg.gda.pl) 1997
+# minor changes by Wojtek Pilorz (wpilorz@bdk.lublin.pl) 1997
+# minor changes by Michele Marziani (marziani@fe.infn.it) 1997
+# slight changes to put rpm to Trpm by Balazs Nagy (julian7@kva.hu) 1998
+# locale bugfix by Michal Svec (rebel@penguin.cz) 2000
+# (C) 1996 The Free Software Foundation.
+#
+#
+
+# override any locale for dates
+unset LC_ALL
+LC_TIME=C
+export LC_TIME
+
+if rpm --nosignature --version >/dev/null 2>&1; then
+ RPM="rpm --nosignature"
+else
+ RPM="rpm"
+fi
+
+mcrpmfs_list ()
+{
+ # set MCFASTRPM_DFLT to 1 for faster rpm files handling by default, to 0 for
+ # slower handling
+ MCFASTRPM_DFLT=0
+ if test -z "$MCFASTRPM"; then
+ MCFASTRPM=$MCFASTRPM_DFLT
+ fi
+ FILEPREF="-r--r--r-- 1 root root "
+ DESC=`$RPM -qi -- "$1"`
+ DATE=`$RPM -q --qf "%{BUILDTIME:date}" -- "$1" | cut -c 5-11,21-24`
+ HEADERSIZE=`echo "$DESC" | wc -c`
+ echo "-r--r--r-- 1 root root $HEADERSIZE $DATE HEADER"
+ echo "-r-xr-xr-x 1 root root 40 $DATE UNINSTALL"
+ echo "dr-xr-xr-x 3 root root 0 $DATE INFO"
+ echo "$FILEPREF 0 $DATE INFO/NAME-VERSION-RELEASE"
+ echo "$FILEPREF 0 $DATE INFO/GROUP"
+ echo "$FILEPREF 0 $DATE INFO/BUILDHOST"
+ echo "$FILEPREF 0 $DATE INFO/SOURCERPM"
+ if test "$MCFASTRPM" = 0 ; then
+ test "`$RPM -q --qf \"%{DISTRIBUTION}\" -- "$1"`" = "(none)" ||
+ echo "$FILEPREF 0 $DATE INFO/DISTRIBUTION"
+ test "`$RPM -q --qf \"%{VENDOR}\" -- "$1"`" = "(none)" ||
+ echo "$FILEPREF 0 $DATE INFO/VENDOR"
+ test "`$RPM -q --qf \"%{DESCRIPTION}\" -- "$1"`" = "(none)" ||
+ echo "$FILEPREF 0 $DATE INFO/DESCRIPTION"
+ test "`$RPM -q --qf \"%{SUMMARY}\" -- "$1"`" = "(none)" ||
+ echo "$FILEPREF 0 $DATE INFO/SUMMARY"
+ if test "`$RPM -q --qf \"%{RPMTAG_PREIN}%{RPMTAG_POSTIN}%{RPMTAG_PREUN}%{RPMTAG_POSTUN}%{VERIFYSCRIPT}\" -- "$1"`" != "(none)(none)(none)(none)(none)"; then
+ echo "dr-xr-xr-x 1 root root 0 $DATE INFO/SCRIPTS"
+ test "`$RPM -q --qf \"%{RPMTAG_PREIN}\" -- "$1"`" = '(none)' ||
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREIN"
+ test "`$RPM -q --qf \"%{RPMTAG_POSTIN}\" -- "$1"`" = '(none)' ||
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTIN"
+ test "`$RPM -q --qf \"%{RPMTAG_PREUN}\" -- "$1"`" = '(none)' ||
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREUN"
+ test "`$RPM -q --qf \"%{RPMTAG_POSTUN}\" -- "$1"`" = '(none)' ||
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTUN"
+ test "`$RPM -q --qf \"%{VERIFYSCRIPT}\" -- "$1"`" = '(none)' ||
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/VERIFYSCRIPT"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/ALL"
+ fi
+ else
+ echo "$FILEPREF 0 $DATE INFO/DISTRIBUTION"
+ echo "$FILEPREF 0 $DATE INFO/VENDOR"
+ echo "$FILEPREF 0 $DATE INFO/DESCRIPTION"
+ echo "$FILEPREF 0 $DATE INFO/SUMMARY"
+ echo "dr-xr-xr-x 1 root root 0 $DATE INFO/SCRIPTS"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREIN"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTIN"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/PREUN"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/POSTUN"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/VERIFYSCRIPT"
+ echo "$FILEPREF 0 $DATE INFO/SCRIPTS/ALL"
+ fi
+ if test "$MCFASTRPM" = 0 ; then
+ test "`$RPM -q --qf \"%{PACKAGER}\" -- "$1"`" = "(none)" ||
+ echo "$FILEPREF 0 $DATE INFO/PACKAGER"
+ test "`$RPM -q --qf \"%{URL}\" -- "$1"`" = "(none)" ||
+ echo "$FILEPREF 0 $DATE INFO/URL"
+ test "`$RPM -q --qf \"%{EPOCH}\" -- "$1"`" = "(none)" ||
+ echo "$FILEPREF 0 $DATE INFO/EPOCH"
+ test "`$RPM -q --qf \"%{LICENSE}\" -- "$1"`" = "(none)" ||
+ echo "$FILEPREF 0 $DATE INFO/LICENSE"
+ else
+ echo "$FILEPREF 0 $DATE INFO/PACKAGER"
+ echo "$FILEPREF 0 $DATE INFO/URL"
+ echo "$FILEPREF 0 $DATE INFO/EPOCH"
+ echo "$FILEPREF 0 $DATE INFO/LICENSE"
+ fi
+ echo "$FILEPREF 0 $DATE INFO/BUILDTIME"
+ echo "$FILEPREF 0 $DATE INFO/RPMVERSION"
+ echo "$FILEPREF 0 $DATE INFO/OS"
+ echo "$FILEPREF 0 $DATE INFO/SIZE"
+ if test "$MCFASTRPM" != 0 ; then
+ $RPM -q --qf "[%{REQUIRENAME}\n]" -- "$1" | grep "(none)" > /dev/null ||
+ echo "$FILEPREF 0 $DATE INFO/REQUIRENAME"
+ $RPM -q --qf "[%{OBSOLETES}\n]" -- "$1" | grep "(none)" > /dev/null ||
+ echo "$FILEPREF 0 $DATE INFO/OBSOLETES"
+ $RPM -q --qf "[%{PROVIDES}\n]" -- "$1" | grep "(none)" > /dev/null ||
+ echo "$FILEPREF 0 $DATE INFO/PROVIDES"
+ $RPM -q --qf "[%{CONFLICTS}\n]" -- "$1" | grep "(none)" > /dev/null ||
+ echo "$FILEPREF 0 $DATE INFO/CONFLICTS"
+ test "`$RPM -q --qf \"%{CHANGELOGTEXT}\" -- "$1"`" = "(none)" ||
+ echo "$FILEPREF 0 $DATE INFO/CHANGELOG"
+ else
+ echo "$FILEPREF 0 $DATE INFO/REQUIRENAME"
+ echo "$FILEPREF 0 $DATE INFO/OBSOLETES"
+ echo "$FILEPREF 0 $DATE INFO/PROVIDES"
+ echo "$FILEPREF 0 $DATE INFO/CONFLICTS"
+ echo "$FILEPREF 0 $DATE INFO/CHANGELOG"
+ fi
+
+ $RPM -qlv -- "$1" | grep '^[A-Za-z0-9-]'
+}
+
+mcrpmfs_copyout ()
+{
+ case "$2" in
+ HEADER) $RPM -qi -- "$1" > "$3"; exit 0;;
+ UNINSTALL) echo "# Run this to uninstall this RPM package" > "$3"; exit 0;;
+ INFO/NAME-VERSION-RELEASE) $RPM -q --qf "%{NAME}-%{VERSION}-%{RELEASE}\n" -- "$1" > "$3"; exit 0;;
+ INFO/RELEASE) $RPM -q --qf "%{RELEASE}\n" -- "$1" > "$3"; exit 0;;
+ INFO/GROUP) $RPM -q --qf "%{GROUP}\n" -- "$1" > "$3"; exit 0;;
+ INFO/DISTRIBUTION) $RPM -q --qf "%{DISTRIBUTION}\n" -- "$1" > "$3"; exit 0;;
+ INFO/VENDOR) $RPM -q --qf "%{VENDOR}\n" -- "$1" > "$3"; exit 0;;
+ INFO/BUILDHOST) $RPM -q --qf "%{BUILDHOST}\n" -- "$1" > "$3"; exit 0;;
+ INFO/SOURCERPM) $RPM -q --qf "%{SOURCERPM}\n" -- "$1" > "$3"; exit 0;;
+ INFO/DESCRIPTION) $RPM -q --qf "%{DESCRIPTION}\n" -- "$1" > "$3"; exit 0;;
+ INFO/PACKAGER) $RPM -q --qf "%{PACKAGER}\n" -- "$1" > "$3"; exit 0;;
+ INFO/URL) $RPM -q --qf "%{URL}\n" -- "$1" > "$3"; exit 0;;
+ INFO/BUILDTIME) $RPM -q --qf "%{BUILDTIME:date}\n" -- "$1" > "$3"; exit 0;;
+ INFO/EPOCH) $RPM -q --qf "%{EPOCH}\n" -- "$1" > "$3"; exit 0;;
+ INFO/LICENSE) $RPM -q --qf "%{LICENSE}\n" -- "$1" > "$3"; exit 0;;
+ INFO/RPMVERSION) $RPM -q --qf "%{RPMVERSION}\n" -- "$1" > "$3"; exit 0;;
+ INFO/REQUIRENAME) $RPM -q --qf "[%{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}\n]" -- "$1" > "$3"; exit 0;;
+ INFO/OBSOLETES) $RPM -q --qf "[%{OBSOLETENAME} %|OBSOLETEFLAGS?{%{OBSOLETEFLAGS:depflags} %{OBSOLETEVERSION}}:{}|\n]" -- "$1" > "$3"; exit 0;;
+ INFO/PROVIDES) $RPM -q --qf "[%{PROVIDES}\n]" -- "$1" > "$3"; exit 0;;
+ INFO/CONFLICTS) $RPM -q --qf "[%{CONFLICTS}\n]" -- "$1" > "$3"; exit 0;;
+ INFO/SCRIPTS/PREIN) $RPM -q --qf "%{RPMTAG_PREIN}\n" -- "$1" > "$3"; exit 0;;
+ INFO/SCRIPTS/POSTIN) $RPM -q --qf "%{RPMTAG_POSTIN}\n" -- "$1" > "$3"; exit 0;;
+ INFO/SCRIPTS/PREUN) $RPM -q --qf "%{RPMTAG_PREUN}\n" -- "$1" > "$3"; exit 0;;
+ INFO/SCRIPTS/POSTUN) $RPM -q --qf "%{RPMTAG_POSTUN}\n" -- "$1" > "$3"; exit 0;;
+ INFO/SCRIPTS/VERIFYSCRIPT) $RPM -q --qf "%{VERIFYSCRIPT}\n" -- "$1" > "$3"; exit 0;;
+ INFO/SCRIPTS/ALL) $RPM -q --scripts -- "$1" > "$3"; exit 0;;
+ INFO/SUMMARY) $RPM -q --qf "%{SUMMARY}\n" -- "$1" > "$3"; exit 0;;
+ INFO/OS) $RPM -q --qf "%{OS}\n" -- "$1" > "$3"; exit 0;;
+ INFO/CHANGELOG) $RPM -q --qf "[* %{CHANGELOGTIME:date} %{CHANGELOGNAME}\n%{CHANGELOGTEXT}\n\n]\n" -- "$1" > "$3"; exit 0;;
+ INFO/SIZE) $RPM -q --qf "%{SIZE} bytes\n" -- "$1" > "$3"; exit 0;;
+ *)
+ cp "/$2" "$3"
+ esac
+}
+
+mcrpmfs_run ()
+{
+ case "$2" in
+ UNINSTALL) echo "Uninstalling $1"; rpm -e -- "$1"; exit 0;;
+ esac
+}
+
+name=`sed 's/.*\///;s/\.trpm$//' "$2"`
+
+case "$1" in
+ list) mcrpmfs_list "$name"; exit 0;;
+ copyout) mcrpmfs_copyout "$name" "$3" "$4"; exit 0;;
+ run) mcrpmfs_run "$name" "$3"; exit 1;;
+esac
+exit 1
diff --git a/src/vfs/extfs/helpers/u7z b/src/vfs/extfs/helpers/u7z
new file mode 100755
index 0000000..91301c3
--- /dev/null
+++ b/src/vfs/extfs/helpers/u7z
@@ -0,0 +1,135 @@
+#! /bin/sh
+#
+# extfs support for p7zip
+# Written by Pavel Roskin <proski@gnu.org>
+# Some Bugfixes/workarounds by Sergiy Niskorodov <sgh@mail.zp.ua>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+P7ZIP=`which 7z 2>/dev/null` \
+ || P7ZIP=`which 7zz 2>/dev/null` \
+ || P7ZIP=`which 7za 2>/dev/null` \
+ || P7ZIP=`which 7zr 2>/dev/null` \
+ || P7ZIP=""
+
+# Let the test framework hook in:
+P7ZIP=${MC_TEST_EXTFS_LIST_CMD:-$P7ZIP}
+STAT=${MC_TEST_EXTFS_U7Z_STAT:-stat}
+
+mcu7zip_list ()
+{
+ # Symlinks are not shown - no idea how to distinguish them
+ # Read-only files are not shown as such - it's rarely useful
+
+ ugid="`id -nu` `id -ng`"
+
+ date_re='^\(....\)-\(..\)-\(..\) \(..:..:..\)' # 19 chars.
+ date_mc='\2-\3-\1 \4'
+ empty_date_re='^ \{19\}'
+
+ size_re='............' # 12 chars.
+ empty_size_re=' \{12\}'
+ zero_size=' 0'
+
+ # archive entries can have no datetime info, 7z will use archive file datetime
+ date_archive=`$STAT -c %y "$1" 2>/dev/null | sed -n "s/${date_re}.*/${date_mc}/p" 2>/dev/null`
+ [ "${date_archive}"x = x ] && date_archive=`ls -lan "$1" 2>/dev/null | awk '{print $6, $7, $8}' 2>/dev/null`
+ [ "${date_archive}"x = x ] && date_archive="01-01-1970 00:00:00"
+
+ $P7ZIP l "$1" | sed -n "
+
+ # If the uncompressed size is missing, we copy the compressed size onto it.
+ #
+ # But first, if the compressed size is missing too, set it to zero:
+ s/^\(.\{19\} [D.]....\) $empty_size_re $empty_size_re/\1 $zero_size $zero_size/
+ # Next, do the copy:
+ s/^\(.\{19\} [D.]....\) $empty_size_re \($size_re\)/\1 \2 \2/
+ #
+ # (We use '.\{19\}' as the date may be missing. It may give false positives
+ # but we don't mind: the printing commands that follow use strict patterns.).
+
+ # Handle directories.
+ s/$date_re D.... $size_re $size_re\(.*\)/drwxr-xr-x 1 $ugid 0 $date_mc \5/p
+ s/$empty_date_re D.... $size_re $size_re\(.*\)/drwxr-xr-x 1 $ugid 0 $date_archive \1/p
+
+ # Handle normal files.
+ s/$date_re \..... \($size_re\) $size_re\(.*\)/-rw-r--r-- 1 $ugid \5 $date_mc \6/p
+ s/$empty_date_re \..... \($size_re\) $size_re\(.*\)/-rw-r--r-- 1 $ugid \1 $date_archive \2/p
+ "
+}
+
+mcu7zip_copyout ()
+{
+ #first we check if we have old p7zip archive with prefix ./ in filename
+ $P7ZIP l "$1" "$2" | grep -q "0 files, 0 folders" && \
+ EXFNAME='*./'"$2" || EXFNAME="$2"
+ $P7ZIP e -so "$1" "$EXFNAME" > "$3" 2>/dev/null
+}
+
+mcu7zip_copyin ()
+{
+ $P7ZIP a -si"$2" "$1" <"$3" >/dev/null 2>&1
+}
+
+mcu7zip_mkdir ()
+{
+ dir=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-u7z.XXXXXX"` || exit 1
+ mkdir -p "$dir"/"$2"
+ $P7ZIP a -w"$dir" "$1" "$dir"/"$2" >/dev/null 2>&1
+ rm -rf "$dir"
+}
+
+mcu7zip_rm ()
+{
+ # NOTE: Version 4.20 fails to delete files in subdirectories
+ #first we check if we have old p7zip archive with prefix ./ in filename
+ $P7ZIP l "$1" "$2" | grep -q "0 files, 0 folders" && \
+ EXFNAME='*./'"$2" || EXFNAME="$2"
+ $P7ZIP d "$1" "$EXFNAME" 2>&1 | grep -q E_NOTIMPL > /dev/null 2>&1 && \
+ { printf "Function not implemented...\n7z cannot delete from solid archive." >&2 ; exit 1 ; }
+}
+
+mcu7zip_rmdir ()
+{
+ #first we check if we have old p7zip archive with prefix ./ in filename
+ $P7ZIP l "$1" "$2" | grep -q "0 files, 0 folders" && \
+ EXFNAME='*./'"$2" || EXFNAME="$2"
+ $P7ZIP d "$1" "$EXFNAME"/ 2>&1 | grep -q E_NOTIMPL > /dev/null 2>&1 && \
+ { printf "Function not implemented...\n7z cannot delete from solid archive." >&2 ; exit 1 ; }
+}
+
+# override any locale for dates
+LC_DATE=C
+export LC_DATE
+
+umask 077
+
+if [ -z "$P7ZIP" ]; then
+ echo "Error: could not find p7zip (looked for 7z, 7za and 7zr)" >&2
+ exit 1
+fi
+
+cmd="$1"
+shift
+
+case "$cmd" in
+ list) mcu7zip_list "$@" | sort -k 8 ;;
+ copyout) mcu7zip_copyout "$@" ;;
+ copyin) mcu7zip_copyin "$@" ;;
+ mkdir) mcu7zip_mkdir "$@" ;;
+ rm) mcu7zip_rm "$@" ;;
+ rmdir) mcu7zip_rmdir "$@" ;;
+ *) exit 1 ;;
+esac
+exit 0
diff --git a/src/vfs/extfs/helpers/uace.in b/src/vfs/extfs/helpers/uace.in
new file mode 100644
index 0000000..22eae30
--- /dev/null
+++ b/src/vfs/extfs/helpers/uace.in
@@ -0,0 +1,67 @@
+#! /bin/sh
+
+#
+# ACE Virtual filesystem executive v0.1
+# Works with unace v2.5
+
+# Note: There are two packages for Debian: 'unace' (v1.2b) and
+# 'unace-nonfree' (v2.x). This script supports 'unace-nonfree' only.
+# 'unace', which supports only old versions of ACE archives (and is
+# therefore of little use), uses the pipe character to separate columns
+# in its listing format.
+
+# Copyright (C) 2008 Jacques Pelletier
+# May be distributed under the terms of the GNU Public License
+# <jpelletier@ieee.org>
+#
+
+# Define which archiver you are using with appropriate options
+ACE_LIST=${MC_TEST_EXTFS_LIST_CMD:-"unace l"}
+ACE_GET="unace x"
+# ACE_PUT="unace ?" not available
+
+# The 'list' command executive
+
+# Unace: DD.MM.YY HH:MM packed size ratio file
+# ls:
+mc_ace_fs_list()
+{
+ if [ "x$UID" = "x" ]; then
+ UID=`id -ru 2>/dev/null`
+ if [ "x$UID" = "x" ]; then
+ UID=0
+ fi
+ fi
+ $ACE_LIST "$1" | @AWK@ -v uid=$UID '
+/%/ {
+ split($1,date,".")
+
+ if (date[3] > 50)
+ date[3]=date[3] + 1900
+ else
+ date[3]=date[3] + 2000
+
+ printf "-rw-r--r-- 1 %-8d %-8d %8d %02d-%02d-%04d %s %s\n", uid, 0, $4, date[2], date[1], date[3], $2, $6
+}' 2>/dev/null
+ exit 0
+}
+
+# Command: copyout archivename storedfilename extractto
+mc_ace_fs_copyout()
+{
+ $ACE_GET "$1" "$2" > /dev/null 2>&1
+ mv "$2" "$3"
+}
+
+# The main routine
+umask 077
+
+cmd="$1"
+shift
+
+case "$cmd" in
+ list) mc_ace_fs_list "$@" ;;
+ copyout) mc_ace_fs_copyout "$@" ;;
+ *) exit 1 ;;
+esac
+exit 0
diff --git a/src/vfs/extfs/helpers/ualz.in b/src/vfs/extfs/helpers/ualz.in
new file mode 100644
index 0000000..d054553
--- /dev/null
+++ b/src/vfs/extfs/helpers/ualz.in
@@ -0,0 +1,68 @@
+#!/bin/sh
+#
+# Written by Pavel Roskin <proski@gnu.org>
+# (C) 2005 The Free Software Foundation.
+#
+#
+
+UNALZ=unalz
+
+mcualz_list ()
+{
+ $UNALZ -l "$1" | @AWK@ -v uid=`id -nu` -v gid=`id -ng` '
+{
+ if ($1 ~ /[0-9][0-9][:/][0-9][0-9][:/][0-9][0-9]$/)
+ {
+ # Kludge for non-POSIX date format in unalz 0.50
+ split($1, date, "[/:]")
+ if (length(date[1]) == 4) {
+ pdate = date[2] "/" date[3] "/" date[1]
+ } else {
+ pdate = date[1] "/" date[2] "/" date[3]
+ }
+
+ time=$2
+ perm=$3
+ size=$4
+ sub(/^ *[^ ]* *[^ ]* *[^ ]* *[^ ]* *[^ ]* */, "")
+ file=$0
+ gsub(/\\/, "/", file)
+ if (perm ~ /.D../)
+ perm = "drwxr-xr-x"
+ else
+ perm = "-rw-r--r--"
+ printf "%s 1 %s %s %d %s %s %s\n", perm, uid, gid, size, pdate, time, file
+ }
+}
+'
+}
+
+mcualz_copyout ()
+{
+ TMPDIR=`mktemp -d ${MC_TMPDIR:-/tmp}/mctmpdir-ualz.XXXXXX` || exit 1
+
+ # This is a workaround for a bug in unalz 0.50 - it crashes if the
+ # output directory is an absolute path.
+ dir=`dirname "$TMPDIR/$2"`
+ mkdir -p "$dir"
+
+ $UNALZ -d "$TMPDIR" "$1" "$2" >/dev/null
+ cat "$TMPDIR/$2" > "$3"
+ rm -rf "$TMPDIR"
+}
+
+# override any locale for dates
+LC_ALL=C
+export LC_ALL
+umask 077
+
+cmd="$1"
+shift
+
+case "$cmd" in
+ list) mcualz_list "$@" ;;
+ copyout) mcualz_copyout "$@" ;;
+ *) exit 1 ;;
+esac
+
+exit 0
diff --git a/src/vfs/extfs/helpers/uar.in b/src/vfs/extfs/helpers/uar.in
new file mode 100644
index 0000000..269bdb6
--- /dev/null
+++ b/src/vfs/extfs/helpers/uar.in
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Written by Alex Kuchma <ask@bcs.zp.ua>
+# Alex Tkachenko <alex@bcs.zp.ua>
+# Updated by Vitezslav Samel <xsamel00@dcse.fee.vutbr.cz>
+#
+# (C) 1997, 1998 The Free Software Foundation.
+#
+#
+
+XAR=ar
+
+mcarfs_list ()
+{
+ # If $temp_replace string is part of the filename that part might get lost
+ temp_replace='Unique Separator String'
+ thisyear="`date +%Y`"
+ $XAR tv "$1" | sed 's,^,-,;s, , 1 ,;s,/, ,' |
+ sed -e "s/\( [0-2][0-9]\:[0-5][0-9]\)\( $thisyear \)\(.*\)/\1$temp_replace\3/" |
+ sed -e "s/\( [0-2][0-9]\:[0-5][0-9] \)\([12][0-9][0-9][0-9] \)\(.*\)/ \2\3/" |
+ sed -e "s/$temp_replace/ /"
+}
+
+mcarfs_copyout ()
+{
+ $XAR p "$1" "$2" > "$3"
+}
+
+mcarfs_copyin ()
+{
+ TMPDIR=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-uar.XXXXXX"` || exit 1
+ name=`basename "$2"`
+ (cd "$TMPDIR" && cp -fp "$3" "$name" && $XAR r "$1" "$name")
+ rm -rf "$TMPDIR"
+}
+
+mcarfs_rm ()
+{
+ $XAR d "$1" "$2"
+}
+
+# override any locale for dates
+LC_ALL=C
+export LC_ALL
+
+umask 077
+case "$1" in
+ list) mcarfs_list "$2" ;;
+ copyout) shift; mcarfs_copyout "$@" ;;
+ copyin) shift; mcarfs_copyin "$@" ;;
+ rm) shift; mcarfs_rm "$@" ;;
+ mkdir|rmdir)
+ echo "mcarfs: ar archives cannot contain directories." 1>&2
+ exit 1;;
+ *)
+ echo "mcarfs: unknown command: \"$1\"." 1>&2
+ exit 1;;
+esac
+
+exit 0
diff --git a/src/vfs/extfs/helpers/uarc.in b/src/vfs/extfs/helpers/uarc.in
new file mode 100644
index 0000000..a81839a
--- /dev/null
+++ b/src/vfs/extfs/helpers/uarc.in
@@ -0,0 +1,92 @@
+#! /bin/sh
+
+#
+# ARC Virtual filesystem executive
+# Copyright (C) 2008 Jacques Pelletier
+# May be distributed under the terms of the GNU Public License
+# <jpelletier@ieee.org>
+#
+
+# Define which archiver you are using with appropriate options
+ARC_LIST=${MC_TEST_EXTFS_LIST_CMD:-"arc v"}
+ARC_GET="arc x"
+ARC_PUT="arc a"
+ARC_DEL="arc d"
+
+# The 'list' command executive
+
+mc_arc_fs_list()
+{
+ if [ "x$UID" = "x" ]; then
+ UID=`id -ru 2>/dev/null`
+ if [ "x$UID" = "x" ]; then
+ UID=0
+ fi
+ fi
+ $ARC_LIST "$1" | @AWK@ -v uid=$UID '
+BEGIN {
+ # Copied from uzoo.in.
+ split("Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec", month_list, ":")
+ for (i=1; i<=12; i++) {
+ month[month_list[i]] = i
+ }
+}
+/^Name/ { next }
+/===/ { next }
+/^Total/ { next }
+{
+ if ($8 > 50)
+ $8=$8 + 1900
+ else
+ $8=$8 + 2000
+
+ split($9, a, ":")
+
+ # convert AM/PM to 00-23
+ if (a[2] ~ /a$|p$/)
+ {
+ if (a[2] ~ /p$/)
+ a[1] = a[1]+12
+
+ a[2]=substr(a[2],1,2)
+ }
+
+ printf "-rw-r--r-- 1 %-8d %-8d %8d %02d-%02d-%04d %02d:%02d %s\n", uid, 0, $2, month[$7], $6, $8, a[1], a[2], $1
+}' 2>/dev/null
+ exit 0
+}
+
+# Command: copyout archivename storedfilename extractto
+mc_arc_fs_copyout()
+{
+ $ARC_GET "$1" "$2" 2> /dev/null
+ mv "$2" "$3"
+}
+
+# Command: copyin archivename storedfilename sourcefile
+mc_arc_fs_copyin()
+{
+ mv "$3" "$2"
+ $ARC_PUT "$1" "$2" 2> /dev/null
+}
+
+# Command: rm archivename storedfilename
+mc_arc_fs_rm()
+{
+ $ARC_DEL "$1" "$2" 2> /dev/null
+}
+
+# The main routine
+umask 077
+
+cmd="$1"
+shift
+
+case "$cmd" in
+ list) mc_arc_fs_list "$@" ;;
+ copyout) mc_arc_fs_copyout "$@" ;;
+ copyin) mc_arc_fs_copyin "$@" ;;
+ rm) mc_arc_fs_rm "$@" ;;
+ *) exit 1 ;;
+esac
+exit 0
diff --git a/src/vfs/extfs/helpers/uarj.in b/src/vfs/extfs/helpers/uarj.in
new file mode 100644
index 0000000..15549a0
--- /dev/null
+++ b/src/vfs/extfs/helpers/uarj.in
@@ -0,0 +1,75 @@
+#! /bin/sh
+#
+# Written by Viatcheslav Odintsov (2:5020/181)
+# (C) 2002 ARJ Software Russia.
+#
+# This is an updated parser for ARJ archives in Midnight Commander. You need
+# full ARJ rather than UNARJ. Open-source ARJ v 3.10 for Unix platforms can
+# be obtained here:
+#
+# - http://www.sourceforge.net/projects/arj/
+# - http://arj.sourceforge.net/
+
+
+ARJ="arj -+ -ja1"
+
+
+mcarjfs_list ()
+{
+ $ARJ v "$1" | @AWK@ -v uuid=$(id -ru) '
+ {
+ if (($0 ~ /^[0-9]+\) .*/)||($0 ~ /^------------ ---------- ---------- -----/)){
+ if (filestr ~ /^[0-9]+\) .*/) {
+ printf "%s 1 %-8d %-8d %8d %02d-%02d-%02d %02d:%02d %s%s\n", perm, uid, gid, size, date[2], date[3], date[1], time[1], time[2], file, symfile
+ perm=""
+ file=""
+ symfile=""
+ filestr=""
+ }
+ }
+
+ if ($0 ~ /^[0-9]+\) .*/) {
+ filestr=$0
+ sub(/^[0-9]*\) /, "")
+ file=$0
+ uid=uuid
+ gid=0
+ }
+
+ if ($0 ~ /^.* [0-9]+[\t ]+[0-9]+ [0-9]\.[0-9][0-9][0-9] [0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9].*/) {
+ size=$3
+ split($6, date, "-")
+ split($7, time, ":")
+ if ($8 ~ /^[rwx-]/) {perm=$8;}
+ else {perm="-rw-r--r--"}
+ }
+
+ if ($0 ~ /^[\t ]+SymLink -> .*/) {
+ symfile = " -> "$3
+ perm="l"substr(perm, 2)
+ }
+
+ if ($0 ~ /^[\t ]+Owner: UID [0-9]+\, GID [0-9]+/) {
+ uid=$3
+ gid=$5
+ owner=1
+ }
+ }'
+}
+
+
+mcarjfs_copyout ()
+{
+ $ARJ e -y "$1" "$2" -jw"$3" >/dev/null 2>/dev/null
+}
+
+
+umask 077
+cmd="$1"
+shift
+case "$cmd" in
+ list) mcarjfs_list "$@" ;;
+ copyout) mcarjfs_copyout "$@" ;;
+ *) exit 1 ;;
+esac
+exit 0
diff --git a/src/vfs/extfs/helpers/uc1541 b/src/vfs/extfs/helpers/uc1541
new file mode 100755
index 0000000..dc15b42
--- /dev/null
+++ b/src/vfs/extfs/helpers/uc1541
@@ -0,0 +1,702 @@
+#!/usr/bin/env python
+"""
+UC1541 Virtual filesystem
+
+Author: Roman 'gryf' Dobosz <gryf73@gmail.com>
+Date: 2019-09-20
+Version: 3.3
+Licence: BSD
+source: https://bitbucket.org/gryf/uc1541
+mirror: https://github.com/gryf/uc1541
+"""
+
+import sys
+import re
+import os
+import gzip
+from subprocess import Popen, PIPE
+
+if os.getenv('UC1541_DEBUG'):
+ import logging
+ LOG = logging.getLogger('UC1541')
+ LOG.setLevel(logging.DEBUG)
+ FILE_HANDLER = logging.FileHandler("/tmp/uc1541.log")
+ FILE_FORMATTER = logging.Formatter("%(asctime)s %(levelname)-8s "
+ "%(lineno)s %(funcName)s - %(message)s")
+ FILE_HANDLER.setFormatter(FILE_FORMATTER)
+ FILE_HANDLER.setLevel(logging.DEBUG)
+ LOG.addHandler(FILE_HANDLER)
+else:
+ class LOG(object):
+ """
+ Dummy logger object. Does nothing.
+ """
+ @classmethod
+ def debug(*args, **kwargs):
+ pass
+
+ @classmethod
+ def info(*args, **kwargs):
+ pass
+
+ @classmethod
+ def warning(*args, **kwargs):
+ pass
+
+ @classmethod
+ def error(*args, **kwargs):
+ pass
+
+ @classmethod
+ def critical(*args, **kwargs):
+ pass
+
+
+SECLEN = 256
+
+
+def _ord(string_or_int):
+ """
+ Return an int value for the (possible) string passed in argument. This
+ function is for compatibility between python2 and python3, where single
+ element in byte string array is a string or an int respectively.
+ """
+ try:
+ return ord(string_or_int)
+ except TypeError:
+ return string_or_int
+
+
+def _get_raw(dimage):
+ """
+ Try to get contents of the D64 image either it's gzip compressed or not.
+ """
+ raw = None
+ with gzip.open(dimage, 'rb') as fobj:
+ # Although the common approach with gzipped files is to check the
+ # magic number, in this case there is no guarantee that first track
+ # does not contain exactly the same byte sequence as the magic number.
+ # So the only way left is to actually try to uncompress the file.
+ try:
+ raw = fobj.read()
+ except (IOError, OSError):
+ pass
+ if not raw:
+ with open(dimage, 'rb') as fobj:
+ raw = fobj.read()
+
+ return raw
+
+
+def _get_implementation(disk):
+ """
+ Check the file under fname and return right class for creating an object
+ corresponding for the file
+ """
+ len_map = {822400: D81, # 80 tracks
+ 819200: D81, # 80 tracks, 3200 error bytes
+ 349696: D71, # 70 tracks
+ 351062: D71, # 70 tracks, 1366 error bytes
+ 174848: D64, # usual d64 disc image, 35 tracks, no errors
+ 175531: D64, # 35 track, 683 error bytes
+ 196608: D64, # 40 track, no errors
+ 197376: D64} # 40 track, 768 error bytes
+
+ if disk[:32].startswith(b'C64'):
+ return # T64
+
+ return len_map.get(len(disk))(disk)
+
+
+class Disk(object):
+ """
+ Represent common disk interface
+ """
+ CHAR_MAP = {32: ' ', 33: '!', 34: '"', 35: '#', 37: '%', 38: '&', 39: "'",
+ 40: '(', 41: ')', 42: '*', 43: '+', 44: ',', 45: '-', 46: '.',
+ 47: '/', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5',
+ 54: '6', 55: '7', 56: '8', 57: '9', 59: ';', 60: '<', 61: '=',
+ 62: '>', 63: '?', 64: '@', 65: 'a', 66: 'b', 67: 'c', 68: 'd',
+ 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k',
+ 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r',
+ 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y',
+ 90: 'z', 91: '[', 93: ']', 97: 'A', 98: 'B', 99: 'C',
+ 100: 'D', 101: 'E', 102: 'F', 103: 'G', 104: 'H', 105: 'I',
+ 106: 'J', 107: 'K', 108: 'L', 109: 'M', 110: 'N', 111: 'O',
+ 112: 'P', 113: 'Q', 114: 'R', 115: 'S', 116: 'T', 117: 'U',
+ 118: 'V', 119: 'W', 120: 'X', 121: 'Y', 122: 'Z', 193: 'A',
+ 194: 'B', 195: 'C', 196: 'D', 197: 'E', 198: 'F', 199: 'G',
+ 200: 'H', 201: 'I', 202: 'J', 203: 'K', 204: 'L', 205: 'M',
+ 206: 'N', 207: 'O', 208: 'P', 209: 'Q', 210: 'R', 211: 'S',
+ 212: 'T', 213: 'U', 214: 'V', 215: 'W', 216: 'X', 217: 'Y',
+ 218: 'Z'}
+
+ FILE_TYPES = {0b000: 'del',
+ 0b001: 'seq',
+ 0b010: 'prg',
+ 0b011: 'usr',
+ 0b100: 'rel'}
+
+ DIR_TRACK = 18
+ DIR_SECTOR = 1
+
+ def __init__(self, raw):
+ """
+ Init
+ """
+ self.raw = raw
+ self.current_sector_data = None
+ self.next_sector = 0
+ self.next_track = None
+ self._dir_contents = []
+ self._already_done = []
+
+ def _map_filename(self, string):
+ """
+ Transcode filename to ASCII compatible. Replace not supported
+ characters with jokers.
+ """
+
+ filename = list()
+
+ for chr_ in string:
+ if _ord(chr_) == 160: # shift+space character; $a0
+ break
+
+ character = D64.CHAR_MAP.get(_ord(chr_), '?')
+ filename.append(character)
+
+ # special cases
+ if filename[0] == "-":
+ filename[0] = "?"
+
+ LOG.debug("string: ``%s'' mapped to: ``%s''", string,
+ "".join(filename))
+ return "".join(filename)
+
+ def _go_to_next_sector(self):
+ """
+ Fetch (if exist) next sector from a directory chain
+ Return False if the chain ends, True otherwise
+ """
+
+ # Well, self.next_sector _should_ have value $FF, but apparently there
+ # are the cases where it is not, therefore checking for that will not
+ # be performed and value of $00 on the next track will end the
+ # directory
+ if self.next_track == 0:
+ LOG.debug("End of directory")
+ return False
+
+ if self.next_track is None:
+ LOG.debug("Going to the track: %s, %s", self.DIR_TRACK,
+ self.DIR_SECTOR)
+ offset = self._get_offset(self.DIR_TRACK, self.DIR_SECTOR)
+ else:
+ offset = self._get_offset(self.next_track, self.next_sector)
+ LOG.debug("Going to the track: %s,%s", self.next_track,
+ self.next_sector)
+
+ self.current_sector_data = self.raw[offset:offset + SECLEN]
+
+ # Guard for reading data out of bound - that happened for discs which
+ # store only raw data, even on directory track
+ if not self.current_sector_data:
+ return False
+
+ self.next_track = _ord(self.current_sector_data[0])
+ self.next_sector = _ord(self.current_sector_data[1])
+
+ if (self.next_track, self.next_sector) in self._already_done:
+ # Just a failsafe. Endless loop is not what is expected.
+ LOG.debug("Loop in track/sector pointer at %d,%d",
+ self.next_track, self.next_sector)
+ self._already_done = []
+ return False
+
+ self._already_done.append((self.next_track, self.next_sector))
+ LOG.debug("Next track: %s,%s", self.next_track, self.next_sector)
+ return True
+
+ def _get_ftype(self, num):
+ """
+ Get filetype as a string
+ """
+ return D64.FILE_TYPES.get(int("%d%d%d" % (num & 4 and 1,
+ num & 2 and 1,
+ num & 1), 2), '???')
+
+ def _get_offset(self, track, sector):
+ """
+ Return offset (in bytes) for specified track and sector.
+ """
+ return 0
+
+ def _harvest_entries(self):
+ """
+ Traverse through sectors and store entries in _dir_contents
+ """
+ sector = self.current_sector_data
+ for dummy in range(8):
+ entry = sector[:32]
+ ftype = _ord(entry[2])
+
+ if ftype == 0: # deleted
+ sector = sector[32:]
+ continue
+
+ type_verbose = self._get_ftype(ftype)
+
+ protect = _ord(entry[2]) & 64 and "<" or " "
+ fname = entry[5:21]
+ if ftype == 'rel':
+ size = _ord(entry[23])
+ else:
+ size = _ord(entry[30]) + _ord(entry[31]) * 226
+
+ self._dir_contents.append({'fname': self._map_filename(fname),
+ 'ftype': type_verbose,
+ 'size': size,
+ 'protect': protect})
+ sector = sector[32:]
+
+ def list_dir(self):
+ """
+ Return directory list as list of dict with keys:
+ fname, ftype, protect and size
+ """
+ while self._go_to_next_sector():
+ self._harvest_entries()
+
+ return self._dir_contents
+
+
+class D64(Disk):
+ """
+ Implement d64 directory reader
+ """
+
+ def _get_offset(self, track, sector):
+ """
+ Return offset (in bytes) for specified track and sector.
+
+ Track Sectors/track # Tracks
+ ----- ------------- ---------
+ 1-17 21 17
+ 18-24 19 7
+ 25-30 18 6
+ 31-40 17 10
+ """
+ offset = 0
+ truncate_track = 0
+
+ if track > 17:
+ offset = 17 * 21 * SECLEN
+ truncate_track = 17
+
+ if track > 24:
+ offset += 7 * 19 * SECLEN
+ truncate_track = 24
+
+ if track > 30:
+ offset += 6 * 18 * SECLEN
+ truncate_track = 30
+
+ track = track - truncate_track
+ offset += track * sector * SECLEN
+
+ return offset
+
+
+class D71(Disk):
+ """
+ Implement d71 directory reader
+ """
+
+ def _get_offset(self, track, sector):
+ """
+ Return offset (in bytes) for specified track and sector.
+
+ Track Sec/trk # Tracks
+ -------------- ------- ---------
+ 1-17 (side 0) 21 17
+ 18-24 (side 0) 19 7
+ 25-30 (side 0) 18 6
+ 31-35 (side 0) 17 5
+ 36-52 (side 1) 21 17
+ 53-59 (side 1) 19 7
+ 60-65 (side 1) 18 6
+ 66-70 (side 1) 17 5
+ """
+ offset = 0
+ truncate_track = 0
+
+ if track > 17:
+ offset = 17 * 21 * SECLEN
+ truncate_track = 17
+
+ if track > 24:
+ offset += 7 * 19 * SECLEN
+ truncate_track = 24
+
+ if track > 30:
+ offset += 6 * 18 * SECLEN
+ truncate_track = 30
+
+ if track > 35:
+ offset += 5 * 17 * SECLEN
+ truncate_track = 35
+
+ if track > 52:
+ offset = 17 * 21 * SECLEN
+ truncate_track = 17
+
+ if track > 59:
+ offset += 7 * 19 * SECLEN
+ truncate_track = 24
+
+ if track > 65:
+ offset += 6 * 18 * SECLEN
+ truncate_track = 30
+
+ track = track - truncate_track
+ offset += track * sector * SECLEN
+
+ return offset
+
+
+class D81(Disk):
+ """
+ Implement d81 directory reader
+ """
+ DIR_TRACK = 40
+ DIR_SECTOR = 3
+ FILE_TYPES = {0b000: 'del',
+ 0b001: 'seq',
+ 0b010: 'prg',
+ 0b011: 'usr',
+ 0b100: 'rel',
+ 0b101: 'cbm'}
+
+ def _get_offset(self, track, sector):
+ """
+ Return offset (in bytes) for specified track and sector. In d81 is
+ easy, since we have 80 tracks with 40 sectors for 256 bytes each.
+ """
+ # we wan to go to the beginning (first sector) of the track, not it's
+ # max, so that we need to extract its amount.
+ return (track * 40 - 40) * SECLEN + sector * SECLEN
+
+
+class Uc1541(object):
+ """
+ Class for interact with c1541 program and MC
+ """
+ PRG = re.compile(r'(\d+)\s+"([^"]*)".+?\s(del|prg|rel|seq|usr)([\s<])')
+
+ def __init__(self, archname):
+ self.arch = archname
+ self.out = ''
+ self.err = ''
+ self._verbose = os.getenv("UC1541_VERBOSE", False)
+ self._hide_del = os.getenv("UC1541_HIDE_DEL", False)
+
+ self.dirlist = _get_implementation(_get_raw(archname)).list_dir()
+ self.file_map = {}
+ self.directory = []
+
+ def list(self):
+ """
+ Output list contents of D64 image.
+ Convert filenames to be Unix filesystem friendly
+ Add suffix to show user what kind of file do he dealing with.
+ """
+ LOG.info("List contents of %s", self.arch)
+ directory = self._get_dir()
+
+ # If there is an error reading directory, show the reason to the user
+ if self.out.startswith("Error"):
+ sys.stderr.write(self.out.split("\n")[0] + "\n")
+ return 2
+
+ for entry in directory:
+ sys.stdout.write("%(perms)s 1 %(uid)-8d %(gid)-8d %(size)8d "
+ "Jan 01 1980 %(display_name)s\n" % entry)
+ return 0
+
+ def rm(self, dst):
+ """
+ Remove file from D64 image
+ """
+ LOG.info("Removing file %s", dst)
+ dst = self._get_masked_fname(dst)
+
+ if not self._call_command('delete', dst=dst):
+ return self._show_error()
+
+ return 0
+
+ def copyin(self, dst, src):
+ """
+ Copy file to the D64 image. Destination filename has to be corrected.
+ """
+ LOG.info("Copy into D64 %s as %s", src, dst)
+ dst = self._correct_fname(dst)
+
+ if not self._call_command('write', src=src, dst=dst):
+ return self._show_error()
+
+ return 0
+
+ def copyout(self, src, dst):
+ """
+ Copy file form the D64 image. Source filename has to be corrected,
+ since it's representation differ from the real one inside D64 image.
+ """
+ LOG.info("Copy form D64 %s as %s", src, dst)
+ if not src.endswith(".prg"):
+ return "cannot read"
+
+ src = self._get_masked_fname(src)
+
+ if not self._call_command('read', src=src, dst=dst):
+ return self._show_error()
+
+ return 0
+
+ def mkdir(self, dirname):
+ """Not supported"""
+ self.err = "D64 format doesn't support directories"
+ return self._show_error()
+
+ def run(self, fname):
+ """Not supported"""
+ self.err = "Not supported, unless you are using MC on real C64 ;)"
+ return self._show_error()
+
+ def _correct_fname(self, fname):
+ """
+ Return filename with mapped characters, without .prg extension.
+ Characters like $, *, + in filenames are perfectly legal, but c1541
+ program seem to have issues with it while writing, so it will also be
+ replaced.
+ """
+ char_map = {'|': "/",
+ "\\": "/",
+ "~": " ",
+ "$": "?",
+ "*": "?"}
+
+ if fname.lower().endswith(".prg"):
+ fname = fname[:-4]
+
+ new_fname = []
+ for char in fname:
+ trans = char_map.get(char)
+ new_fname.append(trans if trans else char)
+
+ return "".join(new_fname)
+
+ def _get_masked_fname(self, fname):
+ """
+ Return masked filename with '?' jokers instead of non ASCII
+ characters, useful for copying or deleting files with c1541. In case
+ of several files with same name exists in directory, only first one
+ will be operative (first as appeared in directory).
+
+ Warning! If there are two different names but the only difference is in
+ non-ASCII characters (some PET ASCII or control characters) there is
+ a risk that one can remove both files.
+ """
+ directory = self._get_dir()
+
+ for entry in directory:
+ if entry['display_name'] == fname:
+ return entry['pattern_name']
+
+ def _get_dir(self):
+ """
+ Retrieve directory via c1541 program
+ """
+ directory = []
+
+ uid = os.getuid()
+ gid = os.getgid()
+
+ if not self._call_command('list'):
+ return self._show_error()
+
+ idx = 0
+ for line in self.out.split("\n"):
+ if Uc1541.PRG.match(line):
+ blocks, fname, ext, rw = Uc1541.PRG.match(line).groups()
+
+ if ext == 'del' and self._hide_del:
+ continue
+
+ display_name = ".".join([fname, ext])
+ pattern_name = self.dirlist[idx]['fname']
+
+ if '/' in display_name:
+ display_name = display_name.replace('/', '|')
+
+ # workaround for space and dash at the beginning of the
+ # filename
+ char_map = {' ': '~',
+ '-': '_'}
+ display_name = "".join([char_map.get(display_name[0],
+ display_name[0]),
+ display_name[1:]])
+
+ if ext == 'del':
+ perms = "----------"
+ else:
+ perms = "-r%s-r--r--" % (rw.strip() and "-" or "w")
+
+ directory.append({'pattern_name': pattern_name,
+ 'display_name': display_name,
+ 'uid': uid,
+ 'gid': gid,
+ 'size': int(blocks) * SECLEN,
+ 'perms': perms})
+ idx += 1
+ return directory
+
+ def _show_error(self):
+ """
+ Pass out error output from c1541 execution
+ """
+ if self._verbose:
+ return self.err
+ else:
+ return 1
+
+ def _call_command(self, cmd, src=None, dst=None):
+ """
+ Return status of the provided command, which can be one of:
+ write
+ read
+ delete
+ dir/list
+ """
+ command = ['c1541', '-attach', self.arch, '-%s' % cmd]
+ if src:
+ command.append(src)
+ if dst:
+ command.append(dst)
+
+ LOG.debug('executing command: %s', ' '.join(command))
+ # For some reason using write and delete commands and reading output
+ # confuses Python3 beneath MC and as a consequence MC report an
+ # error...therefore for those commands let's not use
+ # universal_newlines...
+ universal_newlines = True
+ if cmd in ['delete', 'write']:
+ universal_newlines = False
+ self.out, self.err = Popen(command,
+ universal_newlines=universal_newlines,
+ stdout=PIPE, stderr=PIPE).communicate()
+
+ if self.err:
+ LOG.debug('an err: %s', self.err)
+ return not self.err
+
+
+CALL_MAP = {'list': lambda a: Uc1541(a.arch).list(),
+ 'copyin': lambda a: Uc1541(a.arch).copyin(a.src, a.dst),
+ 'copyout': lambda a: Uc1541(a.arch).copyout(a.src, a.dst),
+ 'mkdir': lambda a: Uc1541(a.arch).mkdir(a.dst),
+ 'rm': lambda a: Uc1541(a.arch).rm(a.dst),
+ 'run': lambda a: Uc1541(a.arch).run(a.dst)}
+
+
+def parse_args():
+ """Use ArgumentParser to check for script arguments and execute."""
+ parser = ArgumentParser()
+ subparsers = parser.add_subparsers(help='supported commands',
+ dest='subcommand')
+ subparsers.required = True
+ parser_list = subparsers.add_parser('list', help="List contents of D64 "
+ "image")
+ parser_copyin = subparsers.add_parser('copyin', help="Copy file into D64 "
+ "image")
+ parser_copyout = subparsers.add_parser('copyout', help="Copy file out of "
+ "D64 image")
+ parser_rm = subparsers.add_parser('rm', help="Delete file from D64 image")
+ parser_mkdir = subparsers.add_parser('mkdir', help="Create directory in "
+ "archive")
+ parser_run = subparsers.add_parser('run', help="Execute archived file")
+
+ parser_list.add_argument('arch', help="D64 Image filename")
+ parser_list.set_defaults(func=CALL_MAP['list'])
+
+ parser_copyin.add_argument('arch', help="D64 Image filename")
+ parser_copyin.add_argument('src', help="Source filename")
+ parser_copyin.add_argument('dst', help="Destination filename (to be "
+ "written into D64 image)")
+ parser_copyin.set_defaults(func=CALL_MAP['copyin'])
+
+ parser_copyout.add_argument('arch', help="D64 Image filename")
+ parser_copyout.add_argument('src', help="Source filename (to be read from"
+ " D64 image")
+ parser_copyout.add_argument('dst', help="Destination filename")
+ parser_copyout.set_defaults(func=CALL_MAP['copyout'])
+
+ parser_rm.add_argument('arch', help="D64 Image filename")
+ parser_rm.add_argument('dst', help="File inside D64 image to be deleted")
+ parser_rm.set_defaults(func=CALL_MAP['rm'])
+
+ parser_mkdir.add_argument('arch', help="archive filename")
+ parser_mkdir.add_argument('dst', help="Directory name inside archive to "
+ "be created")
+ parser_mkdir.set_defaults(func=CALL_MAP['mkdir'])
+
+ parser_run.add_argument('arch', help="archive filename")
+ parser_run.add_argument('dst', help="File to be executed")
+ parser_run.set_defaults(func=CALL_MAP['run'])
+
+ args = parser.parse_args()
+ return args.func(args)
+
+
+def no_parse():
+ """Failsafe argument "parsing". Note, that it blindly takes positional
+ arguments without checking them. In case of wrong arguments it will
+ silently exit"""
+ try:
+ if sys.argv[1] not in ('list', 'copyin', 'copyout', 'rm', 'mkdir',
+ "run"):
+ sys.exit(2)
+ except IndexError:
+ sys.exit(2)
+
+ class Arg(object):
+ """Mimic argparse object"""
+ dst = None
+ src = None
+ arch = None
+
+ arg = Arg()
+
+ try:
+ arg.arch = sys.argv[2]
+ if sys.argv[1] in ('copyin', 'copyout'):
+ arg.src = sys.argv[3]
+ arg.dst = sys.argv[4]
+ elif sys.argv[1] in ('rm', 'run', 'mkdir'):
+ arg.dst = sys.argv[3]
+ except IndexError:
+ sys.exit(2)
+
+ return CALL_MAP[sys.argv[1]](arg)
+
+
+if __name__ == "__main__":
+ LOG.debug("Script params: %s", str(sys.argv))
+ try:
+ from argparse import ArgumentParser
+ PARSE_FUNC = parse_args
+ except ImportError:
+ PARSE_FUNC = no_parse
+
+ sys.exit(PARSE_FUNC())
diff --git a/src/vfs/extfs/helpers/ucab.in b/src/vfs/extfs/helpers/ucab.in
new file mode 100644
index 0000000..252c8ca
--- /dev/null
+++ b/src/vfs/extfs/helpers/ucab.in
@@ -0,0 +1,40 @@
+#! /bin/sh
+
+CAB=cabextract
+
+mccabfs_list ()
+{
+ $CAB -l "$1" | @AWK@ -v uid=`id -un` -v gid=`id -gn` '
+BEGIN { flag=0 }
+/^-------/ { flag++; if (flag > 1) exit 0; next }
+{
+if (flag == 0) next
+if (length($6) == 0) next
+pr="-rw-r--r--"
+split($3, a, ".")
+split($4, b, ":")
+printf "%s 1 %s %s %d %02d/%02d/%02d %02d:%02d %s\n", pr, uid, gid, $1, a[2], a[1], a[3], b[1], b[2], $6
+}'
+
+}
+
+mccabfs_copyout ()
+{
+ $CAB -F "$2" -p "$1" > "$3"
+}
+
+LC_ALL=C
+export LC_ALL
+
+umask 077
+
+cmd="$1"
+
+case "$cmd" in
+ # Workaround for a bug in mc - directories must precede files to
+ # avoid duplicate entries, so we sort output by filenames
+ list) mccabfs_list "$2" ;;
+ copyout) mccabfs_copyout "$2" "$3" "$4" ;;
+ *) exit 1 ;;
+esac
+exit 0
diff --git a/src/vfs/extfs/helpers/uha.in b/src/vfs/extfs/helpers/uha.in
new file mode 100644
index 0000000..9dd0016
--- /dev/null
+++ b/src/vfs/extfs/helpers/uha.in
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# It is the uhafs Valery Kornienkov vlk@st.simbirsk.su 2:5051/30@fidonet
+# ver 0.1 Thu Apr 6 12:05:08 2000
+#
+# Tested with HA 0.999. Source of ha can be found at
+# ftp://ftp.ibiblio.org/pub/Linux/utils/compress/
+
+HA=ha
+
+mchafs_list ()
+{
+ $HA lf "$1" 2>/dev/null | @AWK@ -v uid=$(id -ru) '
+/^===========/ {next}
+{
+ if ($5="%" && $8~/DIR|ASC|HSC|CPY/) {
+ split($6, a, "-")
+ split($7, t, ":")
+ filename=$1
+ filesize=$2
+ getline
+ if ($2=="(none)") $2=""
+ path=$2
+ getline
+ if ($1~/^d.*/) next
+ printf "%s %s %-8d %-8d %8d %s-%s-%s %s:%s %s%s\n",\
+ $1,1,0,0,filesize,a[3],a[2],a[1],t[1],t[2],path,filename
+ }
+}'
+}
+
+mchafs_copyout ()
+{
+ TMPDIR=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-uha.XXXXXX"` || exit 1
+ cd "$TMPDIR"
+
+ $HA xyq "$1" "$2" >/dev/null
+ cat "$2" > "$3"
+
+ cd /
+ rm -rf "$TMPDIR"
+}
+
+cmd="$1"
+shift
+
+case "$cmd" in
+ list) mchafs_list "$@" ;;
+ copyout) mchafs_copyout "$@" ;;
+ *) exit 1 ;;
+esac
+exit 0
diff --git a/src/vfs/extfs/helpers/ulha.in b/src/vfs/extfs/helpers/ulha.in
new file mode 100644
index 0000000..0b5735c
--- /dev/null
+++ b/src/vfs/extfs/helpers/ulha.in
@@ -0,0 +1,142 @@
+#! /bin/sh
+
+#
+# LHa Virtual filesystem executive v0.1
+# Copyright (C) 1996, 1997 Joseph M. Hinkle
+# May be distributed under the terms of the GNU Public License
+# <jhinkle@rockisland.com>
+#
+
+# Code for mc_lha_fs_run() suggested by:
+# Jan 97 Zdenek Kabelac <kabi@informatics.muni.cz>
+
+# Tested with mc 3.5.18 and gawk 3.0.0 on Linux 2.0.0
+# Tested with lha v1.01 and lharc v1.02
+# Information and sources for other forms of lha/lzh appreciated
+
+# Nota bene:
+# There are several compression utilities which produce *.lha files.
+# LHArc and LHa in exist several versions, and their listing output varies.
+# Another variable is the architecture on which the compressed file was made.
+# This program attempts to sort out the variables known to me, but it is likely
+# to display an empty panel if it encounters a mystery.
+# In that case it will be useful to execute this file from the command line:
+# ./lha list Mystery.lha
+# to examine the output directly on the console. The output string must be
+# precisely in the format described in the README in this directory.
+# Caveat emptor.
+# Learn Latin.
+
+# Define your awk
+AWK=@AWK@
+
+# Define which archiver you are using with appropriate options
+LHA_LIST="lha lq"
+LHA_GET="lha pq"
+LHA_PUT="lha aq"
+
+# The 'list' command executive
+
+mc_lha_fs_list()
+{
+ # List the contents of the archive and sort it out
+ $LHA_LIST "$1" | $AWK -v uid=`id -nu` -v gid=`id -ng` '
+ # Strip a leading '/' if present in a filepath
+ $(NF) ~ /^\// { $(NF) = substr($NF,2) }
+ # Print the line this way if there is no permission string
+ $1 ~ /^\[.*\]/ {
+ # Invent a generic permission
+ $1 = ($NF ~ /\/$/) ? "drwxr-xr-x":"-rwxr--r--";
+ # Print it
+ printf "%s 1 %-8s %-8s %-8d %s %s %s %s\n",
+ $1, uid, gid, $2, $4, $5, $6, $7;
+ # Get the next line of the list
+ next;
+ }
+ # Do it this way for a defined permission
+ $1 !~ /^\[.*\]/ {
+ # If the permissions and UID run together
+ if ($1 ~ /\//) {
+ $8 = $7;
+ $7 = $6;
+ $6 = $5;
+ $5 = $4;
+ $3 = $2;
+ $2 = substr($1,10);
+ $1 = substr($1,1,9);
+ }
+ # If the permission string is missing a type
+ if (length($1) == 9) {
+ if ($NF ~ /\/$/)
+ $1 = ("d" $1);
+ else
+ $1 = ("-" $1);
+ }
+ # UID:GID might not be the same as on your system so print numbers
+ # Well, that is the intent. At the moment mc is translating them.
+ split($2, id, "/");
+ printf "%s 1 %-8d %-8d %-8d %s %s %s %s\n",
+ $1, id[1], id[2], $3, $5, $6, $7, $8;
+ # Get the next line of the list
+ next;
+ }
+
+ '
+}
+
+# The 'copyout' command executive to copy displayed files to a destination
+
+mc_lha_fs_copyout()
+{
+ $LHA_GET "$1" "$2" > "$3"
+}
+
+# The 'copyin' command executive to add something to the archive
+
+mc_lha_fs_copyin ()
+{
+ NAME2=`basename "$2"`; DIR2=${2%$NAME2}
+ NAME3=`basename "$3"`; DIR3=${3%$NAME3}
+
+ cd "${DIR3}"
+
+ ONE2=${2%%/*}
+ [ -n "${ONE2}" ] || exit 1
+ [ -e "${ONE2}" ] && exit 1
+
+ [ -e "${DIR2}" ] || mkdir -p "${DIR2}"
+ ln "$3" "$2" || exit 1
+
+ $LHA_PUT "$1" "$2"
+ rm -r "${ONE2}"
+}
+
+# The 'run' command executive to run a command from within an archive
+
+mc_lha_fs_run()
+{
+ TMPDIR=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-ulha.XXXXXX"` || exit 1
+ trap "rm -rf \"$TMPDIR\"; exit 0" 1 2 3 4 15
+ TMPCMD=$TMPDIR/run
+ $LHA_GET "$1" "$2" > $TMPCMD
+ chmod a+x "$TMPCMD"
+ "$TMPCMD"
+ rm -rf "$TMPDIR"
+}
+
+
+# The main routine
+umask 077
+
+cmd="$1"
+shift
+
+case "$cmd" in
+ list) mc_lha_fs_list "$@" ;;
+ copyout) mc_lha_fs_copyout "$@" ;;
+ copyin) mc_lha_fs_copyin "$@" ;;
+ run) mc_lha_fs_run "$@" ;;
+ *) exit 1 ;;
+esac
+
+exit 0
diff --git a/src/vfs/extfs/helpers/ulib.in b/src/vfs/extfs/helpers/ulib.in
new file mode 100644
index 0000000..82c7ccf
--- /dev/null
+++ b/src/vfs/extfs/helpers/ulib.in
@@ -0,0 +1,146 @@
+#! @PERL@
+#
+# VFS to manage the gputils archives.
+# Written by Molnár Károly (proton7@freemail.hu) 2012
+#
+
+use warnings;
+
+my %month = ('jan' => '01', 'feb' => '02', 'mar' => '03',
+ 'apr' => '04', 'may' => '05', 'jun' => '06',
+ 'jul' => '07', 'aug' => '08', 'sep' => '09',
+ 'oct' => '10', 'nov' => '11', 'dec' => '12');
+
+my @PATHS = ('/usr/bin/gplib', '/usr/local/bin/gplib');
+
+my $gplib = '';
+
+foreach my $i (@PATHS)
+{
+ if (-x $i)
+ {
+ $gplib = $i;
+ last;
+ }
+}
+
+if ($gplib eq '')
+{
+ print STDERR "\a\t$0 : Gplib not found!\n";
+ exit(1);
+}
+
+my $cmd = shift;
+my $archive = shift;
+
+#-------------------------------------------------------------------------------
+
+sub mc_ulib_fs_list
+{
+ open(PIPE, "$gplib -tq $archive |") || die("Error in $gplib -tq");
+
+ my($dev, $inode, $mode, $nlink, $uid, $gid) = stat($archive);
+
+ while (<PIPE>)
+ {
+ chomp;
+ my @w = split(/\s+/o);
+ my $fname = $w[0];
+
+ $fname =~ s|\\|/|g;
+
+ printf("-rw-r--r-- 1 %s %s %d %s-%02u-%s %s %s\n",
+ $uid, $gid, int($w[1]), $month{lc($w[4])}, $w[5], $w[7], substr($w[6], 0, 5), $fname);
+ }
+ close (PIPE);
+}
+
+#-------------------------------------------------------------------------------
+
+sub mc_ulib_fs_copyin
+{
+ system("$gplib -r $archive $_[0]");
+ my $ret = $?;
+
+ if ($ret)
+ {
+ die("Error in: $gplib -r");
+ }
+}
+
+#-------------------------------------------------------------------------------
+
+sub mc_ulib_fs_copyout
+{
+ my($module, $fname) = @_;
+ my $tmpdir = $ENV{'TMPDIR'};
+
+ $tmpdir = '/tmp' if (! defined $tmpdir or $tmpdir eq '');
+
+ open(PIPE, "$gplib -tq $archive |") || die("Error in: $gplib -tq");
+
+ while (<PIPE>)
+ {
+ chomp;
+ my @w = split(/\s+/o);
+ my $module_orig = $w[0];
+ my $count = () = ($module_orig =~ /(\\)/g);
+ my $md = $module_orig;
+
+ $md =~ s|\\|/|g;
+
+ if ($module eq $md)
+ {
+ return if ($count);
+ }
+ }
+
+ close (PIPE);
+
+ chdir($tmpdir);
+ system("$gplib -x $archive $module");
+ my $ret = $?;
+
+ if ($ret)
+ {
+ die("Error in: $gplib -x");
+ }
+
+ rename($module, $fname) || die("Error in: rename($module, $fname)");
+}
+
+#-------------------------------------------------------------------------------
+
+sub mc_ulib_fs_rm
+{
+ system("$gplib -d $archive $_[0]");
+ my $ret = $?;
+
+ if ($ret)
+ {
+ die("Error in: $gplib -d");
+ }
+}
+
+################################################################################
+
+if ($cmd eq 'list')
+{
+ mc_ulib_fs_list(@ARGV);
+}
+elsif ($cmd eq 'copyin')
+{
+ mc_ulib_fs_copyin(@ARGV);
+}
+elsif ($cmd eq 'copyout')
+{
+ mc_ulib_fs_copyout(@ARGV);
+}
+elsif ($cmd eq 'rm')
+{
+ mc_ulib_fs_rm(@ARGV);
+}
+else
+{
+ exit(1);
+}
diff --git a/src/vfs/extfs/helpers/unar.in b/src/vfs/extfs/helpers/unar.in
new file mode 100644
index 0000000..e810307
--- /dev/null
+++ b/src/vfs/extfs/helpers/unar.in
@@ -0,0 +1,59 @@
+#! /bin/sh
+
+# Written by Ilia Maslakov <il.smind@gmail.com>
+#
+# (C) 2020 The Free Software Foundation.
+
+# Define awk
+AWK=@AWK@
+
+# Define which archiver you are using with appropriate options
+UNAR_LIST="lsar "
+UNAR_GET="unar "
+
+# The 'list' command executive
+mc_unar_fs_list()
+{
+ # List the contents of the archive and sort it out
+ $UNAR_LIST -l "$1" | $AWK -v uid=`id -nu` -v gid=`id -ng` '
+ BEGIN { flag = 0 }
+ /^\(Flags/ {next}
+ /^\(Mode/ {next}
+ {
+ flag++;
+ if (flag < 4)
+ next
+ pr="-r--r--r--"
+ if (index($2, "D") != 0)
+ pr="dr-xr-xr-x"
+ split($6, a, "-")
+ split($7, b, ":")
+ printf "%s 1 %s %s %d %02d/%02d/%02d %02d:%02d %s\n", pr, uid, gid, $3, a[3], a[2], a[1], b[1], b[2], $8
+ }'
+}
+
+# The 'copyout' command executive to copy displayed files to a destination
+mc_unar_fs_copyout ()
+{
+ TMPDIR=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-uha.XXXXXX"` || exit 1
+
+ $UNAR_GET "$1" "$2" -o "$TMPDIR" >/dev/null
+ we=`basename "$1" | sed -E 's|^(.*?)\.\w+$|\1|'`
+ cat "$TMPDIR/$we/$2" > "$3"
+ cd /
+ rm -rf "$TMPDIR"
+}
+
+# The main routine
+umask 077
+
+cmd="$1"
+shift
+
+case "$cmd" in
+ list) mc_unar_fs_list "$@" ;;
+ copyout) mc_unar_fs_copyout "$@" ;;
+ *) exit 1 ;;
+esac
+
+exit 0
diff --git a/src/vfs/extfs/helpers/urar.in b/src/vfs/extfs/helpers/urar.in
new file mode 100644
index 0000000..684234c
--- /dev/null
+++ b/src/vfs/extfs/helpers/urar.in
@@ -0,0 +1,180 @@
+#! /bin/sh
+#
+# Written by andrey joukov
+# (C) 1996 2:5020/337.13@fidonet.org
+# Updated by christian.gennerat@alcatel.fr 1999
+# Andrew V. Samoilov <sav@bcs.zp.ua> 2000
+#
+# Andrew Borodin <aborodin@vmail.ru>
+# David Haller <dnh@opensuse.org>
+# 2013: support unrar5
+#
+# beta version 2.0
+#
+# rar and unrar can be found on http://www.rarlabs.com/
+
+RAR=rar
+
+# Prefer unrar (freeware).
+UNRAR=`which unrar 2>/dev/null`
+
+[ -z $UNRAR ] && UNRAR=$RAR
+[ ! -x $UNRAR -a -x $RAR ] && UNRAR=$RAR
+
+# Let the test framework hook in:
+UNRAR=${MC_TEST_EXTFS_LIST_CMD:-$UNRAR}
+
+# Determine the $UNRAR version
+if [ -n "$MC_TEST_EXTFS_UNRAR_VERSION" ]; then
+ # Let the test framework fool us:
+ UNRAR_VERSION=$MC_TEST_EXTFS_UNRAR_VERSION
+else
+ # Figure it out from rar itself:
+ UNRAR_VERSION=`$UNRAR -cfg- -? | grep "Copyright" | sed -e 's/.*\([0-9]\)\..*/\1/'`
+fi
+
+mcrar4fs_list ()
+{
+ $UNRAR v -c- -cfg- "$1" | @AWK@ -v uid=`id -u` -v gid=`id -g` '
+BEGIN { flag=0 }
+/^-------/ { flag++; if (flag > 1) exit 0; next }
+flag==1 {
+ str = substr($0, 2)
+ getline
+ split($4, a, "-")
+ if (index($6, "D") != 0)
+ $6="drwxr-xr-x"
+ else
+ if (index($6, ".") != 0)
+ $6="-rw-r--r--"
+ printf "%s 1 %s %s %d %02d/%02d/%02d %s ./%s\n", $6, uid, gid, $1, a[2], a[1], a[3], $5, str
+}'
+}
+
+mcrar5fs_list ()
+{
+ $UNRAR vt -c- -cfg- "$1" | @AWK@ -F ':' -v uid=`id -u` -v gid=`id -g` '
+ {
+ ### remove space after the ":" of the field name
+ sub ("^ ", "", $2);
+ }
+
+ $1 ~ /^ *Name$/ {
+ ### next file
+ name = mtime = size = attrs = "";
+ delete date;
+ name = $2;
+ ### if the name contains ":", append the rest of the fields
+ if (NF > 2) {
+ for (i = 3; i <= NF; i++) {
+ name = name ":" $i;
+ }
+ }
+ }
+ $1 ~ /^ *mtime$/ {
+ mtime = $2 ":" $3;
+ }
+ $1 ~ /^ *Size$/ {
+ size = $2;
+ }
+ $1 ~ /^ *Attributes$/ {
+ attrs = $2;
+ }
+
+ $1 ~ /^ *Compression$/ {
+ ### file done, using /^$/ is not so good you
+ ### would have to skip the version stuff first
+
+ ### get date and time
+ split (mtime, date, " ");
+ time = date[2];
+ ### cut off seconds from the time
+ sub (",[0-9]*$", "", time);
+ ### split for reordering of the date in the printf below
+ split (date[1], date, "-");
+ ### mc seems to be able to parse 4 digit years too, so remove if tested
+ # sub ("^..", "", date[1]); ### cut year to 2 digits only
+
+ ### check/adjust rights
+ if (index (attrs, "D") != 0) {
+ attrs = "drwxr-xr-x";
+ } else {
+ if (index (attrs, ".") != 0) {
+ attrs = "-rw-r--r--";
+ }
+ }
+
+ ### and finally
+ printf ("%s 1 %s %s %d %02d/%02d/%02d %s ./%s\n",
+ attrs, uid, gid, size, date[2], date[3], date[1], time, name);
+ }
+'
+}
+
+mcrarfs_list ()
+{
+ [ x$UNRAR_VERSION = x6 -o x$UNRAR_VERSION = x5 ] && mcrar5fs_list "$@" || mcrar4fs_list "$@"
+}
+
+mcrarfs_copyin ()
+{
+# copyin by christian.gennerat@alcatel.fr
+# preserve pwd. It is clean, but is it necessary?
+ pwd=`pwd`
+# Create a directory and copy in it the tmp file with the good name
+ mkdir "$3.dir"
+ cd "$3.dir"
+ di="${2%/*}"
+# if file is to be written upper in the archive tree, make fake dir
+ if test x"$di" != x"${2##*/}" ; then
+ mkdir -p "$di"
+ fi
+ cp -fp "$3" "$3.dir/$2"
+ $RAR a "$1" "$2" >/dev/null
+ cd "$pwd"
+ rm -rf "$3.dir"
+}
+
+mcrarfs_copyout ()
+{
+ $UNRAR p -p- -c- -cfg- -inul "$1" "$2" > "$3"
+}
+
+mcrarfs_mkdir ()
+{
+# preserve pwd. It is clean, but is it necessary?
+ pwd=`pwd`
+# Create a directory and create in it a tmp directory with the good name
+ dir=`mktemp -d "${MC_TMPDIR:-/tmp}/mctmpdir-urar.XXXXXX"` || exit 1
+ cd "$dir"
+ mkdir -p "$2"
+# rar cannot create an empty directory
+ touch "$2"/.rarfs
+ $RAR a -r "$1" "$2" >/dev/null
+ $RAR d "$1" "$2/.rarfs" >/dev/null
+ cd "$pwd"
+ rm -rf "$dir"
+}
+
+mcrarfs_rm ()
+{
+ $RAR d "$1" "$2" >/dev/null
+}
+
+umask 077
+
+cmd="$1"
+shift
+
+case "$cmd" in
+ # Workaround for a bug in mc - directories must precede files to
+ # avoid duplicate entries, so we sort output by filenames
+ list) mcrarfs_list "$@" | sort -k 8 ;;
+ rm) mcrarfs_rm "$@" ;;
+ rmdir) mcrarfs_rm "$@" ;;
+ mkdir) mcrarfs_mkdir "$@" ;;
+ copyin) mcrarfs_copyin "$@" ;;
+ copyout) mcrarfs_copyout "$@" ;;
+ *) exit 1 ;;
+esac
+exit 0
diff --git a/src/vfs/extfs/helpers/uwim.in b/src/vfs/extfs/helpers/uwim.in
new file mode 100644
index 0000000..76197f7
--- /dev/null
+++ b/src/vfs/extfs/helpers/uwim.in
@@ -0,0 +1,208 @@
+#! /bin/sh
+# Midnight Commander - WIM support
+#
+# Written by:
+# Vadim Kalinnikov <moose@ylsoftware.com>,
+#
+# This file is part of the Midnight Commander.
+#
+# It required wimtools: https://wimlib.net/
+# On Debian/Ubuntu wimtools can be installed via:
+# apt install wimtools
+
+which wimlib-imagex 2>/dev/null > /dev/null || exit 1
+WIM=`which wimlib-imagex`
+AWK=@AWK@
+
+[ -n "$2" ] || exit 1
+
+ACTION=$1
+WIMFILENAME=$2
+
+mcwim_list() {
+ # Here we can use "Image count" from output,
+ # but on some broken images we can get garbage, instead of number
+ IMAGECOUNT=`${WIM} info ${WIMFILENAME} | grep Index: | grep -v Boot | wc -l`
+ IMGNUM=1
+ VUID=`id -nu`
+ VGID=`id -ng`
+ while [ ${IMGNUM} -le ${IMAGECOUNT} ]; do
+ ${WIM} dir ${WIMFILENAME} ${IMGNUM} --detailed | \
+ ${AWK} -v uid=${VUID} -v gid=${VGID} -v imgnum=${IMGNUM} '
+ /^----------------------------------------------------------------------------/,/^$/ {
+ if (match($0, /^Full Path/)) {
+ split($0, namesrc, "\"");
+ name=namesrc[2];
+ }
+ if (match($0, /FILE_ATTRIBUTE_DIRECTORY is set/)) {
+ attr="drwxr-xr-x"
+ }
+ if (match($0, /^Uncompressed size/)) {
+ size=$4;
+ }
+ if (match($0, /^Last Write Time/)) {
+ months["Jan"] = "01";
+ months["Feb"] = "02";
+ months["Mar"] = "03";
+ months["Apr"] = "04";
+ months["May"] = "05";
+ months["Jun"] = "06";
+ months["Jul"] = "07";
+ months["Aug"] = "08";
+ months["Sep"] = "09";
+ months["Oct"] = "10";
+ months["Nov"] = "11";
+ months["Dec"] = "12";
+ split($0, mtimesrc, " ");
+ mtime=sprintf("%s/%s/%s %s", months[mtimesrc[6]], mtimesrc[7], mtimesrc[9], mtimesrc[8]);
+ }
+
+ if (match($0, /^$/)) {
+ printf("%s 1 %s %s % 20s % 24s IMAGE%s%s\n",
+ attr, uid, gid, size, mtime, imgnum, name);
+ name = size = mtime = "";
+ attr="-rw-r--r--";
+ }
+ }
+ '
+
+ IMGNUM=$((IMGNUM+1))
+ done
+
+ # Virtual files
+ echo "-r-xr-xr-x 1 ${VUID} ${VGID} 1 01/01/2020 00:00:00 OPTIMIZE"
+ echo "-r-xr-xr-x 1 ${VUID} ${VGID} 1 01/01/2020 00:00:00 VERIFY"
+}
+
+mcwim_copyout() {
+ # Virtual files
+ if [ "${FILENAMESRC}" = "OPTIMIZE" ]; then
+ echo "#/bin/sh" > ${FILENAMEDST}
+ echo "# Run this to optimize archive" >> ${FILENAMEDST}
+ echo "${WIM} optimize \"${WIMFILENAME}\"" >> ${FILENAMEDST}
+ chmod a+x ${FILENAMEDST}
+ exit 0;
+ elif [ "${FILENAMESRC}" = "VERIFY" ]; then
+ echo "#/bin/sh" > ${FILENAMEDST}
+ echo "# Run this to verify archive" >> ${FILENAMEDST}
+ echo "${WIM} verify \"${WIMFILENAME}\"" >> ${FILENAMEDST}
+ chmod a+x ${FILENAMEDST}
+ exit 0;
+ fi
+
+ # Filename must contain imgnum
+ echo ${FILENAMESRC} | grep -E '^IMAGE[0-9]+/' || exit 1
+
+ IMGNUM=`echo ${FILENAMESRC} | cut -d '/' -f1 | sed "s/IMAGE//"`
+ REALFILENAME=`echo ${FILENAMESRC} | sed "s/IMAGE${IMGNUM}//"`
+ ${WIM} extract ${WIMFILENAME} ${IMGNUM} ${REALFILENAME} --to-stdout > ${FILENAMEDST}
+}
+
+mcwim_copyin() {
+ # Skip virtual files
+ [ "${FILENAMEDST}" = "OPTIMIZE" ] && exit 1;
+ [ "${FILENAMEDST}" = "VERIFY" ] && exit 1;
+
+ # Filename must contain imgnum
+ echo ${FILENAMEDST} | grep -E '^IMAGE[0-9]+/' || exit 1
+
+ IMGNUM=`echo ${FILENAMEDST} | cut -d '/' -f1 | sed "s/IMAGE//"`
+ REALFILENAME=`echo ${FILENAMEDST} | sed "s/IMAGE${IMGNUM}//"`
+ echo "add \"${FILENAMESRC}\" \"${REALFILENAME}\"" | ${WIM} update ${WIMFILENAME} ${IMGNUM} > /dev/null
+}
+
+
+mcwim_rm() {
+ # Skip virtual files
+ [ "${FILENAMESRC}" = "OPTIMIZE" ] && exit 0;
+ [ "${FILENAMESRC}" = "VERIFY" ] && exit 0;
+
+ # Filename must contain imgnum
+ echo ${FILENAMESRC} | grep -E '^IMAGE[0-9]+/' || exit 1
+
+ IMGNUM=`echo ${FILENAMESRC} | cut -d '/' -f1 | sed "s/IMAGE//"`
+ REALFILENAME=`echo ${FILENAMESRC} | sed "s/IMAGE${IMGNUM}//"`
+
+ if [ -z "${REALFILENAME}" ]; then
+ # If user want to remove image
+ ${WIM} delete ${WIMFILENAME} ${IMGNUM}
+ else
+ # remove regular file or directory
+ echo "delete \"${REALFILENAME}\"" | ${WIM} update ${WIMFILENAME} ${IMGNUM} --force --recursive > /dev/null
+ fi
+}
+
+mcwim_run() {
+ case ${RUNFILENAME} in
+ OPTIMIZE)
+ ${WIM} optimize ${WIMFILENAME}
+ exit 0;
+ ;;
+ VERIFY)
+ ${WIM} verify ${WIMFILENAME}
+ exit 0;
+ ;;
+ esac
+ exit 1;
+}
+
+
+mcwim_mkdir() {
+ # New dirname must contain imgnum
+ echo ${NEWDIRNAME} | grep -E '^IMAGE[0-9]+/' || exit 1
+ IMGNUM=`echo ${NEWDIRNAME} | cut -d '/' -f1 | sed "s/IMAGE//"`
+ REALDIRNAME=`echo ${NEWDIRNAME} | sed "s/IMAGE${IMGNUM}//"`
+ [ -z "${REALDIRNAME}" ] && exit 1
+
+ TMPDIR=`mktemp -d`
+ DSTBASENAME=`basename ${REALDIRNAME}`
+ SRCDIRNAME="${TMPDIR}/${DSTBASENAME}"
+ mkdir -p ${SRCDIRNAME}
+ echo "add \"${SRCDIRNAME}\" \"${REALDIRNAME}\"" | ${WIM} update ${WIMFILENAME} ${IMGNUM} > /dev/null
+ rm -rf ${TMPDIR}
+}
+
+#echo "'$1' '$2' '$3' '$4' '$5'" >> /tmp/mcdebug
+
+case "$ACTION" in
+ list)
+ mcwim_list
+ ;;
+
+ copyout)
+ [ -n "$4" ] || exit 1
+ FILENAMESRC="$3"
+ FILENAMEDST="$4"
+ mcwim_copyout
+ ;;
+
+ copyin)
+ [ -n "$4" ] || exit 1
+ FILENAMEDST="$3"
+ FILENAMESRC="$4"
+ mcwim_copyin
+ ;;
+
+ rm|rmdir)
+ [ -n "$3" ] || exit 1
+ FILENAMESRC="$3"
+ mcwim_rm
+ ;;
+
+ run)
+ [ -n "$3" ] || exit 1
+ RUNFILENAME="$3"
+ mcwim_run
+ ;;
+
+ mkdir)
+ [ -n "$3" ] || exit 1
+ NEWDIRNAME="$3"
+ mcwim_mkdir
+ ;;
+
+
+ *)
+ exit 1
+ ;;
+esac
diff --git a/src/vfs/extfs/helpers/uzip.in b/src/vfs/extfs/helpers/uzip.in
new file mode 100644
index 0000000..ceffb53
--- /dev/null
+++ b/src/vfs/extfs/helpers/uzip.in
@@ -0,0 +1,483 @@
+#! @PERL@
+#
+# zip file archive Virtual File System for Midnight Commander
+# Version 1.4.0 (2001-08-07).
+#
+# (C) 2000-2001 Oskar Liljeblad <osk@hem.passagen.se>.
+#
+
+use POSIX;
+use File::Basename;
+use strict;
+use warnings;
+
+#
+# Configuration options
+#
+
+# Location of the zip program
+my $app_zip = "@ZIP@";
+# Location of the unzip program
+my $app_unzip = $ENV{MC_TEST_EXTFS_LIST_CMD} || "@UNZIP@";
+# Set this to 1 if zipinfo (unzip -Z) is to be used (recommended), otherwise 0.
+my $op_has_zipinfo = exists($ENV{MC_TEST_EXTFS_HAVE_ZIPINFO}) ? $ENV{MC_TEST_EXTFS_HAVE_ZIPINFO} : @HAVE_ZIPINFO@;
+
+# Command used to list archives (zipinfo mode)
+my $cmd_list_zi = "$app_unzip -Z -l -T";
+# Command used to list archives (non-zipinfo mode)
+my $cmd_list_nzi = "$app_unzip -qq -v";
+# Command used to add a file to the archive
+my $cmd_add = "$app_zip -g";
+# Command used to add a link file to the archive (unused)
+my $cmd_addlink = "$app_zip -g -y";
+# Command used to delete a file from the archive
+my $cmd_delete = "$app_zip -d";
+# Command used to extract a file to standard out
+my $cmd_extract = "$app_unzip -p";
+
+# -rw-r--r-- 2.2 unx 2891 tx 1435 defN 20000330.211927 ./edit.html
+# (perm) (?) (?) (size) (?) (zippedsize) (method) (yyyy)(mm)(dd).(HH)(MM)(SS) (fname)
+my $regex_zipinfo_line = qr"^(\S{7,10})\s+(\d+\.\d+)\s+(\S+)\s+(\d+)\s+(\S\S)\s+(\d+)\s+(\S{4})\s+(\d{4})(\d\d)(\d\d)\.(\d\d)(\d\d)(\d\d)\s(.*)$";
+
+# 2891 Defl:N 1435 50% 03-30-00 21:19 50cbaaf8 ./edit.html
+# (size) (method) (zippedsize) (zipratio) (mm)-(dd)-(yy|yyyy) (HH):(MM) (cksum) (fname)
+# or: (yyyy)-(mm)-(dd)
+my $regex_nonzipinfo_line = qr"^\s*(\d+)\s+(\S+)\s+(\d+)\s+(-?\d+\%)\s+(\d+)-(\d?\d)-(\d+)\s+(\d?\d):(\d\d)\s+([0-9a-f]+)\s\s(.*)$";
+
+#
+# Main code
+#
+
+die "uzip: missing command and/or archive arguments\n" if ($#ARGV < 1);
+
+# Initialization of some global variables
+my $cmd = shift;
+my %known = ( './' => 1 );
+my %pending = ();
+my $oldpwd = POSIX::getcwd();
+my $archive = shift;
+my $aarchive = absolutize($archive, $oldpwd);
+my $cmd_list = ($op_has_zipinfo ? $cmd_list_zi : $cmd_list_nzi);
+my ($qarchive, $aqarchive) = map (quotemeta, $archive, $aarchive);
+
+# Strip all "." and ".." path components from a pathname.
+sub zipfs_canonicalize_pathname($) {
+ my ($fname) = @_;
+ $fname =~ s,/+,/,g;
+ $fname =~ s,(^|/)(?:\.?\./)+,$1,;
+ return $fname;
+}
+
+# The Midnight Commander never calls this script with archive pathnames
+# starting with either "./" or "../". Some ZIP files contain such names,
+# so we need to build a translation table for them.
+my $zipfs_realpathname_table = undef;
+sub zipfs_realpathname($) {
+ my ($fname) = @_;
+
+ if (!defined($zipfs_realpathname_table)) {
+ $zipfs_realpathname_table = {};
+ if (!open(ZIP, "$cmd_list $qarchive |")) {
+ return $fname;
+ }
+ foreach my $line (<ZIP>) {
+ $line =~ s/\r*\n*$//;
+ if ($op_has_zipinfo) {
+ if ($line =~ $regex_zipinfo_line) {
+ my ($fname) = ($14);
+ $zipfs_realpathname_table->{zipfs_canonicalize_pathname($fname)} = $fname;
+ }
+ } else {
+ if ($line =~ $regex_nonzipinfo_line) {
+ my ($fname) = ($11);
+ $zipfs_realpathname_table->{zipfs_canonicalize_pathname($fname)} = $fname;
+ }
+ }
+ }
+ if (!close(ZIP)) {
+ return $fname;
+ }
+ }
+ if (exists($zipfs_realpathname_table->{$fname})) {
+ return $zipfs_realpathname_table->{$fname};
+ }
+ return $fname;
+}
+
+if ($cmd eq 'list') { &mczipfs_list(@ARGV); }
+if ($cmd eq 'rm') { &mczipfs_rm(@ARGV); }
+if ($cmd eq 'rmdir') { &mczipfs_rmdir(@ARGV); }
+if ($cmd eq 'mkdir') { &mczipfs_mkdir(@ARGV); }
+if ($cmd eq 'copyin') { &mczipfs_copyin(@ARGV); }
+if ($cmd eq 'copyout') { &mczipfs_copyout(@ARGV); }
+if ($cmd eq 'run') { &mczipfs_run(@ARGV); }
+#if ($cmd eq 'mklink') { &mczipfs_mklink(@ARGV); } # Not supported by MC extfs
+#if ($cmd eq 'linkout') { &mczipfs_linkout(@ARGV); } # Not supported by MC extfs
+exit 1;
+
+# Remove a file from the archive.
+sub mczipfs_rm {
+ my ($qfile) = map { &zipquotemeta(zipfs_realpathname($_)) } @_;
+
+ # "./" at the beginning of pathnames is stripped by Info-ZIP,
+ # so convert it to "[.]/" to prevent stripping.
+ $qfile =~ s/^\\\./[.]/;
+
+ &checkargs(1, 'archive file', @_);
+ &safesystem("$cmd_delete $qarchive $qfile >/dev/null");
+ exit;
+}
+
+# Remove an empty directory from the archive.
+# The only difference from mczipfs_rm is that we append an
+# additional slash to the directory name to remove. I am not
+# sure this is absolutely necessary, but it doesn't hurt.
+sub mczipfs_rmdir {
+ my ($qfile) = map { &zipquotemeta(zipfs_realpathname($_)) } @_;
+ &checkargs(1, 'archive directory', @_);
+ &safesystem("$cmd_delete $qarchive $qfile/ >/dev/null", 12);
+ exit;
+}
+
+# Extract a file from the archive.
+# Note that we don't need to check if the file is a link,
+# because mc apparently doesn't call copyout for symbolic links.
+sub mczipfs_copyout {
+ my ($qafile, $qfsfile) = map { &zipquotemeta(zipfs_realpathname($_)) } @_;
+ &checkargs(1, 'archive file', @_);
+ &checkargs(2, 'local file', @_);
+ &safesystem("$cmd_extract $qarchive $qafile > $qfsfile", 11);
+ exit;
+}
+
+# Add a file to the archive.
+# This is done by making a temporary directory, in which
+# we create a symlink the original file (with a new name).
+# Zip is then run to include the real file in the archive,
+# with the name of the symbolic link.
+# Here we also doesn't need to check for symbolic links,
+# because the mc extfs doesn't allow adding of symbolic
+# links.
+sub mczipfs_copyin {
+ my ($afile, $fsfile) = @_;
+ &checkargs(1, 'archive file', @_);
+ &checkargs(2, 'local file', @_);
+ my ($qafile) = quotemeta $afile;
+ $fsfile = &absolutize($fsfile, $oldpwd);
+ my $adir = File::Basename::dirname($afile);
+
+ my $tmpdir = &mktmpdir();
+ chdir $tmpdir || &croak("chdir $tmpdir failed");
+ &mkdirs($adir, 0700);
+ symlink ($fsfile, $afile) || &croak("link $afile failed");
+ &safesystem("$cmd_add $aqarchive $qafile >/dev/null");
+ unlink $afile || &croak("unlink $afile failed");
+ &rmdirs($adir);
+ chdir $oldpwd || &croak("chdir $oldpwd failed");
+ rmdir $tmpdir || &croak("rmdir $tmpdir failed");
+ exit;
+}
+
+# Add an empty directory the the archive.
+# This is similar to mczipfs_copyin, except that we don't need
+# to use symlinks.
+sub mczipfs_mkdir {
+ my ($dir) = @_;
+ &checkargs(1, 'directory', @_);
+ my ($qdir) = quotemeta $dir;
+
+ my $tmpdir = &mktmpdir();
+ chdir $tmpdir || &croak("chdir $tmpdir failed");
+ &mkdirs($dir, 0700);
+ &safesystem("$cmd_add $aqarchive $qdir >/dev/null");
+ &rmdirs($dir);
+ chdir $oldpwd || &croak("chdir $oldpwd failed");
+ rmdir $tmpdir || &croak("rmdir $tmpdir failed");
+ exit;
+}
+
+# Add a link to the archive. This operation is not used yet,
+# because it is not supported by the MC extfs.
+sub mczipfs_mklink {
+ my ($linkdest, $afile) = @_;
+ &checkargs(1, 'link destination', @_);
+ &checkargs(2, 'archive file', @_);
+ my ($qafile) = quotemeta $afile;
+ my $adir = File::Basename::dirname($afile);
+
+ my $tmpdir = &mktmpdir();
+ chdir $tmpdir || &croak("chdir $tmpdir failed");
+ &mkdirs($adir, 0700);
+ symlink ($linkdest, $afile) || &croak("link $afile failed");
+ &safesystem("$cmd_addlink $aqarchive $qafile >/dev/null");
+ unlink $afile || &croak("unlink $afile failed");
+ &rmdirs($adir);
+ chdir $oldpwd || &croak("chdir $oldpwd failed");
+ rmdir $tmpdir || &croak("rmdir $tmpdir failed");
+ exit;
+}
+
+# This operation is not used yet, because it is not
+# supported by the MC extfs.
+sub mczipfs_linkout {
+ my ($afile, $fsfile) = @_;
+ &checkargs(1, 'archive file', @_);
+ &checkargs(2, 'local file', @_);
+ my ($qafile) = map { &zipquotemeta($_) } $afile;
+
+ my $linkdest = &get_link_destination($afile);
+ symlink ($linkdest, $fsfile) || &croak("link $fsfile failed");
+ exit;
+}
+
+# Use unzip to find the link destination of a certain file in the
+# archive.
+sub get_link_destination {
+ my ($afile) = @_;
+ my ($qafile) = map { &zipquotemeta($_) } $afile;
+ my $linkdest = safeticks("$cmd_extract $qarchive $qafile");
+ &croak ("extract failed", "link destination of $afile not found")
+ if (!defined $linkdest || $linkdest eq '');
+ return $linkdest;
+}
+
+# List files in the archive.
+# Because mc currently doesn't allow a file's parent directory
+# to be listed after the file itself, we need to do some
+# rearranging of the output. Most of this is done in
+# checked_print_file.
+sub mczipfs_list {
+ open (PIPE, "$cmd_list $qarchive |") || &croak("$app_unzip failed");
+ if ($op_has_zipinfo) {
+ while (<PIPE>) {
+ chomp;
+ next if /^Archive:/;
+ next if /^\d+ file/;
+ next if /^Empty zipfile\.$/;
+ my @match = /$regex_zipinfo_line/;
+ next if ($#match != 13);
+ &checked_print_file(@match);
+ }
+ } else {
+ while (<PIPE>) {
+ chomp;
+ my @match = /$regex_nonzipinfo_line/;
+ next if ($#match != 10);
+
+ # Massage the date.
+ my ($year, $month, $day) = $match[4] > 12
+ ? ($match[4], $match[5], $match[6]) # 4,5,6 = Y,M,D
+ : ($match[6], $match[4], $match[5]); # 4,5,6 = M,D,Y
+ $year += ($year < 70 ? 2000 : 1900) if $year < 100; # Fix 2-digit year.
+
+ my @rmatch = ('', '', 'unknown', $match[0], '', $match[2], $match[1],
+ $year, $month, $day, $match[7], $match[8], "00", $match[10]);
+ &checked_print_file(@rmatch);
+ }
+ }
+ if (!close (PIPE)) {
+ &croak("$app_unzip failed") if ($! != 0);
+ &croak("$app_unzip failed", 'non-zero exit status ('.($? >> 8).')')
+ }
+
+ foreach my $key (sort keys %pending) {
+ foreach my $file (@{ $pending{$key} }) {
+ &print_file(@{ $file });
+ }
+ }
+
+ exit;
+}
+
+# Execute a file in the archive, by first extracting it to a
+# temporary directory. The name of the extracted file will be
+# the same as the name of it in the archive.
+sub mczipfs_run {
+ my ($afile) = @_;
+ &checkargs(1, 'archive file', @_);
+ my $qafile = &zipquotemeta(zipfs_realpathname($afile));
+ my $tmpdir = &mktmpdir();
+ my $tmpfile = File::Basename::basename($afile);
+
+ chdir $tmpdir || &croak("chdir $tmpdir failed");
+ &safesystem("$cmd_extract $aqarchive $qafile > $tmpfile");
+ chmod 0700, $tmpfile;
+ &safesystem("./$tmpfile");
+ unlink $tmpfile || &croak("rm $tmpfile failed");
+ chdir $oldpwd || &croak("chdir $oldpwd failed");
+ rmdir $tmpdir || &croak("rmdir $tmpdir failed");
+ exit;
+}
+
+# This is called prior to printing the listing of a file.
+# A check is done to see if the parent directory of the file has already
+# been printed or not. If it hasn't, we must cache it (in %pending) and
+# print it later once the parent directory has been listed. When all
+# files have been processed, there may still be some that haven't been
+# printed because their parent directories weren't listed at all. These
+# files are dealt with in mczipfs_list.
+sub checked_print_file {
+ my @waiting = ([ @_ ]);
+
+ while ($#waiting != -1) {
+ my $item = shift @waiting;
+ my $filename = ${$item}[13];
+ my $dirname = File::Basename::dirname($filename) . '/';
+
+ if (exists $known{$dirname}) {
+ &print_file(@{$item});
+ if ($filename =~ /\/$/) {
+ $known{$filename} = 1;
+ if (exists $pending{$filename}) {
+ push @waiting, @{ $pending{$filename} };
+ delete $pending{$filename};
+ }
+ }
+ } else {
+ push @{$pending{$dirname}}, $item;
+ }
+ }
+}
+
+# Print the mc extfs listing of a file from a set of parsed fields.
+# If the file is a link, we extract it from the zip archive and
+# include the output as the link destination. Because this output
+# is not newline terminated, we must execute unzip once for each
+# link file encountered.
+sub print_file {
+ my ($perms,$zipver,$platform,$realsize,$format,$cmpsize,$comp,$year,$mon,$day,$hours,$mins,$secs,$filename) = @_;
+ if ($platform ne 'unx') {
+ $perms = ($filename =~ /\/$/ ? 'drwxr-xr-x' : '-rw-r--r--');
+ }
+ # adjust abnormal perms on directory
+ if ($platform eq 'unx' && $filename =~ /\/$/ && $perms =~ /^\?(.*)$/) {
+ $perms = 'd'.$1;
+ }
+ printf "%-10s 1 %-8d %-8d %8s %s/%s/%s %s:%s:%s ./%s", $perms, $<,
+ $(, $realsize, $mon, $day, $year, $hours, $mins, $secs, $filename;
+ if ($platform eq 'unx' && $perms =~ /^l/) {
+ my $linkdest = &get_link_destination($filename);
+ print " -> $linkdest";
+ }
+ print "\n";
+}
+
+# Die with a reasonable error message.
+sub croak {
+ my ($command, $desc) = @_;
+ die "uzip ($cmd): $command - $desc\n" if (defined $desc);
+ die "uzip ($cmd): $command - $!\n";
+}
+
+# Make a set of directories, like the command `mkdir -p'.
+# This subroutine has been tailored for this script, and
+# because of that, it ignored the directory name '.'.
+sub mkdirs {
+ my ($dirs, $mode) = @_;
+ $dirs = &cleandirs($dirs);
+ return if ($dirs eq '.');
+
+ my $newpos = -1;
+ while (($newpos = index($dirs, '/', $newpos+1)) != -1) {
+ my $dir = substr($dirs, 0, $newpos);
+ mkdir ($dir, $mode) || &croak("mkdir $dir failed");
+ }
+ mkdir ($dirs, $mode) || &croak("mkdir $dirs failed");
+}
+
+# Remove a set of directories, failing if the directories
+# contain other files.
+# This subroutine has been tailored for this script, and
+# because of that, it ignored the directory name '.'.
+sub rmdirs {
+ my ($dirs) = @_;
+ $dirs = &cleandirs($dirs);
+ return if ($dirs eq '.');
+
+ rmdir $dirs || &croak("rmdir $dirs failed");
+ my $newpos = length($dirs);
+ while (($newpos = rindex($dirs, '/', $newpos-1)) != -1) {
+ my $dir = substr($dirs, 0, $newpos);
+ rmdir $dir || &croak("rmdir $dir failed");
+ }
+}
+
+# Return a semi-canonical directory name.
+sub cleandirs {
+ my ($dir) = @_;
+ $dir =~ s:/+:/:g;
+ $dir =~ s:/*$::;
+ return $dir;
+}
+
+# Make a temporary directory with mode 0700.
+sub mktmpdir {
+ use File::Temp qw(mkdtemp);
+ my $template = "/tmp/mcuzipfs.XXXXXX";
+ $template="$ENV{MC_TMPDIR}/mcuzipfs.XXXXXX" if ($ENV{MC_TMPDIR});
+ return mkdtemp($template);
+}
+
+# Make a filename absolute and return it.
+sub absolutize {
+ my ($file, $pwd) = @_;
+ return "$pwd/$file" if ($file !~ /^\//);
+ return $file;
+}
+
+# Like the system built-in function, but with error checking.
+# The other argument is an exit status to allow.
+sub safesystem {
+ my ($command, @allowrc) = @_;
+ my ($desc) = ($command =~ /^([^ ]*) */);
+ $desc = File::Basename::basename($desc);
+ system $command;
+ my $rc = $?;
+ &croak("`$desc' failed") if (($rc & 0xFF) != 0);
+ if ($rc != 0) {
+ $rc = $rc >> 8;
+ foreach my $arc (@allowrc) {
+ return if ($rc == $arc);
+ }
+ &croak("`$desc' failed", "non-zero exit status ($rc)");
+ }
+}
+
+# Like backticks built-in, but with error checking.
+sub safeticks {
+ my ($command, @allowrc) = @_;
+ my ($desc) = ($command =~ /^([^ ]*) /);
+ $desc = File::Basename::basename($desc);
+ my $out = `$command`;
+ my $rc = $?;
+ &croak("`$desc' failed") if (($rc & 0xFF) != 0);
+ if ($rc != 0) {
+ $rc = $rc >> 8;
+ foreach my $arc (@allowrc) {
+ return if ($rc == $arc);
+ }
+ &croak("`$desc' failed", "non-zero exit status ($rc)");
+ }
+ return $out;
+}
+
+# Make sure enough arguments are supplied, or die.
+sub checkargs {
+ my $count = shift;
+ my $desc = shift;
+ &croak('missing argument', $desc) if ($count-1 > $#_);
+}
+
+# Quote zip wildcard metacharacters. Unfortunately Info-ZIP zip and unzip
+# on unix interpret some wildcards in filenames, despite the fact that
+# the shell already does this. Thus this function.
+sub zipquotemeta {
+ my ($name) = @_;
+ my $out = '';
+ for (my $c = 0; $c < length $name; $c++) {
+ my $ch = substr($name, $c, 1);
+ $out .= '\\' if (index('*?[]\\', $ch) != -1);
+ $out .= $ch;
+ }
+ return quotemeta($out);
+}
diff --git a/src/vfs/extfs/helpers/uzoo.in b/src/vfs/extfs/helpers/uzoo.in
new file mode 100644
index 0000000..fb079a5
--- /dev/null
+++ b/src/vfs/extfs/helpers/uzoo.in
@@ -0,0 +1,69 @@
+#! /bin/sh
+#
+# Zoo file system
+#
+# Source of zoo can be found at
+# ftp://ftp.ibiblio.org/pub/Linux/utils/compress/
+
+ZOO=${MC_TEST_EXTFS_LIST_CMD:-zoo}
+
+# Stupid zoo won't work if the archive name has no .zoo extension, so we
+# have to make a symlink with a "better" name. Also, zoo can create
+# directories even if printing files to stdout, so it's safer to confine
+# it to a temporary directory.
+mklink ()
+{
+ TMPDIR=`mktemp -d ${MC_TMPDIR:-/tmp}/mctmpdir-uzoo.XXXXXX` || exit 1
+ trap 'cd /; rm -rf "$TMPDIR"' 0 1 2 3 5 13 15
+ ARCHIVE="$TMPDIR/tmp.zoo"
+ ln -sf "$1" "$ARCHIVE"
+ cd "$TMPDIR" || exit 1
+}
+
+mczoofs_list ()
+{
+ mklink "$1"
+ $ZOO lq "$ARCHIVE" | @AWK@ -v uid=$(id -ru) '
+/^[^\ ]/ { next }
+{
+if (NF < 8)
+ next
+if ($8 ~ /^\^/)
+ $8=substr($8, 2)
+if ($6 > 50)
+ $6=$6 + 1900
+else
+ $6=$6 + 2000
+split($7, a, ":")
+split("Jan:Feb:Mar:Apr:May:Jun:Jul:Aug:Sep:Oct:Nov:Dec", month_list, ":")
+for (i=1; i<=12; i++) {
+ month[month_list[i]] = i
+}
+if ($8 ~ /\/$/)
+ printf "drwxr-xr-x 1 %-8d %-8d %8d %02d-%02d-%4d %02d:%02d %s\n", uid, 0, $1, month[$5], $4, $6, a[1], a[2], $8
+else
+ printf "-rw-r--r-- 1 %-8d %-8d %8d %02d-%02d-%4d %02d:%02d %s\n", uid, 0, $1, month[$5], $4, $6, a[1], a[2], $8
+}' 2>/dev/null
+ exit 0
+}
+
+mczoofs_copyout ()
+{
+ mklink "$1"
+ # zoo only accepts name without directory as file to extract
+ base=`echo "$2" | sed 's,.*/,,'`
+ $ZOO xpq: "$ARCHIVE" "$base" > "$3"
+ cd /
+ exit 0
+}
+
+umask 077
+
+cmd="$1"
+shift
+case "$cmd" in
+ list) mczoofs_list "$@" ;;
+ copyout) mczoofs_copyout "$@" ;;
+ *) exit 1 ;;
+esac
+exit 0