diff -Nru rifiuti2-0.6.1/appveyor.bat rifiuti2-0.7.0/appveyor.bat --- rifiuti2-0.6.1/appveyor.bat 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/appveyor.bat 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,58 @@ +@echo off +:: Copyright 2018-2019 Abel Cheung +:: All rights reserved. + +set OLDPATH=%PATH% +PATH C:\msys64\%MSYSTEM%\bin;C:\msys64\usr\bin;%OLDPATH% + +cd %APPVEYOR_BUILD_FOLDER% +goto %1 + +echo Unknown build target. +exit 1 + + +:build +@echo on + +:: Build fails (unresolved dep) w/o updated msys2 runtime :-/ +pacman.exe --noconfirm --noprogressbar -Syu + +:: Second invocation updates everything else +:: Wastes too much time updating unnecessary non-core stuff +:: pacman --noconfirm --noprogressbar -Syu + +pacman.exe --noconfirm --noprogressbar -S --needed markdown mingw-w64-%MSYS2_ARCH%-glib2 +bash -lc "autoreconf -f -i -v && ./configure --enable-static && make all" + +@echo off +goto :eof + + +:check +@echo on + +file.exe src\rifiuti.exe +ldd.exe src\rifiuti.exe + +file.exe src\rifiuti-vista.exe +ldd.exe src\rifiuti-vista.exe + +bash -lc "make check" + +@echo off +goto :eof + + +:package +@echo on + +if "%APPVEYOR_REPO_TAG%" == "true" ( + echo "*** Building official release ***" + bash -lc "make -f dist-win.mk dist-win" +) else ( + bash -lc "make -f dist-win.mk dist-win ZIPNAME=%APPVEYOR_PROJECT_SLUG%-%APPVEYOR_REPO_COMMIT:~0,8%-win-%MSYS2_ARCH%" +) + +@echo off +goto :eof diff -Nru rifiuti2-0.6.1/autogen.sh rifiuti2-0.7.0/autogen.sh --- rifiuti2-0.6.1/autogen.sh 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/autogen.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -#!/bin/sh -ev - -glib-gettextize --copy --force - -autoreconf -f -i -v "$@" diff -Nru rifiuti2-0.6.1/ChangeLog rifiuti2-0.7.0/ChangeLog --- rifiuti2-0.6.1/ChangeLog 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/ChangeLog 2019-05-09 01:26:08.000000000 +0000 @@ -1,4 +1,3 @@ This file is intentionally left blank. -ChangeLog prior to 0.5.1 available in docs/ folder. -Newer changes can be inspected via git log. +ChangeLog can be inspected via git log. Announcement for users is available in NEWS.md . diff -Nru rifiuti2-0.6.1/configure.ac rifiuti2-0.7.0/configure.ac --- rifiuti2-0.6.1/configure.ac 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/configure.ac 2019-05-09 01:26:08.000000000 +0000 @@ -1,71 +1,136 @@ -dnl Process this file with autoconf to produce a configure script. - -AC_INIT([rifiuti2], [0.6.1], - [https://github.com/abelcheung/rifiuti2/issues],, +dnl vim: set sw=2 ts=2 expandtab : +dnl +dnl Copyright (C) 2007-2019 Abel Cheung. +dnl All rights reserved. + +AC_PREREQ([2.69]) +AC_INIT([rifiuti2], [0.7.0], + [https://github.com/abelcheung/rifiuti2/issues], [], [https://abelcheung.github.io/rifiuti2/]) AC_CONFIG_SRCDIR([src/rifiuti-vista.c]) -AC_CONFIG_HEADER([config.h]) +AC_CONFIG_HEADERS([config.h]) AC_CONFIG_TESTDIR([test]) AM_INIT_AUTOMAKE([1.11]) AC_CANONICAL_HOST +dnl Platform check. +AS_CASE([$host], + [*-*-msys* | *-*-mingw*], [is_mingw=y ], + [*-*-cygwin* ], [is_cygwin=y ], + [*-*-solaris* ], [os_solaris=y]) +AC_SUBST(is_mingw) +AC_SUBST(is_cygwin) +AM_CONDITIONAL([OS_WINDOWS], [test "x$is_mingw" != x]) dnl I18N -GETTEXT_PACKAGE=rifiuti -ALL_LINGUAS="`cat $srcdir/po/LINGUAS`" -AC_SUBST(GETTEXT_PACKAGE) -AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, ["$GETTEXT_PACKAGE"], - [Define the gettext package to be used]) - -AM_GLIB_GNU_GETTEXT +AM_GNU_GETTEXT([external]) +AM_GNU_GETTEXT_VERSION([0.19]) +LOCALEDIR_PORTABLE="${PACKAGE}-l10n" +AC_SUBST([LOCALEDIR_PORTABLE]) -dnl Compiler checks. +dnl Compiler and devel toolchain checks. +AS_BOX([Compiler and toolchain]) AC_PROG_CC AC_PROG_CC_STDC if test "x$ac_cv_prog_cc_c99" = "xno"; then - AC_MSG_ERROR([Could not find a C99 compatible compiler]) + AC_MSG_ERROR([Could not find a C99 compatible compiler]) fi dnl Always use -Wall for gcc -changequote(,)dnl -if test "x$GCC" = "xyes"; then - case " $CFLAGS " in - *[\ \ ]-Wall[\ \ ]*) ;; - *) CFLAGS="$CFLAGS -Wall" ;; - esac -fi -changequote([,])dnl - +AC_MSG_CHECKING(whether compiler understands -Wall) +old_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -Wall" +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], + [AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no) + CFLAGS="$old_CFLAGS"]) dnl Checks for programs. -AC_PROG_INSTALL AC_PROG_LN_S + +dnl dnl For test suite -AC_CHECK_PROG([XMLLINT], [xmllint], [xmllint]) -AC_CHECK_PROG([ICONV], [iconv], [iconv]) -AC_CHECK_PROG([PERL], [perl], [perl]) +dnl +AS_BOX([Test suite programs]) +AC_PATH_PROG([XMLLINT],[xmllint]) +dnl CP932 is commonly available among glibc, libiconv, +dnl as well as solaris with extra iconv module installed +AC_CACHE_CHECK([for iconv that supports more Windows codepages], [ac_cv_path_ICONV], + [AC_PATH_PROGS_FEATURE_CHECK([ICONV], [iconv], + [$ac_path_ICONV -f UTF-8 -t CP932 < /dev/null > /dev/null 2>&1 \ + && ac_cv_path_ICONV=$ac_path_ICONV ac_path_ICONV_found=:])]) +AC_SUBST([ICONV],[$ac_cv_path_ICONV]) +dnl dnl Checks for libraries. -PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.16.0], [:], - AC_MSG_ERROR([*** GLib not found. Errors follow: - $DEP_PKG_ERRORS])) +dnl +AS_BOX([Libraries]) + +dnl g_slist_free_full() requires 2.28.0, and +dnl g_win32_get_command_line() needs 2.40.0 +AS_VAR_SET_IF([is_mingw], + [glib_req="glib-2.0 >= 2.40.0"], + [glib_req="glib-2.0 >= 2.28.0"]) +PKG_CHECK_MODULES([GLIB], [$glib_req],, + [AC_MSG_ERROR([*** GLib not found. Error is: $GLIB_PKG_ERRORS])]) + +dnl +dnl See if glib is linked to Solaris implementation of iconv +dnl FIXME this check goes berserk under MinGW but magically works +dnl after manual gcc argument reordering +dnl +dnl There exists solaris derivatives that can install external +dnl glib2 package that links with libiconv, and they would require +dnl appropriate RPATH settings. However this is exception rather than +dnl the norm, and I don't have such system readily available now. +dnl GLIB_RPATH=`pkg-config --libs-only-L glib-2.0 | sed -e 's#-L#-R#g'` +dnl GLIB_LIBS="$GLIB_LIBS $GLIB_RPATH" + +AS_VAR_SET_IF([os_solaris],[dnl +save_CFLAGS="$CFLAGS" +save_LDFLAGS="$LDFLAGS" +CFLAGS="$CFLAGS $GLIB_CFLAGS" +LDFLAGS="$LDFLAGS $GLIB_LIBS $GLIB_RPATH" +AC_LANG_ASSERT([C]) +AC_MSG_CHECKING([iconv implementation used by glib2]) +AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ], + [return (g_iconv_open("UTF-8","CP950") == (GIConv) -1);])], + [AC_MSG_RESULT([native/libiconv])], + [AC_RUN_IFELSE([AC_LANG_PROGRAM([#include ], + [return (g_iconv_open("UTF-8","646") == (GIConv) -1);])], + [AC_MSG_RESULT([solaris])], + [AC_MSG_RESULT([unknown]) + AC_MSG_ERROR([dnl +iconv() implementation used by glib2 is not recognized, which +would encumber functionality of rifiuti2. Please contact maintainer +if you think this is a bug.])])]) +CFLAGS="$save_CFLAGS" +LDFLAGS="$save_LDFLAGS" +])dnl end AS_VAR_SET_IF os_solaris + +AC_ARG_ENABLE(static, + [AS_HELP_STRING([--enable-static], + [build static binaries [default=no]])],, + [enable_static=no]) + +dnl Need to pull in libiconv manually for mingw static build +dnl And this undocumented GLIB_STATIC_COMPILATION thing... dang +AS_VAR_IF([enable_static], [yes], [dnl + AS_VAR_SET_IF([is_mingw], + [pcfiles="glib-2.0 iconv"], + [pcfiles="glib-2.0"]) + GLIB_CFLAGS="$GLIB_CFLAGS -DGLIB_STATIC_COMPILATION" + GLIB_LIBS="`$PKG_CONFIG --libs --static $pcfiles` $GLIB_RPATH" + ]) +AM_CONDITIONAL([STATIC_BUILD], [test "x$enable_static" = "xyes"]) + AC_SUBST(GLIB_CFLAGS) AC_SUBST(GLIB_LIBS) - dnl Checks for header files. AC_CHECK_HEADERS([errno.h inttypes.h stdio.h time.h]) -dnl Platform specific check. -case "$host" in - *-*-mingw*) os_mingw=y ;; - *-*-*bsd*) os_bsd=y ;; -esac -AC_SUBST(os_mingw) -AC_SUBST(os_bsd) -AM_CONDITIONAL([OS_WINDOWS], [test "x$os_mingw" != x]) - dnl Checks for typedefs, structures, and compiler characteristics. AC_TYPE_UINT64_T if test "x$ac_cv_c_uint64_t" = "xno" || test "x$ac_cv_c_uint64_t" = "x"; then @@ -84,5 +149,3 @@ ]) AC_OUTPUT - -dnl vim: set sw=4 ts=4 noexpandtab : diff -Nru rifiuti2-0.6.1/debian/changelog rifiuti2-0.7.0/debian/changelog --- rifiuti2-0.6.1/debian/changelog 2018-08-29 19:08:19.000000000 +0000 +++ rifiuti2-0.7.0/debian/changelog 2019-07-29 00:00:54.000000000 +0000 @@ -1,3 +1,16 @@ +rifiuti2 (0.7.0-1) unstable; urgency=medium + + * New upstream version 0.7.0 + * Bumped DH level to 12. + * Bumped Standards-Version to 4.4.0 + * debian/clean: updated listed files. + * debian/copyright: removed unused references. + * debian/patches/*: removed all patches. + * debian/rules: removed an override. + * debian/tests/*: created to provide trivial tests. + + -- Giovani Augusto Ferreira Sun, 28 Jul 2019 21:00:54 -0300 + rifiuti2 (0.6.1-6) unstable; urgency=medium [ Raphaël Hertzog ] diff -Nru rifiuti2-0.6.1/debian/clean rifiuti2-0.7.0/debian/clean --- rifiuti2-0.6.1/debian/clean 2018-08-29 18:48:41.000000000 +0000 +++ rifiuti2-0.7.0/debian/clean 2019-07-28 21:52:02.000000000 +0000 @@ -1,3 +1,5 @@ po/zh_HK.gmo test/package.m4 test/testsuite +po/stamp-po +po/rifiuti2.pot diff -Nru rifiuti2-0.6.1/debian/compat rifiuti2-0.7.0/debian/compat --- rifiuti2-0.6.1/debian/compat 2018-08-29 18:52:11.000000000 +0000 +++ rifiuti2-0.7.0/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -11 diff -Nru rifiuti2-0.6.1/debian/control rifiuti2-0.7.0/debian/control --- rifiuti2-0.6.1/debian/control 2018-08-29 18:52:53.000000000 +0000 +++ rifiuti2-0.7.0/debian/control 2019-07-28 22:08:56.000000000 +0000 @@ -3,8 +3,8 @@ Priority: optional Maintainer: Debian Security Tools Uploaders: Giovani Augusto Ferreira -Build-Depends: debhelper (>= 11), libglib2.0-dev -Standards-Version: 4.2.1 +Build-Depends: debhelper-compat (= 12), libglib2.0-dev +Standards-Version: 4.4.0 Homepage: https://abelcheung.github.io/rifiuti2 Vcs-Browser: https://salsa.debian.org/pkg-security-team/rifiuti2 Vcs-Git: https://salsa.debian.org/pkg-security-team/rifiuti2.git diff -Nru rifiuti2-0.6.1/debian/copyright rifiuti2-0.7.0/debian/copyright --- rifiuti2-0.6.1/debian/copyright 2018-08-29 18:53:50.000000000 +0000 +++ rifiuti2-0.7.0/debian/copyright 2019-07-28 22:12:16.000000000 +0000 @@ -9,11 +9,6 @@ 2008 Anthony Wong License: BSD-3-Clause -Files: git.mk -Copyright: 2009 Red Hat, Inc. - 2010-2013 Behdad Esfahbod -License: Special - Files: debian/* Copyright: 2008 Anthony Wong 2008-2009 Daniel Baumann @@ -47,11 +42,6 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -License: Special - Copying and distribution of this file, with or without modification, - is permitted in any medium without royalty provided the copyright - notice and this notice are preserved. - License: GPL-2+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff -Nru rifiuti2-0.6.1/debian/patches/fix-no-format-arguments.patch rifiuti2-0.7.0/debian/patches/fix-no-format-arguments.patch --- rifiuti2-0.6.1/debian/patches/fix-no-format-arguments.patch 2018-08-29 18:48:41.000000000 +0000 +++ rifiuti2-0.7.0/debian/patches/fix-no-format-arguments.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -Description: fix a FTBFS caused by no format arguments. -Author: Joao Eriberto Mota Filho -Last-Update: 2015-07-25 -Index: rifiuti2-0.6.1/src/rifiuti.c -=================================================================== ---- rifiuti2-0.6.1.orig/src/rifiuti.c -+++ rifiuti2-0.6.1/src/rifiuti.c -@@ -443,7 +443,7 @@ main (int argc, - g_string_append_printf (str, "\\x%02X", (char) (*i)); - } - while ((char) (* (++i)) != '\0'); -- g_debug (str->str); -+ g_debug ("%s",str->str); - g_string_free (str, TRUE); - } - -Index: rifiuti2-0.6.1/src/rifiuti-vista.c -=================================================================== ---- rifiuti2-0.6.1.orig/src/rifiuti-vista.c -+++ rifiuti2-0.6.1/src/rifiuti-vista.c -@@ -533,7 +533,7 @@ main (int argc, - g_string_append_printf (str, "\\x%02X", (char) (*i)); - } - while ((char) (* (++i)) != '\0'); -- g_debug (str->str); -+ g_debug ("%s",str->str); - g_string_free (str, TRUE); - } - diff -Nru rifiuti2-0.6.1/debian/patches/series rifiuti2-0.7.0/debian/patches/series --- rifiuti2-0.6.1/debian/patches/series 2018-08-29 18:48:41.000000000 +0000 +++ rifiuti2-0.7.0/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -fix-no-format-arguments.patch diff -Nru rifiuti2-0.6.1/debian/rules rifiuti2-0.7.0/debian/rules --- rifiuti2-0.6.1/debian/rules 2018-08-29 18:48:41.000000000 +0000 +++ rifiuti2-0.7.0/debian/rules 2019-07-23 19:10:10.000000000 +0000 @@ -5,8 +5,6 @@ %: dh $@ -override_dh_autoreconf: - dh_autoreconf ./autogen.sh override_dh_auto_install: dh_auto_install diff -Nru rifiuti2-0.6.1/debian/tests/control rifiuti2-0.7.0/debian/tests/control --- rifiuti2-0.6.1/debian/tests/control 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/debian/tests/control 2019-07-28 23:42:27.000000000 +0000 @@ -0,0 +1 @@ +Test-Command: rifiuti2 -h diff -Nru rifiuti2-0.6.1/dist-win.mk rifiuti2-0.7.0/dist-win.mk --- rifiuti2-0.6.1/dist-win.mk 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/dist-win.mk 2019-05-09 01:26:08.000000000 +0000 @@ -1,49 +1,46 @@ +# vim: set sw=4 ts=4 noexpandtab : # # Make file fragment for rifiuti2 -- maintainer only # Handle binary distribution for Windows # -dist-win: win-pkg-data win-pkg/x86 win-pkg/x64 - cd win-pkg && zip -9 -r $(abs_top_builddir)/$(distdir)-win.zip . +include Makefile -win-pkg-data: win-pkg/rifiuti-l10n win-pkg/README.html +ZIPNAME ?= $(distdir)-win-$(build_cpu) + +dist-win: win-pkg-data win-pkg-bin + cd win-pkg && \ + rm -f $(ZIPNAME).zip && \ + 7z a -bd -o$(abs_top_builddir) $(ZIPNAME).zip . + +win-pkg-data: win-pkg/$(LOCALEDIR_PORTABLE) win-pkg/README.html + +win-pkg-bin: \ + win-pkg/rifiuti.exe \ + win-pkg/rifiuti-vista.exe win-pkg/README.html: $(top_srcdir)/src/rifiuti.1 set -e ;\ tmpfile1=$$(mktemp) ;\ tmpfile2=$$(mktemp) ;\ groff -Thtml -mman $< | \ - sed -r '/####CHANGELOG####/ s@( $$tmpfile1 ;\ + sed -r 's@(####CHANGELOG####)

@\n\2\n@' > $$tmpfile1 ;\ ( sed -e '0,/^##[^#]/d; /^----/,$$d;' $(abs_top_srcdir)/NEWS.md | \ - markdown | sed -e 's/h[0-9]>/strong>/g;'; ) > $$tmpfile2 ;\ + markdown | sed -r 's@<(/?)h[0-9]>@<\1strong>@g;'; ) > $$tmpfile2 ;\ ( sed -e '/^####CHANGELOG####/,$$d' $$tmpfile1; cat $$tmpfile2; \ sed -e '0,/^####CHANGELOG####/d' $$tmpfile1 ) > $@ ;\ rm -f $$tmpfile1 $$tmpfile2 -win-pkg/rifiuti-l10n: $(top_srcdir)/po/$(GETTEXT_PACKAGE).pot - cd po && $(MAKE) install gnulocaledir=$(abs_top_builddir)/$@ - cp $< $(abs_top_builddir)/$@ - -win-pkg/x86: - test "`objdump -f $(top_builddir)/src/rifiuti$(EXEEXT) | \ - awk '/file format/ {print $$NF}'`" = "pei-i386" - objdump -f $(top_builddir)/src/rifiuti$(EXEEXT) | grep -q "HAS_RELOC" - $(MKDIR_P) $@ - strip --strip-unneeded -o $@/rifiuti$(EXEEXT) \ - $(top_builddir)/src/rifiuti$(EXEEXT) - strip --strip-unneeded -o $@/rifiuti-vista$(EXEEXT) \ - $(top_builddir)/src/rifiuti-vista$(EXEEXT) - -win-pkg/x64: - test "`objdump -f $(top_builddir)/src/rifiuti$(EXEEXT) | \ - awk '/file format/ {print $$NF}'`" = "pei-x86-64" - objdump -f $(top_builddir)/src/rifiuti$(EXEEXT) | grep -q "HAS_RELOC" - $(MKDIR_P) $@ - strip --strip-unneeded -o $@/rifiuti$(EXEEXT) \ - $(top_builddir)/src/rifiuti$(EXEEXT) - strip --strip-unneeded -o $@/rifiuti-vista$(EXEEXT) \ - $(top_builddir)/src/rifiuti-vista$(EXEEXT) - -.PHONY: win-pkg-data dist-win +win-pkg/$(LOCALEDIR_PORTABLE): $(top_srcdir)/po/$(PACKAGE).pot + cd po && $(MAKE) install localedir=$(abs_top_builddir)/$@ + cp $< $(abs_top_builddir)/$@/ + +win-pkg/rifiuti.exe: $(top_builddir)/src/rifiuti.exe + $(MKDIR_P) win-pkg + strip --strip-unneeded -o $@ $< + +win-pkg/rifiuti-vista.exe: $(top_builddir)/src/rifiuti-vista.exe + $(MKDIR_P) win-pkg + strip --strip-unneeded -o $@ $< +.PHONY: win-pkg-data win-pkg-bin dist-win diff -Nru rifiuti2-0.6.1/docs/AUTHORS rifiuti2-0.7.0/docs/AUTHORS --- rifiuti2-0.6.1/docs/AUTHORS 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/docs/AUTHORS 2019-05-09 01:26:08.000000000 +0000 @@ -1,5 +1,5 @@ Current version: -Copyright (C) 2007, 2015 Abel Cheung +Copyright (C) 2007-2019 Abel Cheung Original version of rifiuti: Copyright (C) 2003 Keith J. Jones diff -Nru rifiuti2-0.6.1/docs/ChangeLog.old rifiuti2-0.7.0/docs/ChangeLog.old --- rifiuti2-0.6.1/docs/ChangeLog.old 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/docs/ChangeLog.old 1970-01-01 00:00:00.000000000 +0000 @@ -1,169 +0,0 @@ -* -* -* This file is now obsolete. All future changes are available from Git log. -* -* - -========== 0.5.1 release ========== - -2008-11-30 Abel Cheung - - * src/rifiuti-vista.c: - Warn if recycle bin contains any special file that shouldn't appear in - recycle bin at all. - -2008-11-25 Anthony Wong - - * test/: Add 3 test cases and results. - -2008-11-23 Anthony Wong - - * debian/: Check in debian packaging related files. - -2008-11-21 Anthony Wong - - * src/rifiuti.1: Add manpage. - -2007-10-14 Abel Cheung - - * configure.ac: Post-release version bump. - -========== 0.5.0 release ========== - -2007-10-07 Abel Cheung - - * TODO: New file. - -2007-10-07 Abel Cheung - - * src/*.h: Include inttypes.h instead of stdint.h. - * src/rifiuti.c: Minor string changes. - -2007-10-05 Abel Cheung - - * src/rifiuti.c, src/rifiuti-vista.c: - - Drop hardcoded i18n parameters. - * README: Minor changes. - -2007-10-04 Abel Cheung - - * THANKS: New file. - * AUTHORS, NEWS, README: Updated. - -2007-10-04 Abel Cheung - - * configure.ac: - - Disable AC_TYPE_UINT{32,64}_T check, not available in older - autoconf. - -2007-10-04 Abel Cheung - - * configure.ac: - - Drop $localedir definition, AM_GLIB_GNU_GETTEXT should do it. - * src/Makefile.am, po/ChangeLog: - - Now make distcheck works. - -2007-10-04 Abel Cheung - - * configure.ac: Check for install prog as well. - * autogen.sh: Add file (thanks to sample from dia). - -2007-10-04 Abel Cheung - - * AUTHORS, COPYING, INSTALL, NEWS, README, configure.ac, po, - Makefile.am, src/Makefile.am: - - Add autotools structure. - -2007-09-28 Abel Cheung - - * src/rifiuti-vista.c: Implement --always-utf8 option. - -2007-09-28 Abel Cheung - - * src/rifiuti.c: Some string changes. - -2007-09-24 Abel Cheung - - * src/rifiuti-vista.dtd: New file. - * src/rifiuti-vista.c: Implement XML output mode. - -2007-09-24 Abel Cheung - - * src/rifiuti-vista.c, src/rifiuti-vista.h: - - New version for Vista recycle bin analysis. - -2007-09-22 Abel Cheung - - * src/utils.c, src/utils.h: New file. - * src/rifiuti.c: - - Move win_filetime_to_epoch() and shared enum's to utils.[ch]. - - Forced UTF-8 output is implemented. - - print_header(), print_footer(), print_record() become private functions. - * src/Makefile: Cope for necessary changes. - -2007-09-21 Abel Cheung - - * src/rifiuti.c: - - Reorganize options into separate groups. - - Make XML output mode conflict with any plain text option. - - Mark some strings for translation. - -2007-09-20 Abel Cheung - - * src/rifiuti.c: - - Build fix for MinGW (avoid GNU-only calls). - -2007-09-20 Abel Cheung - - * src/rifiuti.c (print_record): - - For Win98 INFO2 file, try to convert from legacy file name to UTF-8 - in XML display mode. - - Very minor string changes. - -2007-09-17 Abel Cheung - - * src/rifiuti.c: - - Show warning message if print_* functions are called with unknown - output format (highly unlikely). - - Some calls to guard against failure in charset conversion. - - Add --from-encoding option to specify encoding to convert from, - which is useful ONLY when INFO2 is created by Win98 (thus no unicode - file name), and output is in XML format (which is unicode only). Does - nothing for now. - -2007-09-17 Abel Cheung - - * src/rifiuti.dtd: New file. - * src/rifiuti.h: Add output format enum. - * src/rifiuti.c: - - (print_header, print_footer, print_record) New functions. - - Add support for XML output format. - -2007-09-16 Abel Cheung - - * src/rifiuti.c: - - Add -o option to specify output file name. - - Supports reading data from stdin. - - Use file stream throughout the whole file, drop usage of file - descriptor. - - Change fprintf into fputs or g_fprintf when the most appropriate - one fits. - -2007-09-14 Abel Cheung - - * src/rifiuti.c: - - Cope with the case when deleted file is from a network - share (thus first char is backslash). - - Defines _GNU_SOURCE to avoid warning when compiling under Linux. - - Replace g_strdup_printf with g_snprintf to avoid copying exceedingly - large string. - - Warn if end of file is not a proper footer (exactly 4 bytes). - -2007-09-09 Abel Cheung - - * Makefile: Reorder gcc arguments to fix compilation in cygwin. - -2007-09-08 Abel Cheung - - * First check-in of my modified rifiuti. - diff -Nru rifiuti2-0.6.1/docs/Compile.md rifiuti2-0.7.0/docs/Compile.md --- rifiuti2-0.6.1/docs/Compile.md 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/docs/Compile.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -## Compile requirements - -Bootstraping process can be done for Unix/Linux like OS released since 2010. - - * `glib` ≥ 2.16 (deveopment headers and libraries, available since 2008) - * `pkg-config` (or its replacement on *BSD, `pkgconf`) - * basic autotools-based development toolchain - * C compiler — both `gcc` and `clang` are tested - * make - * automake ≥ 1.11 - * autoconf - -A few extra programs are also checked (`perl`, `iconv`, `xmllint`), but they are -for testsuite only and not necessary for building software. - -## Compile procedure on Linux / BSD - -First execute `autogen.sh` to generate autotools files necessary for building software: -``` -$ ./autogen.sh -``` - -After that, everything follows the usual autotools procedure: -``` -$ ./configure && make check -``` - -*Optional*: install software with administrator privilege: -``` -# make install -``` - -`rifiuti2` can be used even without installing, only that there would be no -translations in messages and result. - -If compile or `make check` fails, please report problem to Github page, describing your OS -and compile environment, and attach `test/testsuite.log` on Gist. - -## Compile procedure on MinGW / MSYS - -`rifiuti2` has only been thoroughly tested on [MSYS2][1], a Unix-like -environment based on [mingw-w64][2], akin to the old [MSYS 1.0][5] which was mingw32 based. -There are other mingw-w64 based distributions like [Cygwin][3] and [win-build][4], -your mileage may vary. - -Follow the following procedure to setup MSYS2 necessary for building `rifiuti2`: - -1. Grab installer from [MSYS2 page][1], and follow all instructions on that page. -In particular, please **tread carefully** when starting to use `pacman` for upgrading -packages. [Some pitfalls may result from careless update.][6] (Read section III of -this page carefully) - -2. After basic setup is done, grab Glib runtime and development packages - - Install with `pacman -Sy mingw-w64-x86_64-glib2` for 64bit build, and/or - - Install with `pacman -Sy mingw-w64-i686-glib2` for 32bit build - -3. If one intend to run `rifiuti2` outside of MSYS bash environment -(such as under Windows `cmd`), either: - 1. Link program statically with `make LDFLAGS="-static"`. In this case - `/mingw{64,32}/lib/pkgconfig/glib-2.0.pc` need to be manually edited to - append `-liconv` to `Libs.private` line. - 2. Copy all necessary libraries under `/mingw64/bin` or `/mingw32/bin` to - the same folder `rifiuti` binaries reside in - -**Note 1**: -Windows binary distributed on Github are statically linked to existing -version of glib available in MSYS2 — 2.44 as of May 2015. - -**Note 2**: -MSYS 1.0 might still be able to compile `rifiuti2`, but no effort will be -made to ensure it's working at all. - -[1]: https://msys2.github.io/ -[2]: http://mingw-w64.yaxm.org/doku.php -[3]: https://cygwin.com/ -[4]: http://win-builds.org/doku.php -[5]: http://www.mingw.org/wiki/msys -[6]: https://sourceforge.net/p/msys2/wiki/MSYS2%20installation/ diff -Nru rifiuti2-0.6.1/docs/LICENSE rifiuti2-0.7.0/docs/LICENSE --- rifiuti2-0.6.1/docs/LICENSE 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/docs/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -Other than autotools generated files (which are covered by GNU licesne), -all other files are available through the following 3-clause BSD license. - -================================================================= - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. Neither the name of the project nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - * -THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -SUCH DAMAGE. - diff -Nru rifiuti2-0.6.1/docs/LICENSE.md rifiuti2-0.7.0/docs/LICENSE.md --- rifiuti2-0.6.1/docs/LICENSE.md 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/docs/LICENSE.md 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,53 @@ +# License + +`rifiuti2` is released in Revised BSD License: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the project nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS *AS IS* AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +---- + +This project relies heavily on [Glib library][1], +which is released under [LGPL license][2]. + +---- + +This project uses [Automake] as build system, +which is covered under [GPL license][2]. + +---- + +This project also incorporates one of the files from [pyrediff project][3], +which carries following license statement: + + Copyright (c) 2013-2017 Luke Mewburn + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the + copyright notice and this notice are preserved. + This file is offered as-is, without any warranty. + +[1]: https://wiki.gnome.org/Projects/GLib +[2]: https://www.gnu.org/licenses/licenses.html +[3]: https://github.com/lukem/pyrediff diff -Nru rifiuti2-0.6.1/docs/THANKS rifiuti2-0.7.0/docs/THANKS --- rifiuti2-0.6.1/docs/THANKS 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/docs/THANKS 2019-05-09 01:26:08.000000000 +0000 @@ -5,8 +5,25 @@ http://odessa.sourceforge.net/ - -But there are more people to say thanks: +There are more people to say thanks: Hilda Chan (Providing Chinese Vista Recycle Bin sample data) Anthony Wong (Providing manpage & original Debian packaging) + +Finally, rifiuti2 won't have extensive testing without several OSS projects +and software archives: + +1. winworldpc.com library, hosting many obsolete and unpurchaseable + versions of Windows, especially ancient beta versions which provide + insight on how Windows has evolved. + +2. VirtualBox and Qemu, without their excellent virtual machine emulation + it is simply impossible to test program behavior on different platforms. + +3. A few website hosting obsolete service packs and ancient IE installers, + relieving some pain on network access and tools installation in virtual + machines: + + - Folks at browser.evolt.org who mirrored many ancient IE versions + - Mirror for all NT service packs in sdfox7.com/winntsp.htm + - There can be other website I forgot to mention diff -Nru rifiuti2-0.6.1/.gitattribute rifiuti2-0.7.0/.gitattribute --- rifiuti2-0.6.1/.gitattribute 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/.gitattribute 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,2 @@ +* text eol=lf +*.bat text eol=crlf diff -Nru rifiuti2-0.6.1/.gitignore rifiuti2-0.7.0/.gitignore --- rifiuti2-0.6.1/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/.gitignore 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,46 @@ +**/*.exe +**/*.o +**/.deps +**/Makefile +**/Makefile.in +/.dirstamp +/ABOUT-NLS +/aclocal.m4 +/autom4te.cache +/compile +/config.cache +/config.guess +/config.h +/config.h.in +/config.log +/config.rpath +/config.status +/config.sub +/configure +/depcomp +/install-sh +/m4 +/missing +/mkinstalldirs +/po/*.gmo +/po/*.header +/po/*.mo +/po/*.pot +/po/*.sed +/po/insert-header.sin +/po/Makefile.in.in +/po/Makevars.template +/po/POTFILES +/po/remove-potcdate.sin +/po/Rules-quot +/po/stamp-it +/po/stamp-po +/src/rifiuti +/src/rifiuti-vista +/stamp-h1 +/test/atconfig +/test/atlocal +/test/package.m4 +/test/testsuite +/test/testsuite.dir +/test/testsuite.log diff -Nru rifiuti2-0.6.1/git.mk rifiuti2-0.7.0/git.mk --- rifiuti2-0.6.1/git.mk 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/git.mk 1970-01-01 00:00:00.000000000 +0000 @@ -1,312 +0,0 @@ -# git.mk -# -# Copyright 2009, Red Hat, Inc. -# Copyright 2010,2011,2012,2013 Behdad Esfahbod -# Written by Behdad Esfahbod -# -# Copying and distribution of this file, with or without modification, -# is permitted in any medium without royalty provided the copyright -# notice and this notice are preserved. -# -# The latest version of this file can be downloaded from: -# https://raw.github.com/behdad/git.mk/master/git.mk -# Bugs, etc, should be reported upstream at: -# https://github.com/behdad/git.mk -# -# To use in your project, import this file in your git repo's toplevel, -# then do "make -f git.mk". This modifies all Makefile.am files in -# your project to -include git.mk. Remember to add that line to new -# Makefile.am files you create in your project, or just rerun the -# "make -f git.mk". -# -# This enables automatic .gitignore generation. If you need to ignore -# more files, add them to the GITIGNOREFILES variable in your Makefile.am. -# But think twice before doing that. If a file has to be in .gitignore, -# chances are very high that it's a generated file and should be in one -# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES. -# -# The only case that you need to manually add a file to GITIGNOREFILES is -# when remove files in one of mostlyclean-local, clean-local, distclean-local, -# or maintainer-clean-local make targets. -# -# Note that for files like editor backup, etc, there are better places to -# ignore them. See "man gitignore". -# -# If "make maintainer-clean" removes the files but they are not recognized -# by this script (that is, if "git status" shows untracked files still), send -# me the output of "git status" as well as your Makefile.am and Makefile for -# the directories involved and I'll diagnose. -# -# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see -# Makefile.am.sample in the git.mk git repo. -# -# Don't EXTRA_DIST this file. It is supposed to only live in git clones, -# not tarballs. It serves no useful purpose in tarballs and clutters the -# build dir. -# -# This file knows how to handle autoconf, automake, libtool, gtk-doc, -# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu. -# -# This makefile provides the following targets: -# -# - all: "make all" will build all gitignore files. -# - gitignore: makes all gitignore files in the current dir and subdirs. -# - .gitignore: make gitignore file for the current dir. -# - gitignore-recurse: makes all gitignore files in the subdirs. -# -# KNOWN ISSUES: -# -# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the -# submodule doesn't find us. If you have configure.{in,ac} files in -# subdirs, add a proxy git.mk file in those dirs that simply does: -# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste. -# And add those files to git. See vte/gnome-pty-helper/git.mk for -# example. -# - - - -############################################################################### -# Variables user modules may want to add to toplevel MAINTAINERCLEANFILES: -############################################################################### - -# -# Most autotools-using modules should be fine including this variable in their -# toplevel MAINTAINERCLEANFILES: -GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \ - $(srcdir)/aclocal.m4 \ - $(srcdir)/autoscan.log \ - $(srcdir)/configure.scan \ - `AUX_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_AUX_DIR:$$1' ./configure.ac); \ - test "x$$AUX_DIR" = "x$(srcdir)/" && AUX_DIR=$(srcdir); \ - for x in \ - ar-lib \ - compile \ - config.guess \ - config.sub \ - depcomp \ - install-sh \ - ltmain.sh \ - missing \ - mkinstalldirs \ - test-driver \ - ; do echo "$$AUX_DIR/$$x"; done` \ - `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_HEADERS:$$1' ./configure.ac | \ - head -n 1 | while read f; do echo "$(srcdir)/$$f.in"; done` -# -# All modules should also be fine including the following variable, which -# removes automake-generated Makefile.in files: -GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \ - `cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' ./configure.ac | \ - while read f; do \ - case $$f in Makefile|*/Makefile) \ - test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \ - done` -# -# Modules that use libtool and use AC_CONFIG_MACRO_DIR() may also include this, -# though it's harmless to include regardless. -GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \ - `MACRO_DIR=$(srcdir)/$$(cd $(top_srcdir); $(AUTOCONF) --trace 'AC_CONFIG_MACRO_DIR:$$1' ./configure.ac); \ - if test "x$$MACRO_DIR" != "x$(srcdir)/"; then \ - for x in \ - libtool.m4 \ - ltoptions.m4 \ - ltsugar.m4 \ - ltversion.m4 \ - lt~obsolete.m4 \ - ; do echo "$$MACRO_DIR/$$x"; done; \ - fi` - - - -############################################################################### -# Default rule is to install ourselves in all Makefile.am files: -############################################################################### - -git-all: git-mk-install - -git-mk-install: - @echo "Installing git makefile" - @any_failed=; \ - find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \ - if grep 'include .*/git.mk' $$x >/dev/null; then \ - echo "$$x already includes git.mk"; \ - else \ - failed=; \ - echo "Updating $$x"; \ - { cat $$x; \ - echo ''; \ - echo '-include $$(top_srcdir)/git.mk'; \ - } > $$x.tmp || failed=1; \ - if test x$$failed = x; then \ - mv $$x.tmp $$x || failed=1; \ - fi; \ - if test x$$failed = x; then : else \ - echo "Failed updating $$x"; >&2 \ - any_failed=1; \ - fi; \ - fi; done; test -z "$$any_failed" - -.PHONY: git-all git-mk-install - - - -############################################################################### -# Actual .gitignore generation: -############################################################################### - -$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk - @echo "git.mk: Generating $@" - @{ \ - if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \ - for x in \ - $(DOC_MODULE)-decl-list.txt \ - $(DOC_MODULE)-decl.txt \ - tmpl/$(DOC_MODULE)-unused.sgml \ - "tmpl/*.bak" \ - xml html \ - ; do echo "/$$x"; done; \ - FLAVOR=$$(cd $(top_srcdir); $(AUTOCONF) --trace 'GTK_DOC_CHECK:$$2' ./configure.ac); \ - case $$FLAVOR in *no-tmpl*) echo /tmpl;; esac; \ - fi; \ - if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \ - for lc in $(DOC_LINGUAS); do \ - for x in \ - $(if $(DOC_MODULE),$(DOC_MODULE).xml) \ - $(DOC_PAGES) \ - $(DOC_INCLUDES) \ - ; do echo "/$$lc/$$x"; done; \ - done; \ - for x in \ - $(_DOC_OMF_ALL) \ - $(_DOC_DSK_ALL) \ - $(_DOC_HTML_ALL) \ - $(_DOC_MOFILES) \ - $(DOC_H_FILE) \ - "*/.xml2po.mo" \ - "*/*.omf.out" \ - ; do echo /$$x; done; \ - fi; \ - if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \ - for lc in $(HELP_LINGUAS); do \ - for x in \ - $(HELP_FILES) \ - "$$lc.stamp" \ - "$$lc.mo" \ - ; do echo "/$$lc/$$x"; done; \ - done; \ - fi; \ - if test "x$(gsettings_SCHEMAS)" = x; then :; else \ - for x in \ - $(gsettings_SCHEMAS:.xml=.valid) \ - $(gsettings__enum_file) \ - ; do echo "/$$x"; done; \ - fi; \ - if test -f $(srcdir)/po/Makefile.in.in; then \ - for x in \ - po/Makefile.in.in \ - po/Makefile.in.in~ \ - po/Makefile.in \ - po/Makefile \ - po/Makevars.template \ - po/POTFILES \ - po/Rules-quot \ - po/stamp-it \ - po/.intltool-merge-cache \ - "po/*.gmo" \ - "po/*.header" \ - "po/*.mo" \ - "po/*.sed" \ - "po/*.sin" \ - po/$(GETTEXT_PACKAGE).pot \ - intltool-extract.in \ - intltool-merge.in \ - intltool-update.in \ - ; do echo "/$$x"; done; \ - fi; \ - if test -f $(srcdir)/configure; then \ - for x in \ - autom4te.cache \ - configure \ - config.h \ - stamp-h1 \ - libtool \ - config.lt \ - ; do echo "/$$x"; done; \ - fi; \ - if test "x$(DEJATOOL)" = x; then :; else \ - for x in \ - $(DEJATOOL) \ - ; do echo "/$$x.sum"; echo "/$$x.log"; done; \ - echo /site.exp; \ - fi; \ - if test "x$(am__dirstamp)" = x; then :; else \ - echo "$(am__dirstamp)"; \ - fi; \ - if test "x$(LTCOMPILE)" = x -a "x$(GTKDOC_RUN)" = x; then :; else \ - for x in \ - "*.lo" \ - ".libs" "_libs" \ - ; do echo "$$x"; done; \ - fi; \ - for x in \ - .gitignore \ - $(GITIGNOREFILES) \ - $(CLEANFILES) \ - $(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \ - $(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \ - $(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \ - so_locations \ - $(MOSTLYCLEANFILES) \ - $(TEST_LOGS) \ - $(TEST_LOGS:.log=.trs) \ - $(TEST_SUITE_LOG) \ - "*.$(OBJEXT)" \ - $(DISTCLEANFILES) \ - $(am__CONFIG_DISTCLEAN_FILES) \ - $(CONFIG_CLEAN_FILES) \ - TAGS ID GTAGS GRTAGS GSYMS GPATH tags \ - "*.tab.c" \ - $(MAINTAINERCLEANFILES) \ - $(BUILT_SOURCES) \ - $(DEPDIR) \ - $(patsubst %.vala,%.c,$(filter %.vala,$(SOURCES))) \ - $(filter %_vala.stamp,$(DIST_COMMON)) \ - $(filter %.vapi,$(DIST_COMMON)) \ - $(filter %$(patsubst %.vapi,%.h,$(filter %.vapi,$(DIST_COMMON))),$(DIST_COMMON)) \ - Makefile \ - Makefile.in \ - "*.orig" \ - "*.rej" \ - "*.bak" \ - "*~" \ - ".*.sw[nop]" \ - ".*.un~" \ - ".dirstamp" \ - ; do echo "/$$x"; done; \ - } | \ - sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \ - sed 's@/[.]/@/@g' | \ - LC_ALL=C sort | uniq > $@.tmp && \ - mv $@.tmp $@; - -all: $(srcdir)/.gitignore gitignore-recurse-maybe -gitignore: $(srcdir)/.gitignore gitignore-recurse - -gitignore-recurse-maybe: - @for subdir in $(DIST_SUBDIRS); do \ - case " $(SUBDIRS) " in \ - *" $$subdir "*) :;; \ - *) test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir");; \ - esac; \ - done -gitignore-recurse: - @for subdir in $(DIST_SUBDIRS); do \ - test "$$subdir" = . -o -e "$$subdir/.git" || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) gitignore || echo "Skipping $$subdir"); \ - done - -maintainer-clean: gitignore-clean -gitignore-clean: - -rm -f $(srcdir)/.gitignore - -.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe diff -Nru rifiuti2-0.6.1/Makefile.am rifiuti2-0.7.0/Makefile.am --- rifiuti2-0.6.1/Makefile.am 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/Makefile.am 2019-05-09 01:26:08.000000000 +0000 @@ -1,24 +1,31 @@ +# vim: set sw=4 ts=4 noexpandtab : + AUTOMAKE_OPTIONS = foreign dist-xz dist-zip SUBDIRS = src po test EXTRA_DIST = \ - docs \ - README.md \ - NEWS.md \ - $(NULL) + docs \ + README.md \ + NEWS.md \ + $(NULL) dist-hook: -find $(top_distdir) -type f \( -name '*~' -o -name '.*~' \) -print0 | xargs -0 -r rm -f +# despite what manual says, I want all build auxiliary files gone MAINTAINERCLEANFILES = \ - $(GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL) \ - $(GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN) \ - $(NULL) - --include $(top_srcdir)/dist-win.mk + $(top_srcdir)/configure \ + $(top_srcdir)/aclocal.m4 \ + $(top_srcdir)/compile \ + $(top_srcdir)/config.guess \ + $(top_srcdir)/config.h.in \ + $(top_srcdir)/config.sub \ + $(top_srcdir)/depcomp \ + $(top_srcdir)/install-sh \ + $(top_srcdir)/Makefile.in \ + $(top_srcdir)/missing \ + $(NULL) distclean-local: -rm -rf win-pkg - --include $(top_srcdir)/git.mk diff -Nru rifiuti2-0.6.1/NEWS.md rifiuti2-0.7.0/NEWS.md --- rifiuti2-0.6.1/NEWS.md 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/NEWS.md 2019-05-09 01:26:08.000000000 +0000 @@ -1,3 +1,41 @@ +## 0.7.0 +#### Feature Addition +* Support recycle bin from jurassic Windows: 95, NT4, ME (Issue #9) +* Verified to work for recycle bin on network shared folder using + UNC path (such thing is rare but does exist) +* Display timezone in tab-delimited output header +* Guess Windows version based on recycle bin artifacts +* Distributed Windows binaries: + * Copes better with Windows ACL, detecting folder with + insufficient permissions + * Attempts to detect Windows locale setting and automatically determine + translation to use + +#### Change +* Now **mandates UTF-8 locale except on Windows** + * File output is also in UTF-8 encoding under Windows + * `-8` option is rendered obsolete as a result +* **Distributed Windows binaries can only run on Vista or above** + * Windows XP/2003 support removed due to glib changes +* Won't overwrite destination file if it already exists +* `$Recycle.bin` version: + * Not printing file size field if it is corrupt + * Exit with error status whenever errors are found in any entry, + not just the last entry +* `INFO2` version: + * Restricts the choice of legacy path character encoding; generally, + all encodings not ASCII compatible are disallowed +* Building requirement changes + * Remove GNUism for part of build toolchain (`make`, `awk`) + * Use external GNU gettext instead of obsolete `glib-gettextize` + +#### Bug fix +* Fix unicode display on Windows console (Issue #12) +* More robust handling of invalid or undecipherable characters, + displaying escaped hex or unicode sequences in such cases (Issue #5) + +---- + ## 0.6.1 #### Bug fix * Restore old date/time format for tab-delimited output, in order to be diff -Nru rifiuti2-0.6.1/po/Makevars rifiuti2-0.7.0/po/Makevars --- rifiuti2-0.6.1/po/Makevars 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/po/Makevars 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,78 @@ +# Makefile variables for PO directory in any package using GNU gettext. + +# Usually the message domain is the same as the package name. +DOMAIN = $(PACKAGE) + +# These two variables depend on the location of this directory. +subdir = po +top_builddir = .. + +# These options get passed to xgettext. +XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ --add-comments="TRANSLATOR" + +# This is the copyright holder that gets inserted into the header of the +# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding +# package. (Note that the msgstr strings, extracted from the package's +# sources, belong to the copyright holder of the package.) Translators are +# expected to transfer the copyright for their translations to this person +# or entity, or to disclaim their copyright. The empty string stands for +# the public domain; in this case the translators are expected to disclaim +# their copyright. +COPYRIGHT_HOLDER = + +# This tells whether or not to prepend "GNU " prefix to the package +# name that gets inserted into the header of the $(DOMAIN).pot file. +# Possible values are "yes", "no", or empty. If it is empty, try to +# detect it automatically by scanning the files in $(top_srcdir) for +# "GNU packagename" string. +PACKAGE_GNU = no + +# This is the email address or URL to which the translators shall report +# bugs in the untranslated strings: +# - Strings which are not entire sentences, see the maintainer guidelines +# in the GNU gettext documentation, section 'Preparing Strings'. +# - Strings which use unclear terms or require additional context to be +# understood. +# - Strings which make invalid assumptions about notation of date, time or +# money. +# - Pluralisation problems. +# - Incorrect English spelling. +# - Incorrect formatting. +# It can be your email address, or a mailing list address where translators +# can write to without being subscribed, or the URL of a web page through +# which the translators can contact you. +MSGID_BUGS_ADDRESS = https://github.com/abelcheung/rifiuti2/issues + +# This is the list of locale categories, beyond LC_MESSAGES, for which the +# message catalogs shall be used. It is usually empty. +EXTRA_LOCALE_CATEGORIES = + +# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' +# context. Possible values are "yes" and "no". Set this to yes if the +# package uses functions taking also a message context, like pgettext(), or +# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. +USE_MSGCTXT = no + +# These options get passed to msgmerge. +# Useful options are in particular: +# --previous to keep previous msgids of translated messages, +# --quiet to reduce the verbosity. +MSGMERGE_OPTIONS = + +# These options get passed to msginit. +# If you want to disable line wrapping when writing PO files, add +# --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and +# MSGINIT_OPTIONS. +MSGINIT_OPTIONS = + +# This tells whether or not to regenerate a PO file when $(DOMAIN).pot +# has changed. Possible values are "yes" and "no". Set this to no if +# the POT file is checked in the repository and the version control +# program ignores timestamps. +PO_DEPENDS_ON_POT = yes + +# This tells whether or not to forcibly update $(DOMAIN).pot and +# regenerate PO files on "make dist". Possible values are "yes" and +# "no". Set this to no if the POT file and PO files are maintained +# externally. +DIST_DEPENDS_ON_UPDATE_PO = yes diff -Nru rifiuti2-0.6.1/po/POTFILES.in rifiuti2-0.7.0/po/POTFILES.in --- rifiuti2-0.6.1/po/POTFILES.in 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/po/POTFILES.in 2019-05-09 01:26:08.000000000 +0000 @@ -1,3 +1,4 @@ src/rifiuti-vista.c src/rifiuti.c src/utils.c +src/utils-win.c diff -Nru rifiuti2-0.6.1/po/zh_HK.po rifiuti2-0.7.0/po/zh_HK.po --- rifiuti2-0.6.1/po/zh_HK.po 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/po/zh_HK.po 2019-05-09 01:26:08.000000000 +0000 @@ -1,393 +1,466 @@ -# Traditional Chinese (Hong Kong) translation of rifiuti. -# Copyright (C) 2015 Abel Cheung +# Traditional Chinese (Hong Kong) translation of rifiuti2. +# Copyright (C) 2015-2019 Abel Cheung # This file is distributed under the same license as the rifiuti2 package. -# Abel Cheung , 2015. +# Abel Cheung , 2015-2019. # msgid "" msgstr "" -"Project-Id-Version: rifiuti2 0.6.0\n" -"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?" -"product=glib&keywords=I18N+L10N&component=general\n" -"POT-Creation-Date: 2015-05-13 21:00+0800\n" -"PO-Revision-Date: 2015-05-13 20:58China Standard Time\n" +"Project-Id-Version: rifiuti2 0.7.0\n" +"Report-Msgid-Bugs-To: https://github.com/abelcheung/rifiuti2/issues\n" +"POT-Creation-Date: 2019-05-09 06:35+0800\n" +"PO-Revision-Date: 2019-05-01 20:44+0800\n" "Last-Translator: Abel Cheung \n" "Language-Team: Chinese (Hong Kong)\n" -"Language: Chinese (Hong Kong)\n" +"Language: zh_HK\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: ../../src/rifiuti-vista.c:57 ../../src/rifiuti.c:68 -msgid "Write output to FILE" -msgstr "輸出至 <檔案>" +#: src/rifiuti-vista.c:78 +#, c-format +msgid "%s(): failed to retrieve file content for '%s': %s" +msgstr "%s(): 無法讀取檔案 ‘%s’ 的內容:%s" -#: ../../src/rifiuti-vista.c:57 ../../src/rifiuti.c:69 -msgid "FILE" -msgstr "檔案" +#: src/rifiuti-vista.c:91 +msgid "File is truncated, or probably not a $Recycle.bin index file." +msgstr "檔案被截短了,又或者根本不是 $Recycle.bin 索引檔案。" -#: ../../src/rifiuti-vista.c:59 ../../src/rifiuti.c:71 -msgid "Output in XML format instead of tab-delimited values" -msgstr "輸出改為 XML 模式,而非 Tab 字元分隔的文字模式" +#: src/rifiuti-vista.c:111 src/rifiuti-vista.c:129 +msgid "Index file expected size and real size do not match." +msgstr "索引檔實際大小和預期不符。" -#: ../../src/rifiuti-vista.c:61 ../../src/rifiuti.c:76 -msgid "Present deletion time in time zone of local system (default is UTC)" -msgstr "以本系統的時區顯示檔案刪除時間 (預設為 UTC)" +#: src/rifiuti-vista.c:137 +msgid "Unsupported file version, or probably not a $Recycle.bin index file." +msgstr "檔案版本不支援,又或者根本不是 $Recycle.bin 索引檔。" -#: ../../src/rifiuti-vista.c:64 ../../src/rifiuti.c:79 -msgid "Print version information and exit" -msgstr "顯示版本資料並離開" +#: src/rifiuti-vista.c:215 +#, c-format +msgid "(Record %s) Error converting unicode path to UTF-8." +msgstr "(紀錄 %s) 無法將路徑轉換為 UTF-8 編碼。" -#: ../../src/rifiuti-vista.c:66 -msgid "$Recycle.bin folder or file name" -msgstr "$Recycle.bin 目錄或檔案名稱" +#: src/rifiuti-vista.c:241 src/rifiuti.c:325 +#, c-format +msgid "File '%s' fails validation." +msgstr "檔案 ‘%s’ 不通過檢驗。" -#: ../../src/rifiuti-vista.c:73 ../../src/rifiuti.c:88 -msgid "String to use as delimiter (TAB by default)" -msgstr "作為區隔欄位用的字串 (預設為 TAB)" +#. TRANSLATOR: appears in help text short summary +#: src/rifiuti-vista.c:303 +msgid "DIR_OR_FILE" +msgstr "目錄或檔案" -#: ../../src/rifiuti-vista.c:73 ../../src/rifiuti.c:88 -msgid "STRING" -msgstr "字串" +#: src/rifiuti-vista.c:305 +msgid "" +"Parse index files in C:\\$Recycle.bin style folder and dump recycle bin " +"data. Can also dump a single index file." +msgstr "" +"解析 C:\\$Recycle.bin 形式的目錄裏面的索引檔,並顯示其中的回收筒資料。也可以" +"解析單一的索引檔。" -#: ../../src/rifiuti-vista.c:75 ../../src/rifiuti.c:90 -msgid "Don't show header info" -msgstr "不顯示檔頭資料" +#: src/rifiuti-vista.c:328 +msgid "No valid recycle bin index file found." +msgstr "找不到合符格式的回收筒索引檔。" -#: ../../src/rifiuti-vista.c:77 ../../src/rifiuti.c:92 -msgid "Always display result in UTF-8 encoding" -msgstr "輸出結果永遠用 UTF-8 編碼顯示" +#: src/rifiuti-vista.c:355 +msgid "" +"Index files come from multiple versions of Windows. Please check each file " +"independently." +msgstr "索引檔來自多個不同的 Windows 版本。請每個檔案獨立進行檢測。" -#: ../../src/rifiuti-vista.c:99 -#, c-format -msgid "File is truncated, or probably not a $Recycle.bin index file.\n" -msgstr "檔案被截短了,又或者根本不是 $Recycle.bin 索引檔案。\n" +#: src/rifiuti-vista.c:404 src/rifiuti.c:456 +msgid "" +"Some entries could not be presented as correct unicode path. The concerned " +"characters are displayed in escaped unicode sequences." +msgstr "" +"某些項目無法顯示為正常的 unicode 路徑。有關字元會使用 unicode 碼點顯示。" -#. TRANSLATOR COMMENT: the variable is function name -#: ../../src/rifiuti-vista.c:109 ../../src/rifiuti.c:125 +#: src/rifiuti.c:84 #, c-format -msgid "%s(): fread() failed when reading version info" -msgstr "%s(): 當讀取版本資料時,fread() 發生錯誤" +msgid "Error opening file '%s' for reading: %s" +msgstr "無法開啟檔案 ‘%s’ 讀取內容:%s" -#. TRANSLATOR COMMENT: the variable is function name -#: ../../src/rifiuti-vista.c:132 +#. TRANSLATOR COMMENT: file size must be at least 20 bytes +#: src/rifiuti.c:95 #, c-format -msgid "%s(): fread() failed when reading file name length" -msgstr "%s(): 當讀取檔案名稱長度時,fread() 發生錯誤" +msgid "File size less than minimum allowed (%d bytes)" +msgstr "檔案大小小於下限 (%d 位元組)" -#: ../../src/rifiuti-vista.c:146 -#, c-format +#: src/rifiuti.c:126 src/rifiuti.c:163 +msgid "Unsupported file version, or probably not an INFO2 file at all." +msgstr "檔案版本不支援,又或者根本不是 INFO2 檔案。" + +#: src/rifiuti.c:134 msgid "" -"File is not supported, or it is probably not a $Recycle.bin index file.\n" -msgstr "檔案格式不支援,又或者根本不是 $Recycle.bin 索引檔案。\n" +"This INFO2 file was produced on a legacy system without Unicode file name " +"(Windows ME or earlier). Please specify codepage of concerned system with '-" +"l' or '--legacy-filename' option." +msgstr "" +"這個 INFO2 檔來自沒有 Unicode 檔名支援的古老 Windows (ME 或更早)。請用 ‘-l’ " +"或 ‘--legacy-filename’ 選項指定原本的系統的編碼。" -#: ../../src/rifiuti-vista.c:153 -#, c-format -msgid "Index file expected size and real size do not match.\n" -msgstr "索引檔實際大小和預期不符。\n" +#. TRANSLATOR COMMENT: can choose example from YOUR language & code page +#: src/rifiuti.c:140 +msgid "" +"For example, if recycle bin is expected to come from West European versions " +"of Windows, use '-l CP1252' option; or in case of Japanese Windows, use '-l " +"CP932'." +msgstr "" +"舉個例子,如果預期回收筒來自繁體中文 Windows,請使用 ‘-l CP950’ 選項;如果是" +"簡體的,則用 ‘-l CP936’。" -#: ../../src/rifiuti-vista.c:211 +#: src/rifiuti.c:216 #, c-format -msgid "Error converting file name from %s encoding to UTF-8 encoding: %s" -msgstr "無法將檔案名稱從 %s 編碼轉換為 UTF-8 編碼:%s" +msgid "Invalid drive number (0x%X) for record %u." +msgstr "第 %2$u 項紀錄中的磁碟號碼 (0x%1$X) 不合法。" -#: ../../src/rifiuti-vista.c:234 ../../src/rifiuti.c:466 +#: src/rifiuti.c:252 #, c-format -msgid "Error getting metadata of file '%s': %s\n" -msgstr "無法獲取 ‘%s’ 檔案的相關資料:%s\n" +msgid "(Record %u) Error converting legacy path to UTF-8." +msgstr "(紀錄 %u) 無法將舊式路徑轉換為 UTF-8 編碼。" -#: ../../src/rifiuti-vista.c:241 ../../src/rifiuti.c:473 +#: src/rifiuti.c:272 #, c-format -msgid "Error opening file '%s' for reading: %s\n" -msgstr "無法開啟檔案 ‘%s’ 讀取內容:%s\n" +msgid "(Record %u) Error converting unicode path to UTF-8." +msgstr "(紀錄 %u) 無法將 unicode 路徑轉換為 UTF-8 編碼。" -#: ../../src/rifiuti-vista.c:248 +#: src/rifiuti.c:359 #, c-format -msgid "File '%s' fails validation.\n" -msgstr "檔案 ‘%s’ 不通過檢驗。\n" +msgid "Failed to read record at position %li: %s" +msgstr "無法在位置 %li 讀取紀錄:%s" -#. -#. * TRANSLATOR COMMENT: 1st parameter is function name, -#. * 2nd is file name, 3rd is error message -#. -#: ../../src/rifiuti-vista.c:261 +#: src/rifiuti.c:365 #, c-format -msgid "%s(): fread() failed when reading content of file '%s': %s\n" -msgstr "%s(): 讀取檔案 ‘%s’ 內容時,fread() 失敗:%s\n" +msgid "Premature end of file, last record (%zu bytes) discarded" +msgstr "檔案被截短了,最後一筆紀錄 (%zu 位元組) 被無視" -#. VERY wrong if reaching here. Version info has already been filtered once -#: ../../src/rifiuti-vista.c:283 -#, c-format -msgid "Version info for '%s' still wrong despite file validation." -msgstr "即使通過檢驗後,‘%s’ 的版本依然是錯誤的。" +#. TRANSLATOR: appears in help text short summary +#: src/rifiuti.c:385 +msgid "INFO2" +msgstr "INFO2" -#: ../../src/rifiuti-vista.c:315 -#, c-format -msgid "Error opening directory '%s': %s\n" -msgstr "無法開啟 ‘%s’ 目錄:%s\n" +#: src/rifiuti.c:387 +msgid "Parse INFO2 file and dump recycle bin data." +msgstr "分析 INFO2 檔案並顯示資源回收筒的內容。" -#: ../../src/rifiuti-vista.c:415 -msgid "DIR_OR_FILE" -msgstr "目錄或檔案" +#: src/rifiuti.c:417 +msgid "Recycle bin file has no valid record.\n" +msgstr "回收筒中沒有任何刪檔紀錄。\n" -#: ../../src/rifiuti-vista.c:417 +#: src/rifiuti.c:451 +#, c-format msgid "" -"Parse index files in C:\\$Recycle.bin style folder and dump recycle bin " -"data. Can also dump a single index file." +"Some entries could not be interpreted in %s encoding. The concerned " +"characters are displayed in hex value instead. Very likely the (localised) " +"Windows generating the recycle bin artifact does not use specified codepage." msgstr "" -"解析 C:\\$Recycle.bin 形式的目錄裏面的索引檔,並顯示其中的回收筒資料。也可以" -"解析單一的索引檔。" - -#: ../../src/rifiuti-vista.c:421 ../../src/rifiuti.c:303 -#, c-format -msgid "Report bugs to %s" -msgstr "如程式有問題,請在 %s 報告" - -#: ../../src/rifiuti-vista.c:427 ../../src/rifiuti.c:309 -msgid "Plain text output options:" -msgstr "文字模式輸出選項:" +"某些項目無法用 %s 編碼表示;受影響的字元會改用 16 進制數值顯示。很有可能產生" +"該回收筒檔案的 (本地化) Windows 並不是使用那個編碼。" -#: ../../src/rifiuti-vista.c:428 ../../src/rifiuti.c:310 -msgid "Show plain text output options" -msgstr "顯示和文字模式輸出有關的選項" +#: src/utils.c:64 +msgid "Windows 95" +msgstr "Windows 95" + +#: src/utils.c:65 +msgid "Windows NT 4.0" +msgstr "Windows NT 4.0" + +#: src/utils.c:66 +msgid "Windows 98" +msgstr "Windows 98" + +#: src/utils.c:67 +msgid "Windows ME" +msgstr "Windows ME" + +#: src/utils.c:68 +msgid "Windows 2000" +msgstr "Windows 2000" + +#: src/utils.c:69 +msgid "Windows XP or 2003" +msgstr "Windows XP 或 2003" + +#: src/utils.c:70 +msgid "Windows 2000, XP or 2003" +msgstr "Windows 2000、XP 或 2003" + +#: src/utils.c:71 +msgid "Windows Vista - 8.1" +msgstr "Windows Vista 至 8.1" + +#: src/utils.c:72 +msgid "Windows 10 or above" +msgstr "Windows 10 或以上" -#: ../../src/rifiuti-vista.c:468 ../../src/rifiuti.c:354 -#, c-format -msgid "Error parsing options: %s\n" -msgstr "無法解析選項:%s\n" +#: src/utils.c:90 +msgid "String to use as delimiter (TAB by default)" +msgstr "作為區隔欄位用的字串 (預設為 TAB)" -#: ../../src/rifiuti-vista.c:482 -#, c-format -msgid "" -"Must specify exactly one directory containing $Recycle.bin index files, or " -"one such index file, as argument.\n" -"\n" -msgstr "" -"選項中必須指定剛好一個載有 $Recycle.bin 索引檔案的目錄,或者剛好一個這種檔" -"案,不多不少。\n" -"\n" +#: src/utils.c:90 +msgid "STRING" +msgstr "字串" -#: ../../src/rifiuti-vista.c:485 ../../src/rifiuti.c:369 -#, c-format -msgid "Run program with '-?' option for more info.\n" -msgstr "有關詳情請在執行程式時加上 ‘-?’ 選項。\n" +#: src/utils.c:95 +msgid "Don't show column header and metadata" +msgstr "不顯示欄位標題和中繼資料" + +#: src/utils.c:100 +msgid "(This option is deprecated)" +msgstr "(此選項已經被廢置)" -#: ../../src/rifiuti-vista.c:494 ../../src/rifiuti.c:378 -#, c-format -msgid "Error opening file '%s' for writing: %s\n" -msgstr "無法開啟檔案 ‘%s’ 來寫入資料:%s\n" +#: src/utils.c:109 +msgid "Write output to FILE" +msgstr "輸出至 <檔案>" -#: ../../src/rifiuti-vista.c:507 ../../src/rifiuti.c:391 -#, c-format -msgid "Plain text format options can not be used in XML mode.\n" -msgstr "文字模式的選項不可以配合 XML 模式使用。\n" +#: src/utils.c:109 +msgid "FILE" +msgstr "檔案" -#: ../../src/rifiuti-vista.c:544 ../../src/rifiuti.c:454 -#, c-format -msgid "'%s' does not exist.\n" -msgstr "‘%s’ 不存在。\n" +#: src/utils.c:114 +msgid "Output in XML format instead of tab-delimited values" +msgstr "輸出改為 XML 模式,而非 Tab 字元分隔的文字模式" -#: ../../src/rifiuti-vista.c:557 -#, c-format -msgid "" -"No files with name pattern '%s' are found in directory. Probably not a " -"$Recycle.bin directory.\n" -msgstr "目錄中沒有名稱像 ‘%s’ 的檔案。可能根本不是 $Recycle.bin 目錄。\n" +#: src/utils.c:119 +msgid "Present deletion time in time zone of local system (default is UTC)" +msgstr "以本系統的時區顯示檔案刪除時間 (預設為 UTC)" -#: ../../src/rifiuti-vista.c:571 -#, c-format -msgid "'%s' is not a normal file or directory.\n" -msgstr "‘%s’ 不是一般檔案或目錄。\n" +#: src/utils.c:125 +msgid "Print version information and exit" +msgstr "顯示版本資料並離開" -#: ../../src/rifiuti-vista.c:581 -msgid "No valid recycle bin index file found.\n" -msgstr "找不到合符格式的回收筒索引檔。\n" +#: src/utils.c:130 +msgid "INFO2 file name" +msgstr "INFO2 檔案名稱" -#: ../../src/rifiuti.c:73 -msgid "Show legacy (8.3) filename if available and specify its CODEPAGE" -msgstr "如果可能的話,顯示舊式 (8.3) 檔案路徑,並指定該用哪個 <編碼頁> 處理" +#: src/utils.c:140 +msgid "Show legacy (8.3) path if available and specify its CODEPAGE" +msgstr "盡可能顯示舊式 (8.3) 檔案路徑,並指定該用哪個 <編碼頁> 處理" -#: ../../src/rifiuti.c:74 +#: src/utils.c:141 msgid "CODEPAGE" msgstr "編碼頁" -#: ../../src/rifiuti.c:81 -msgid "INFO2 file name" -msgstr "INFO2 檔案名稱" +#: src/utils.c:163 +msgid "Plain text format options can not be used in XML mode." +msgstr "文字模式的選項不可以配合 XML 模式使用。" -#: ../../src/rifiuti.c:115 -#, c-format -msgid "File is truncated, or probably not an INFO2 file.\n" -msgstr "檔案被截短了,又或者根本不是 INFO2 檔案。\n" +#: src/utils.c:251 +msgid "Multiple delimiter options disallowed." +msgstr "不允許使用多個區隔字串選項。" + +#: src/utils.c:272 +msgid "Multiple output destinations disallowed." +msgstr "不允許指定多個輸出檔。" + +#: src/utils.c:280 +msgid "Empty output filename disallowed." +msgstr "輸出檔名稱不允許空白" + +#: src/utils.c:286 +msgid "Output destinations already exists." +msgstr "輸出檔已經存在。" -#. TRANSLATOR COMMENT: the variable is function name -#: ../../src/rifiuti.c:136 +#: src/utils.c:300 #, c-format -msgid "%s(): fread() failed when reading recordsize" -msgstr "%s(): 當讀取 recordsize 時 fread() 失敗" +msgid "NOTE: Option '%s' is deprecated and ignored." +msgstr "‘%s’ 選項已經被廢置,不會生效。" -#: ../../src/rifiuti.c:150 ../../src/rifiuti.c:173 -msgid "Invalid record size for this version of INFO2" -msgstr "回收筒每項紀錄的長度和這個版本的 INFO2 應有的不符" +#: src/utils.c:320 +msgid "Multiple encoding options disallowed." +msgstr "不允許多次使用編碼選項。" -#: ../../src/rifiuti.c:155 +#: src/utils.c:328 +msgid "Empty encoding option disallowed." +msgstr "編碼名稱不允許空白" + +#: src/utils.c:339 src/utils.c:374 #, c-format msgid "" -"This INFO2 file was produced on a Windows 98. Please specify codepage of " -"concerned system with '-l' or '--legacy-filename' option.\n" -"\n" -msgstr "" -"這個 INFO2 檔來自 Windows 98。請用 ‘-l’ 或 ‘--legacy-filename’ 選項指定原本的" -"系統的編碼頁。\n" -"\n" +"'%s' can't possibly be a code page or compatible encoding used by localized " +"Windows." +msgstr "‘%s’ 不可能是任何語言的 Windows 會採用的編碼頁或者其相容編碼" -#. TRANSLATOR COMMENT: use suitable example from YOUR language & code page -#: ../../src/rifiuti.c:159 +#: src/utils.c:356 #, c-format msgid "" -"For example, if file name was expected to contain accented latin characters, " -"use '-l CP1252' option; or in case of Japanese characters, '-l CP932'.\n" +"'%s' encoding is not supported by glib library on this system. If iconv " +"program is present on system, use 'iconv -l' for a list of possible " +"alternatives; otherwise check out following site for a list of probable " +"encodings to use:\n" "\n" -"Code pages (or any other encodings) supported by 'iconv' can be used.\n" +"\t%s" msgstr "" -"舉個例子,如果預期檔名應該有正體中文,請使用 ‘-l CP950’ 選項;如果有簡體的," -"則是 ‘-l CP936’。\n" +"本系統的 glib 程式庫不支援 ‘%s’ 編碼。如果系統有 iconv 程式,可執行 ‘iconv -" +"l’ 列出可作為代替用的編碼;否則請瀏覽以下網站尋找可使用的編碼:\n" "\n" -"任何 ‘iconv’ 支援的編碼和編碼頁都可以使用。\n" +"\t%s" -#: ../../src/rifiuti.c:181 -#, c-format -msgid "File is not supported, or it is probably not an INFO2 file.\n" -msgstr "檔案格式不支援,又或者根本不是 INFO2 檔案。\n" +#: src/utils.c:399 +msgid "Must specify exactly one file or folder argument." +msgstr "必須指定剛好一個檔案或目錄作為參數。" -#: ../../src/rifiuti.c:218 +#: src/utils.c:617 +msgid "Converted path failed UTF-8 validation" +msgstr "轉換後的路徑不通過 UTF-8 檢驗" + +#: src/utils.c:657 src/utils.c:666 +msgid "Coordinated Universal Time (UTC)" +msgstr "協調世界時 (UTC)" + +#: src/utils.c:669 src/utils-win.c:209 src/utils-win.c:219 +msgid "(Failed to retrieve timezone name)" +msgstr "(無法獲取時區名稱)" + +#. TRANSLATOR COMMENT: argument is bug report webpage +#: src/utils.c:806 #, c-format -msgid "Invalid drive number (0x%X) for record %u." -msgstr "第 %2$u 項紀錄中的磁碟號碼 (0x%1$X) 不合法。" +msgid "Report bugs to %s" +msgstr "如程式有問題,請在 %s 報告" -#: ../../src/rifiuti.c:257 +#: src/utils.c:831 +msgid "Plain text output options:" +msgstr "文字模式輸出選項:" + +#: src/utils.c:832 +msgid "Show plain text output options" +msgstr "顯示和文字模式輸出有關的選項" + +#: src/utils.c:889 #, c-format -msgid "" -"Error converting file name from %s encoding to UTF-8 encoding for record %u: " -"%s" -msgstr "無法將第 %2$u 項紀錄的檔案名稱從 %1$s 編碼轉換為 UTF-8 編碼:%3$s" +msgid "Error parsing options: %s" +msgstr "無法解析選項:%s" -#: ../../src/rifiuti.c:301 -msgid "Parse INFO2 file and dump recycle bin data." -msgstr "分析 INFO2 檔案並顯示資源回收筒的內容。" +#: src/utils.c:971 +#, c-format +msgid "Error opening temp file for writing: %s" +msgstr "無法開啟暫存檔來寫入資料:%s" -#: ../../src/rifiuti.c:368 +#: src/utils.c:999 #, c-format -msgid "" -"Must specify exactly one INFO2 file as argument.\n" -"\n" -msgstr "" -"必須指定剛好一個 INFO2 檔作為參數。\n" -"\n" +msgid "Error opening directory '%s': %s" +msgstr "無法開啟 ‘%s’ 目錄:%s" -#: ../../src/rifiuti.c:404 +#: src/utils.c:1064 #, c-format -msgid "" -"'%s' is not a valid code page or encoding. Only those supported by 'iconv' " -"can be used.\n" -msgstr "‘%s’ 不是正確的編碼或編碼頁。只有 ‘iconv’ 支援的才可以使用。\n" +msgid "'%s' does not exist." +msgstr "‘%s’ 不存在。" -#: ../../src/rifiuti.c:408 +#: src/utils.c:1079 #, c-format -msgid "" -"Please visit following web page for a list closely resembling encodings " -"supported by rifiuti:\n" -"\n" -"\t%s\n" -"\n" -msgstr "" -"想獲取 rifiuti 支援的編碼,以下網頁載有近似的清單:\n" -"\n" -"\t%s\n" -"\n" +msgid "No files with name pattern '%s' are found in directory." +msgstr "目錄中沒有名稱像 ‘%s’ 的檔案。" -#: ../../src/rifiuti.c:414 +#: src/utils.c:1090 #, c-format -msgid "Please execute 'iconv -l' for list of supported encodings.\n" -msgstr "要獲取程式支援的編碼,請執行 ‘iconv -l’。\n" +msgid "'%s' is not a normal file or directory." +msgstr "‘%s’ 不是一般檔案或目錄。" -#: ../../src/rifiuti.c:460 +#: src/utils.c:1091 #, c-format -msgid "'%s' is not a normal file.\n" -msgstr "‘%s’ 不是一般檔案。\n" +msgid "'%s' is not a normal file." +msgstr "‘%s’ 不是一般檔案。" -#: ../../src/rifiuti.c:504 +#: src/utils.c:1112 +msgid "Supplied format or arguments not in UTF-8 encoding" +msgstr "所提供的格式或參數並非使用 UTF-8 編碼" + +#: src/utils.c:1130 #, c-format -msgid "Failed to read next record: %s" -msgstr "無法讀取下一項紀錄:%s" +msgid "Error converting output from UTF-8 to UTF-16: %s" +msgstr "無法將輸出內容從 UTF-8 轉換為 UTF-16:%s" -#: ../../src/utils.c:223 +#: src/utils.c:1182 #, c-format msgid "Recycle bin path: '%s'" msgstr "資源回收筒路徑:‘%s’" -#. TRANSLATOR COMMENT: Error when trying to determine recycle bin version -#: ../../src/utils.c:230 +#. TRANSLATOR COMMENT: Empty folder, no file avaiable for analysis +#: src/utils.c:1189 msgid "??? (empty folder)" msgstr "??? (空白目錄)" -#. TRANSLATOR COMMENT: Error when trying to determine recycle bin version -#: ../../src/utils.c:234 -msgid "??? (version inconsistent)" -msgstr "??? (版本不一致)" - -#: ../../src/utils.c:239 +#: src/utils.c:1193 #, c-format msgid "Version: %s" msgstr "版本:%s" -#: ../../src/utils.c:246 +#: src/utils.c:1200 #, c-format -msgid "Index%sDeleted Time%sGone?%sSize%sPath" -msgstr "索引碼%s回收時間%s已刪?%s大小%s路徑" +msgid "Total entries ever existed: %d" +msgstr "曾經回收的項目總數:%d" -#: ../../src/utils.c:250 +#: src/utils.c:1205 +msgid "OS detection failed" +msgstr "操作系統無法估計" + +#: src/utils.c:1207 #, c-format -msgid "Index%sDeleted Time%sSize%sPath" -msgstr "索引%s回收時間%s大小%s路徑" +msgid "OS Guess: %s" +msgstr "估計操作系統為:%s" -#: ../../src/utils.c:298 +#: src/utils.c:1230 #, c-format -msgid "Error formatting file deletion time for record index %s." -msgstr "無法整理紀錄 %s 的檔案刪除時間成為可顯示的形式。" +msgid "Time zone: %s [%s]" +msgstr "時區:%s [%s]" + +#. TRANSLATOR COMMENT: appears in column header +#: src/utils.c:1243 +msgid "Index" +msgstr "索引" + +#: src/utils.c:1243 +msgid "Deleted Time" +msgstr "回收時間" + +#: src/utils.c:1243 +msgid "Size" +msgstr "大小" + +#: src/utils.c:1243 +msgid "Path" +msgstr "路徑" -#: ../../src/utils.c:307 ../../src/utils.c:326 +#. TRANSLATOR COMMENT: appears in column header, means file is restored or purged +#: src/utils.c:1254 +msgid "Gone?" +msgstr "已刪?" + +#: src/utils.c:1327 msgid "(File name not representable in UTF-8 encoding)" msgstr "(檔案名稱無法用 UTF-8 編碼顯示)" -#: ../../src/utils.c:321 +#: src/utils.c:1338 src/utils.c:1368 #, c-format -msgid "Error converting file name from %s encoding to UTF-8 for index %s: %s" -msgstr "無法將紀錄 %2$s 的檔案名稱從 %1$s 編碼轉換為 UTF-8 編碼:%3$s" +msgid "Error formatting file deletion time for record index %s." +msgstr "無法將紀錄 %s 的檔案刪除時間顯示出來。" -#: ../../src/utils.c:337 +#: src/utils.c:1350 msgid "Yes" msgstr "是" -#: ../../src/utils.c:337 +#: src/utils.c:1350 msgid "No" msgstr "否" -#: ../../src/utils.c:350 +#. TRANSLATOR COMMENT: argument is system error message +#: src/utils.c:1448 #, c-format -msgid "Error converting path name to display for record %s: %s" -msgstr "無法將紀錄 %s 的檔案名稱轉換為可顯示的內容:%s" +msgid "Error moving output data to desinated file: %s" +msgstr "無法將輸出檔案移動到指定位置:%s" -#: ../../src/utils.c:354 -msgid "(File name not representable in current language)" -msgstr "(檔案名稱無法用目前語言表示)" +#. TRANSLATOR COMMENT: argument is temp file location +#: src/utils.c:1453 +#, c-format +msgid "Output content is left in '%s'." +msgstr "輸出內容留在 ‘%s’。" -#: ../../src/utils.c:389 +#. TRANSLATOR COMMENT: %s is software name +#: src/utils.c:1464 #, c-format msgid "%s is distributed under the BSD 3-Clause License.\n" -msgstr "%s 根據 3-clause BSD 授權條款發行。\n" +msgstr "%s 根據 3-clause BSD 使用條款發行。\n" #. TRANSLATOR COMMENT: 1st argument is software name, 2nd is official URL -#: ../../src/utils.c:392 +#: src/utils.c:1467 #, c-format msgid "" "Information about %s can be found on\n" @@ -398,42 +471,95 @@ "\n" "\t%s\n" -#: ../../src/utils.c:450 +#: src/utils-win.c:53 msgid "This is a command line application" msgstr "這個是命令提示字元模式下的應用程式" +#: src/utils-win.c:244 +#, c-format +msgid "Failed to get current user name: %s" +msgstr "無法獲取目前使用者的名稱:%s" + +#: src/utils-win.c:257 src/utils-win.c:274 +#, c-format +msgid "LookupAccountName() failed: %s" +msgstr "LookupAccountName() 失敗:%s" + +#: src/utils-win.c:311 +#, c-format +msgid "AuthzInitializeResourceManager() failed: %s" +msgstr "AuthzInitializeResourceManager() 失敗:%s" + +#: src/utils-win.c:322 +#, c-format +msgid "Failed to retrieve Discretionary ACL info for '%s': %s" +msgstr "無法獲取檔案 ‘%s’ 的 Discretionary ACL 內容:%s" + +#: src/utils-win.c:331 +#, c-format +msgid "AuthzInitializeContextFromSid() failed: %s" +msgstr "AuthzInitializeContextFromSid() 失敗:%s" + +#: src/utils-win.c:341 +#, c-format +msgid "AuthzAccessCheck() failed: %s" +msgstr "AuthzAccessCheck() 失敗:%s" + +#: src/utils-win.c:356 +msgid "Error listing directory: Insufficient permission." +msgstr "無法列出目錄:權限不足。" + +#~ msgid "Error getting metadata of file '%s': %s" +#~ msgstr "無法獲取 ‘%s’ 檔案的相關資料:%s" + +#~ msgid "%s(): fread() failed when reading version info from '%s'" +#~ msgstr "%s(): 從 ‘%s’ 讀取版本資料時,fread() 發生錯誤" + +#~ msgid "%s(): fread() failed when reading recordsize from '%s'" +#~ msgstr "%s(): 從 ‘%s’ 讀取 recordsize 時,fread() 發生錯誤" + +#~ msgid "??? (version inconsistent)" +#~ msgstr "??? (版本不一致)" + +#~ msgid "Index%sDeleted Time%sGone?%sSize%sPath" +#~ msgstr "索引碼%s回收時間%s已刪?%s大小%s路徑" + +#~ msgid "Index%sDeleted Time%sSize%sPath" +#~ msgstr "索引檔%s回收時間%s大小%s路徑" + #~ msgid "" -#~ "Output in XML format instead of tab-delimited values (plain text options " -#~ "disallowed in this case)" +#~ "Must specify exactly one directory containing $Recycle.bin index files, " +#~ "or one such index file as argument." #~ msgstr "" -#~ "輸出改為 XML 模式,而非 Tab 字元分隔的文字模式 (這情況下不允許使用文字模式" -#~ "的選項)" +#~ "選項中必須指定剛好一個載有 $Recycle.bin 索引檔案的目錄,或者剛好一個這種檔" +#~ "案,不多不少。" -#~ msgid "File names" -#~ msgstr "檔名" +#~ msgid "Run program without any option for more info." +#~ msgstr "請在執行程式時不加任何選項顯示詳情。" -#~ msgid "Error converting path name to display: %s" -#~ msgstr "無法將檔案名稱轉換為可顯示的內容:%s" +#~ msgid "" +#~ "Error converting file name from %s encoding to UTF-8 for index %s: %s" +#~ msgstr "無法將紀錄 %2$s 的檔案名稱從 %1$s 編碼轉換為 UTF-8 編碼:%3$s" -#~ msgid "Recycle bin file/dir: '%s'" -#~ msgstr "資源回收筒檔案/目錄:‘%s’" +#~ msgid "" +#~ "File is not supported, or it is probably not a $Recycle.bin index file.\n" +#~ msgstr "檔案格式不支援,又或者根本不是 $Recycle.bin 索引檔案。\n" -#~ msgid "Error converting file name to UTF-8 encoding: %s" -#~ msgstr "無法將檔案名稱轉換為 UTF-8 編碼:%s" +#~ msgid "Plain text format options can not be used in XML mode.\n" +#~ msgstr "文字模式的選項不可以配合 XML 模式使用。\n" -#~ msgid "Index file '%s' has incorrect size" -#~ msgstr "索引檔 ‘%s’ 的大小不符合規定" +#~ msgid "Invalid record size for this version of INFO2" +#~ msgstr "回收筒每項紀錄的長度和這個版本的 INFO2 應有的不符" -#~ msgid "Error formatting file deletion time for file '%s'." -#~ msgstr "無法整理索引檔 ‘%s’ 中的檔案刪除時間成為可顯示的形式。" +#~ msgid "Error converting path name to display for record %s: %s" +#~ msgstr "無法將紀錄 %s 的檔案名稱轉換為可顯示的內容:%s" #~ msgid "" -#~ "The assumed file name character set when no unicode file name is present " -#~ "in INFO2 record (mandatory if INFO2 file is created by Win98, ignored " -#~ "otherwise)" +#~ "Output in XML format instead of tab-delimited values (plain text options " +#~ "disallowed in this case)" #~ msgstr "" -#~ "當 INFO2 紀錄中沒有 unicode 檔名時,假設檔名來自何種編碼 (如果 INFO2 檔案" -#~ "來自 Win98,此選項是必要的,反之則會被忽略)" +#~ "輸出改為 XML 模式,而非 Tab 字元分隔的文字模式 (這情況下不允許使用文字模式" +#~ "的選項)" -#~ msgid "ENC" -#~ msgstr "編碼" +#~ msgid "Error formatting file deletion time for file '%s'." +#~ msgstr "無法整理索引檔 ‘%s’ 中的檔案刪除時間成為可顯示的形式。" diff -Nru rifiuti2-0.6.1/README.md rifiuti2-0.7.0/README.md --- rifiuti2-0.6.1/README.md 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/README.md 2019-05-09 01:26:08.000000000 +0000 @@ -1,85 +1,115 @@ -`Rifiuti2` is a rewrite of [rifiuti][1], a great tool from Foundstone -folks for analyzing Windows Recycle Bin INFO2 file. Analysis of +| Appveyor | Travis | +|----------|--------| +| [![Appveyor status](https://ci.appveyor.com/api/projects/status/github/abelcheung/rifiuti2?svg=true&branch=master)](https://ci.appveyor.com/project/abelcheung/rifiuti2) | [![Travis status](https://travis-ci.org/abelcheung/rifiuti2.svg?branch=master)](https://travis-ci.org/abelcheung/rifiuti2) | + +## Introduction + +`Rifiuti2` is a for analyzing Windows Recycle Bin INFO2 file. Analysis of Windows Recycle Bin is usually carried out during Windows computer forensics. `Rifiuti2` can extract file deletion time, original path and size of deleted files and whether the trashed files have been -permanently removed. It was extended to cover more functionalities, such as: +permanently removed. -- [x] Handles recycle bin up to Windows 10 - - [x] Different recycle bin format since Vista - - [x] 64-bit file size support -- [x] Supports all localized versions of Windows — - both newer Unicode-based ones and legacy ones, as old as Win98 -- [x] Supports output in XML format as well as original tab-delimited text +For those interested in what it does, and what functionality it +provides, please [check out official site][1] for more info. +Latest features and changes can be found in [NEWS file](NEWS.md). + +[1]: https://abelcheung.github.io/rifiuti2 + +## Special note for 0.7.0 +* Windows binaries will be automatically built from + [Appveyor](https://www.appveyor.com/) and published to Github. +* **Systems supporting UTF-8 encoding is mandatory, except on Windows console + (file output is also in UTF-8).** This shouldn't be problematic though, + as UTF-8 locale is pretty much standard for Linux and macOS these years. + On Windows front, there are already many featureful text editors + capable of opening UTF-8 unicode text files. +* As a result, `-8` option is obsolete and no more affects output in any way. -Latest features and changes can be found in [NEWS](NEWS.md) file. - -[1]: https://web.archive.org/web/20101121070625/http://www.foundstone.com/us/resources/proddesc/rifiuti.htm ## Usage -`rifiuti` is designed to be portable, and runs on command line environment. -Depending on relevant Windows recycle bin format, there are 2 binaries to choose -(most users would probably want first one): - -Program | Recycle bin from OS | Purpose ---------|---------------------|-------- -`rifiuti-vista`|Vista or above|Scans `\$Recycle.bin` style folder -`rifiuti` |Windows 98 to XP/2003|Reads `INFO2` file in `\RECYCLED` or `\RECYCLER` folder - -Run programs without any option for more detail. Here are some of the -more useful options: - - Option | Purpose --------:|:-------- --8 | Always print result in UTF-8 encoding --o | Output to file --x | Output XML instead of tab-separated fields --l | Display legacy (8.3) filenames and specify its codepage +`rifiuti2` is designed to be portable, and runs on command line environment. +Depending on relevant Windows recycle bin format, there are 2 binaries to +choose from (most users would want first one): + +Program | Recycle bin from OS | Purpose +---------------|---------------------|-------- +`rifiuti-vista`|Vista – Win10 | Scans `\$Recycle.bin` style folder +`rifiuti` |Win95 – XP/2003| Reads `INFO` or `INFO2` file in `\RECYCLED` or `\RECYCLER` folder + +Run programs without any option for more detail. Here are some more +frequently used options: + + Option | Purpose +:----------|:-------- +`-o `| Output to file +`-x` | Output XML instead of tab-separated fields +`-l ` | Display legacy (8.3) filenames and specify its codepage Please consult manpage (Unix) or README.html (bundled with Windows binaries) for complete options and detailed usage description. -#### Examples +### Examples -*
`rifiuti-vista.exe -x -z -o result.xml \case\S-1-2-3\`
-
Scan for index files under `\case\S-1-2-3\`, adjust all deletion time - for local time zone, and write XML output to `result.xml`
-*
`rifiuti -l CP932 -8 INFO2`
-
Assume INFO2 file is generated from Japanese Windows, and display - result on console in UTF-8 encoding
+* `rifiuti-vista.exe -x -z -o result.xml \case\S-1-2-3\` +> Scan for index files under `\case\S-1-2-3\`, adjust all deletion time +> for local time zone, and write XML output to `result.xml` +* `rifiuti -l CP932 -t "\n" INFO2` +> Assume INFO2 file is generated from Japanese Windows (codepage 932), +> and display each field line by line, instead of separated by tab ## Supported platform -It has been tested on Linux (as early as Ubuntu 8.04), Windows XP, -Windows 7, and recent FreeBSD, on both 32 and 64-bit intel CPU. +It has been tested on Linux, Windows 7 and FreeBSD. Some testing on big endian platforms are done with Qemu emulator. More compatibility fix for other architectures welcome. ## Download -Windows binaries, if applicable, would be officially provided +### Windows +Windows binaries are officially provided [on Github release page][6]. -On Linux side: -* DEB format packages are available officially on [Debian][7] -and [Ubuntu][8]. -* There are some third party RPM packages, such as from -[CERT Linux Forensics Tools Repository][9], which might work on CentOS, -RHEL and Fedora. -* [ArchAssault][10], a penetration testing derivative of Arch Linux, has -`rifiuti2` packaged since late 2014. - -Official [FreeBSD port][11] is available since 8.x. - -For platforms not listed above, users would need to compile program themselves. -[Instructions are provided](docs/Compile.md) on how to compile on Linux, -\*BSD and Windows. +Note that 0.6.1 version is the last version that can run on +Windows XP and 2003; upcoming versions would require Vista or above. + +### Linux +* DEB packages available officially on [Debian][7] and [Ubuntu][8], +hence also available on most (if not all) derivatives focusing on +security and forensics, such as (this is incomplete list): + * [Kali Linux][9] + * [Deft X Virtual Appliance][10] + * BackBox Linux +* RPM packages from [Linux Forensics Tools Repository (LiFTeR)][11] + can be used on Fedora, and very likely CentOS and RHEL. +* [ArchStrike (formerly ArchAssault)][12], a penetration testing + derivative of Arch Linux, has `rifiuti2` packaged since late 2014. + +### FreeBSD +Official [FreeBSD port][13] is available since 8.4. + +### Others (Compile from source) +For OS where `rifiuti2` is not readily available, it is always +possible to compile from source. + +`rifiuti2` follows the usual `autotools` based procedure: +```sh +./configure && make check && make install +``` +Please [refer to wiki page][14] for more detail. + +## License + +`rifiuti2` is released under BSD license. Please refer to +[license file](docs/LICENSE.md) for more detail. [6]: https://github.com/abelcheung/rifiuti2/releases [7]: https://packages.debian.org/search?keywords=rifiuti2 [8]: http://packages.ubuntu.com/search?keywords=rifiuti2 -[9]: https://forensics.cert.org/ -[10]: https://archassault.org/packages/?q=rifiuti2 -[11]: http://portsmon.freebsd.org/portoverview.py?category=security&portname=rifiuti2 - +[9]: https://pkg.kali.org/pkg/rifiuti2 +[10]: http://www.deftlinux.net/package-list/deft-x-va/ +[11]: https://forensics.cert.org/ByPackage/rifiuti2.html +[12]: https://archstrike.org/packages/rifiuti2 +[13]: https://www.freebsd.org/cgi/ports.cgi?query=rifiuti2 +[14]: https://github.com/abelcheung/rifiuti2/wiki/Compile-From-Source \ No newline at end of file diff -Nru rifiuti2-0.6.1/src/Makefile.am rifiuti2-0.7.0/src/Makefile.am --- rifiuti2-0.6.1/src/Makefile.am 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/src/Makefile.am 2019-05-09 01:26:08.000000000 +0000 @@ -1,33 +1,50 @@ +# vim: set sw=4 ts=4 noexpandtab : + AM_CPPFLAGS = \ - -I$(srcdir) \ - $(GLIB_CFLAGS) \ - -DLOCALEDIR=\"$(localedir)\" \ - -DG_LOG_DOMAIN=\"$(PACKAGE)\" + -I$(srcdir) \ + $(GLIB_CFLAGS) \ + -DLOCALEDIR=\"$(localedir)\" \ + -DLOCALEDIR_PORTABLE=\"$(LOCALEDIR_PORTABLE)\" \ + -DG_LOG_DOMAIN=\"$(PACKAGE)\" + +if STATIC_BUILD +AM_LDFLAGS = -static +endif bin_PROGRAMS = rifiuti rifiuti-vista man_MANS = rifiuti.1 EXTRA_DIST = rifiuti.1 +COMMON_SOURCES = \ + utils.c \ + utils.h \ + $(NULL) + rifiuti_SOURCES = \ - rifiuti.c \ - rifiuti.h \ - utils.c \ - utils.h + rifiuti.c \ + rifiuti.h \ + $(COMMON_SOURCES) rifiuti_LDADD = $(GLIB_LIBS) rifiuti_vista_SOURCES = \ - rifiuti-vista.c \ - rifiuti-vista.h \ - utils.c \ - utils.h + rifiuti-vista.c \ + rifiuti-vista.h \ + $(COMMON_SOURCES) rifiuti_vista_LDADD = $(GLIB_LIBS) if OS_WINDOWS -rifiuti_LDADD += -luser32 -rifiuti_vista_LDADD += -luser32 +WIN_SOURCES = \ + utils-win.c \ + utils-win.h \ + $(NULL) + +rifiuti_SOURCES += $(WIN_SOURCES) +rifiuti_vista_SOURCES += $(WIN_SOURCES) +rifiuti_LDADD += -lauthz +rifiuti_vista_LDADD += -lauthz endif install-data-hook: @@ -36,4 +53,4 @@ uninstall-hook: -rm -f $(DESTDIR)$(mandir)/man1/rifiuti-vista.1 --include $(top_srcdir)/git.mk +MAINTAINERCLEANFILES = $(srcdir)/Makefile.in diff -Nru rifiuti2-0.6.1/src/rifiuti.1 rifiuti2-0.7.0/src/rifiuti.1 --- rifiuti2-0.6.1/src/rifiuti.1 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/src/rifiuti.1 2019-05-09 01:26:08.000000000 +0000 @@ -11,34 +11,38 @@ .\" see groff_www(7) .BCL black #bbffee blue blue #302226 -.TH "RIFIUTI2 0.6.1" "1" +.HEAD "" + +.TH "RIFIUTI2 0.7.0" "1" .SH INSTALLATION ON WINDOWS -\fBRifiuti2\fP is designed to run as portable Windows command line -application, and no installation is required. Copy the binaries -corresponding to your system (\fCx64\\\fP for 64 bit systems, -\fCx86\\\fP for 32 bit) to any folder of your choice and they are -ready for use. Read sections below on how to use the programs. +\fBRifiuti2\fP is designed to run as portable Windows +\fIcommand line\fP +application, and no installation is required. Extract zip file onto +any folder of your choice and programs are ready for use. +Read sections below on how to use them. .SS Translation By default output messages of rifiuti2 are in English. -Optionally one can copy -.nh -\fCrifiuti\-l10n\fP -.hy -folder to the same location the binaries reside in, and set -\fCLANGUAGE\fP environment variable to appropriate value to enable -translations. Supported language codes are the same as existing -subdirectory names under +The program shall be able to pick up translation automatically +if user has correct Region / Language setting. This setting +is under "Formats" tab of "Region and Language" (name may vary +for different versions of Windows). + +Optionally one may use \fCLANGUAGE\fP environment variable to +force using certain translation. Supported language codes are +the same as existing subdirectory names under .nh -\fCrifiuti\-l10n\fP +\fCrifiuti2\-l10n\fP .hy folder. For example, assuming folder .nh -\fCrifiuti\-l10n\\fr\\\fP +\fCrifiuti2\-l10n\\fr\\\fP .hy -exists, running following in Windows \fCcmd\fP would enable French +exists, running following in Windows console would enable French translation: .RS \fCset LANGUAGE=fr\fP @@ -54,14 +58,12 @@ ####CHANGELOG#### .PP -Changes for previous versions are available from -.RS -https://github.com/abelcheung/rifiuti2/blob/master/NEWS.md -.RE +.URL https://github.com/abelcheung/rifiuti2/blob/master/NEWS.md "Changes for previous versions" +are available on Github. . \} . el \{\ -.TH RIFIUTI2 "1" "May 2015" "0.6.1" "MS Windows recycle bin analysis tool" +.TH RIFIUTI2 "1" "May 2015" "0.7.0" "MS Windows recycle bin analysis tool" .SH NAME rifiuti2 \- MS Windows recycle bin analysis tool @@ -69,11 +71,16 @@ .SH SYNOPSIS .B rifiuti -.RB [ \-hvz ] +or +.B rifiuti-vista +.RB [ \-hv ] + +.B rifiuti .RB [ \-x " |" -.RB [ \-8n ] +.RB [ \-n ] .RB [ \-t .IR delim "]]" +.RB [ \-z ] .RB [ \-l .IR codepage ] .RB [ \-o @@ -81,11 +88,11 @@ .I filename .B rifiuti-vista -.RB [ \-hvz ] .RB [ \-x " |" -.RB [ \-8n ] +.RB [ \-n ] .RB [ \-t .IR delim "]]" +.RB [ \-z ] .RB [ \-o .IR outfile ] .I file_or_directory @@ -97,31 +104,33 @@ path and size of deleted files and whether the deleted files have been moved out from the recycle bin since they are trashed. .PP -Rifiuti2 supports a wide range of Windows versions, from Windows 98 to +Rifiuti2 supports a wide range of Windows versions, from Windows 95 to Windows 10. The command used for analysis depends on the version -of Windows producing the recycle bin (\fBnot the version of users\' +of Windows producing the recycle bin (\fBnot the version of users' system!\fP), which uses vastly different format before and after Vista: .PP -.IP \[bu] -\fCrifiuti-vista\fP: For Vista or later, which is located in +.TP +\fCrifiuti-vista\fP +For Vista or later, which is located in .nh \fC\\$Recycle.bin\\\fP\fI\fP\fC\\\fP. .hy Each deleted file has its own accompanied index file remembering the original path, file size and deletion time. If original file is permanentsly deleted, so is the index file. -.IP \[bu] -\fCrifiuti\fP: For Windows 98 to XP, -which uses a single index file named INFO2 under either +.TP +\fCrifiuti\fP +For Windows 95 to XP/2003, which uses a single index file named +\fCINFO2\fP (98 or above) or \fCINFO\fP (95 and NT4) under either .nh \fC\\RECYCLED\\\fP .hy -or +(FAT 16/32) or .nh \fC\\RECYCLER\\\fP\fI\fP\fC\\\fP .hy -(depending on filesystem). +(NTFS). This file keeps track record for deletion status and info for \fIall\fP deleted items, including those permanently removed or restored. @@ -131,32 +140,31 @@ program. \fB\-x\fP option instructs program to dump XML formatted content instead. .PP +\fBSince 0.7.0 version\fP, rifiuti2 output is in UTF-8 encoding only, +including the case of writing file under Windows. +.PP Index field has different meaning for pre-Vista and post-Vista versions. INFO2 has an index number for each of deletion item indicating the chronological order of items. For Vista version, it means the index file name instead, which matches pattern \(lq$I\fBxxxxxx\fP.\fI\fP\(rq, where \fBx\fP is random -alphanumeric character. +alphanumeric character, and \fI\fP matches the extension of +original deleted item. .PP Deleted time is represented in UTC time by default. Under tab-delimited -mode, the original date/time format is preserved, while in XML mode -ISO 8601 date/time format is used. -For example, 3PM at 2014 X\'mas represented in these modes would be +mode, date/time is presented in format recognized by spreadsheet +programs, while in XML mode ISO 8601 date/time format is used. +For example, 3PM at 2014 X'mas represented in these modes would be respectively: .RS \fC2014-12-25 15:00:00\fP -.RE -.RS +.br \fC2014-12-25T15:00:00Z\fP .RE -It would be easier for spreadsheet programs to interpret first format. .PP File size and file path are self-explanatory, but there are some -special notes. File size can mean the real size of deleted file, -or the cluster size it occupies on filesystem, depending on recycle -bin format. File path might not always be displayable on local system -because it might contain characters from other localized version -of Windows. +special issues to take care about. Refer to \fBCAVEATS\fP section +below for more detail. .SH OPTIONS .TP @@ -179,19 +187,20 @@ .RS \fBNote\fP: This option is mandatory if INFO2 file is created by -Windows 98. This option does not exist in \fCrifiuti-vista\fP. +Windows 95, 98 or ME, since recycle bins under these OS don't contain +Unicode file name. This option does not exist in \fCrifiuti-vista\fP. .RE .TP \fB\-z\fP, \fB\-\-localtime\fP Present deletion time in numeric time zone of local system running the program. By default, UTC time is displayed, which is the time -value recorded in index files. Using the X\'mas example above, the +value recorded in index files. Using the X'mas example above, the time for Berlin (without daylight saving time) would be \fC2014-12-25T16:00:00+0100\fP in ISO 8601 format. .RS -\fBNote\fP: It is possible to use any timezone of users\' choice -by setting $TZ environment variable, though not recommended. +\fBNote\fP: It is possible to use any timezone of users' choice +by setting \fC$TZ\fP environment variable, though not recommended. See \fBENVIRONMENT VARIABLE\fP section below. .RE @@ -199,15 +208,22 @@ PLAIN TEXT OUTPUT OPTIONS .TP \fB\-t\fP, \fB\-\-delimiter\fP=\fI\,STRING\/\fP -String to use as delimiter (TAB by default). Several escaped characters -are recognised: \\r (CARRIAGE RETURN), \\n (NEW LINE), \\t (TAB), -\\f (FORM FEED), \\v (VERTICAL TAB), \\e (ESCAPE) +String to use as delimiter (TAB by default). Other than normal +characters, several escape sequences are also recognised: +.br +\fC\\r\fP (carriage return) +.br +\fC\\n\fP (line feed) +.br +\fC\\t\fP (tab) +.br +\fC\\e\fP (escape) .TP \fB\-n\fP, \fB\-\-no\-heading\fP -Don\'t show recycle bin path name, version and header for each field +Don't show recycle bin path name, metadata and field headers .TP \fB\-8\fP, \fB\-\-always\-utf8\fP -Always display result in UTF\-8 encoding +(Option deprecated since 0.7.0 version) .PP .SS @@ -234,8 +250,8 @@ time for local time zone, and write XML output to result.xml .RE .TP -\fCrifiuti-vista \-n \-8 \\case\\S\-1\-2\-3\\\fP -Show tab-delimited result on screen in UTF-8 encoding without header +\fCrifiuti-vista \-n \\case\\S\-1\-2\-3\\\fP +Show tab-delimited result on screen without header and metadata .TP \fCrifiuti-vista -t '\\r\\n' \\case\\S\-1\-2\-3\\$IF96NJ3.rtf\fP Only analyse a single index file and print each field in its own line @@ -243,27 +259,33 @@ \fCrifiuti \-t ',' -o result.csv INFO2\fP Change tab-delimited result to comma-delimited and write to result.csv .TP -\fCrifiuti \-l CP1255 \-8 \-n INFO2\fP +\fCrifiuti \-l CP1255 \-n INFO2\fP .RS -Read INFO2 from Hebrew version of Windows, display 8.3 file names -on screen in UTF-8 encoding without header +Assuming INFO2 from Hebrew version of Windows, display 8.3 file names +without header and metadata .RE .SH ENVIRONMENT VARIABLES The following environment variables affect execution of program: .TP -\fBCHARSET\fP, \fBLC_CTYPE\fP +\fBLANG\fP / \fBLC_MESSAGES\fP / \fBLC_ALL\fP / \fBLANGUAGE\fP +.RS +Listed in order of increasing importance, these variables determine +the translation to use. They belong to the group of locale +environment variables. In general, these variables are already +properly set up on Unix-like systems, while unused on Windows. +Please consult relevant document of user's operating +system for more detail. +.RE +.TP +\fBLANG\fP / \fBLC_CTYPE\fP / \fBLC_ALL\fP .RS If recycle bin path contains non-ASCII character, these variables -affect how they are displayed. UTF-8 capable systems are recommended -to set -.nh -\fCCHARSET=UTF-8\fP -.hy -or use appropriate UTF-8 values for -\fCLC_CTYPE\fP explicitly, otherwise path might be displayed in -Universal Character Name sequences like \\u1234. +affect how they are displayed, in a manner similar to translation +related variables described above. However it is not recommended +to modify them, as since 0.7.0 version rifiuti2 no more +expects any environment using non UTF-8 encoding. .RE .TP \fBRIFIUTI_DEBUG\fP @@ -285,23 +307,40 @@ Linux would be \(lqAmerica/Los_Angeles\(rq. Please consult manual for your operating system for more info. .PP -Please see \fBBUGS\fP section below for problems when using this variable. +Please see \fBCAVEATS\fP section below for problems when using this variable. .RE .SH EXIT STATUS -Both programs return 0 on success, and >0 if error occurs. +Both programs return 0 on success, and greater than 0 if error occurs. .PP -However \fCrifiuti-vista\fP is more permissive: it still returns -success if \fIsome\fP (not all) of index files are invalid. +In particular, \fCrifiuti-vista\fP would exit with the latest non-zero +status when error is encountered in \fIany\fP of the index files. + +.TP +1 +Wrong command line argument +.TP +2 +Error when opening file or directory +.TP +3 +Recycle bin data fails basic validation +.TP +4 +Error when writing output to file +.TP +5 +User supplied wrong encoding for legacy path .SH HISTORY +.PP \fIRifiuti2\fP is a rewrite of \fIrifiuti\fP, a tool of identical purpose written by Foundstone which was later purchased by McAfee. Quoting from the original FoundStone page: .RS Many computer crime investigations require the reconstruction of a -subject\'s Recycle Bin. Since this analysis technique is executed +subject's Recycle Bin. Since this analysis technique is executed regularly, we researched the structure of the data found in the Recycle Bin repository files (INFO2 files). Rifiuti, the Italian word meaning "trash", was developed to examine the contents of the INFO2 @@ -311,39 +350,56 @@ .RE .PP -However, since the original rifiuti (last updated 2004) can\'t analyze +However, since the original rifiuti (last updated 2004) can't analyze recycle bin from any localized version of Windows (restricted to English), this rewrite effort is born to overcome the limitation. Later rifiuti2 was improved to add support for Vista format recycle bin, XML output and other extra features not available from original version. -.SH BUGS -In very special circumstance (which author can\'t reproduce now), +.SH CAVEATS +In very special circumstance (which author can't reproduce now), index file of certain deleted item can be corrupt, causing incorrect deleted file size to be stored. There is no way to report correct size. -This problem shouldn\'t happen after Vista though. -.PP -Handling of non-ASCII file argument is not satisfactory; it may not -work in certain case under MinGW bash. +This problem was only observed in Vista though, not any other versions +of Window. .PP Non-ASCII deleted item path name may not be always displayed -appropriately, especially on systems with non-UTF-8 locale (such as -Windows \fBcmd\fP, where output is restricted to ANSI codepages). -Storing UTF-8 result into file with \fB\-8\fP or \fB\-x\fP option -and then opening it with Unicode capable editor could be a solution. -.PP -The calculation of local time might not be correct. For example, -documentation of _tzset() function on Windows has this statement: +appropriately on console. Although great care is taken to +display path name as much as possible (resorting to escaped hex +.nh +\fC<\\XX>\fP +.hy +or escaped unicode +.nh +\fC<\\uXXXX>\fP +.hy +in case of invalid or invisible characters), the font used in console +might not be able to display all characters needed. Dumping result +into file and open with UTF-8 capable text editor is an option. +.PP +It is always better to use UTC time whenever possible, because +calculation of local time might not be correct, especially for non-US users. +Documentation of +. ie \n[www-html] \{\ +.URL https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/tzset "\fC_tzset()\fP function on Windows" +. \} +. el \{\ +\fC_tzset()\fP function on Windows +. \} +has this statement: .RS -The C run-time library assumes the United States\' rules for implementing +The C run-time library assumes the United States' rules for implementing the calculation of daylight saving time (DST). .RE -Therefore the time might not be correct in case the files inside -recycle bin are produced on Windows using other countries as region -settings. Besides, the difference between standard time and DST is -hardcoded to be one hour, which is incorrect for a few selected regions. -.PP -So it is always better to use UTC time whenever possible. +Since the difference between standard time and DST is hardcoded to be +one hour (which is incorrect for a few selected regions), the file +deletion time might not be correct for these regions when DST is in +effect. +.PP +File size can mean the real size of deleted file, or the cluster size +it occupies on filesystem, depending on recycle bin format. As a rule +of thumb, if all sizes of entries are multiples of 512, it can be assumed +the concerned sizes refer to cluster size. .SH REPORTING BUGS Report bugs to @@ -377,14 +433,13 @@ .URL http://odessa.sourceforge.net/ "Open Digital Evidence Search and Seizure Architecture project" , which contains the original rifiuti tool .PP -Forensics tools and other security related utilities -.URL http://www.mcafee.com/us/downloads/free-tools/index.aspx "originally written by FoundStone" -are now available under McAfee\'s own license +.URL https://www.blackbagtech.com/blog/2017/01/19/examining-the-windows-10-recycle-bin/ "Windows 10 Recycle Bin Index Structure" +, by BlackBag Technologies .PP -.URL http://me.abelcheung.org/wp-content/uploads/2007/09/vista-recycle-bin-sample.pdf "Vista recycle bin file structure" -, by Abel Cheung +.URL https://pdfs.semanticscholar.org/db62/a02a2f90c569200bf37ead369221e04393d8.pdf "$Recycle.Bin Forensics for Windows 7 and Windows Vista" +, by Timothy R. Leschke .PP -.URL http://www.csisite.net/downloads/INFO2.pdf "INFO2 recycle bin file example" +.URL http://www.cyber-ssct.com/resources/INFO2.pdf "INFO2 recycle bin file example" , by Steve Hailey . \} . el \{\ @@ -392,14 +447,14 @@ Open Digital Evidence Search and Seizure Architecture project, which contains the original rifiuti tool http://odessa.sourceforge.net/ .TP -Forensics tools and other security related utilities originally written by FoundStone are now available under McAfee\'s own license. -http://www.mcafee.com/us/downloads/free-tools/index.aspx +Windows 10 Recycle Bin Index Structure, by BlackBag Technologies +https://www.blackbagtech.com/blog/2017/01/19/examining-the-windows-10-recycle-bin/ .TP -Vista recycle bin file structure, by Abel Cheung -http://me.abelcheung.org/wp-content/uploads/2007/09/vista-recycle-bin-sample.pdf +$Recycle.Bin Forensics for Windows 7 and Windows Vista, by Timothy R. Leschke +https://pdfs.semanticscholar.org/db62/a02a2f90c569200bf37ead369221e04393d8.pdf .TP INFO2 recycle bin file example, by Steve Hailey -http://www.csisite.net/downloads/INFO2.pdf +http://www.cyber-ssct.com/resources/INFO2.pdf . \} .PP @@ -423,4 +478,3 @@ \fC\fP .hy helped in Debian packaging and was author of the original manpage. - diff -Nru rifiuti2-0.6.1/src/rifiuti.c rifiuti2-0.7.0/src/rifiuti.c --- rifiuti2-0.6.1/src/rifiuti.c 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/src/rifiuti.c 2019-05-09 01:26:08.000000000 +0000 @@ -1,6 +1,7 @@ +/* vim: set sw=4 ts=4 noexpandtab : */ /* * Copyright (C) 2003, by Keith J. Jones. - * Copyright (C) 2007, 2015 Abel Cheung. + * Copyright (C) 2007-2019 Abel Cheung. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,6 +35,9 @@ #include #include "utils.h" +#ifdef G_OS_WIN32 +# include "utils-win.h" +#endif #include #include @@ -41,17 +45,9 @@ #include "rifiuti.h" - char *delim = NULL; -static char **fileargs = NULL; -static char *outfilename = NULL; - char *legacy_encoding = NULL; - int output_format = OUTPUT_CSV; -static gboolean no_heading = FALSE; -static gboolean xml_output = FALSE; - gboolean always_utf8 = FALSE; - gboolean has_unicode_filename = FALSE; - gboolean use_localtime = FALSE; -static gboolean do_print_version = FALSE; +static r2status exit_status = EXIT_SUCCESS; +static metarecord meta; +extern char *legacy_encoding; /* 0-25 => A-Z, 26 => '\', 27 or above is erraneous */ unsigned char driveletters[28] = @@ -62,127 +58,132 @@ 'V', 'W', 'X', 'Y', 'Z', '\\', '?' }; -static GOptionEntry mainoptions[] = -{ - {"output", 'o', 0, G_OPTION_ARG_FILENAME, &outfilename, - N_("Write output to FILE"), - N_("FILE")}, - {"xml", 'x', 0, G_OPTION_ARG_NONE, &xml_output, - N_("Output in XML format instead of tab-delimited values"), NULL}, - {"legacy-filename", 'l', 0, G_OPTION_ARG_STRING, &legacy_encoding, - N_("Show legacy (8.3) filename if available and specify its CODEPAGE"), - N_("CODEPAGE")}, - {"localtime", 'z', 0, G_OPTION_ARG_NONE, &use_localtime, - N_("Present deletion time in time zone of local system (default is UTC)"), - NULL}, - {"version", 'v', 0, G_OPTION_ARG_NONE, &do_print_version, - N_("Print version information and exit"), NULL}, - {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &fileargs, - N_("INFO2 file name"), NULL}, - {NULL} -}; - -static GOptionEntry textoptions[] = -{ - {"delimiter", 't', 0, G_OPTION_ARG_STRING, &delim, - N_("String to use as delimiter (TAB by default)"), N_("STRING")}, - {"no-heading", 'n', 0, G_OPTION_ARG_NONE, &no_heading, - N_("Don't show header info"), NULL}, - {"always-utf8", '8', 0, G_OPTION_ARG_NONE, &always_utf8, - N_("Always display result in UTF-8 encoding"), NULL}, - {NULL} -}; - - -/* +/*! * Check if index file has sufficient amount of data for reading * 0 = success, all other return status = error + * If success, infile will be set to file pointer and other args + * will be filled, otherwise file pointer = NULL */ -static int -validate_index_file (FILE *inf, - off_t size, - uint32_t *info2_version, - uint32_t *recordsize) +static r2status +validate_index_file (const char *filename, + FILE **infile) { - size_t status; + void *buf; + FILE *fp = NULL; + uint32_t ver; + int e, ret; g_debug ("Start file validation..."); - if (size < RECORD_START_OFFSET) /* empty INFO2 file has 20 bytes */ + g_return_val_if_fail ( infile != NULL, R2_ERR_INTERNAL ); + *infile = NULL; + + if ( !(fp = g_fopen (filename, "rb")) ) { - g_debug ("file size = %d, expect at least %d\n", (int) size, - RECORD_START_OFFSET); - g_printerr (_("File is truncated, or probably not an INFO2 file.\n")); - return RIFIUTI_ERR_BROKEN_FILE; + e = errno; + g_printerr (_("Error opening file '%s' for reading: %s"), + filename, g_strerror (e)); + g_printerr ("\n"); + return R2_ERR_OPEN_FILE; } - /* with file size check already done, fread fail -> serious problem */ - fseek (inf, 0, SEEK_SET); - status = fread (info2_version, sizeof (*info2_version), 1, inf); - if (status < 1) + buf = g_malloc (RECORD_START_OFFSET); + + if ( 1 > fread (buf, RECORD_START_OFFSET, 1, fp) ) { - /* TRANSLATOR COMMENT: the variable is function name */ - g_critical (_("%s(): fread() failed when reading version info"), - __func__); - return RIFIUTI_ERR_OPEN_FILE; + /* TRANSLATOR COMMENT: file size must be at least 20 bytes */ + g_critical (_("File size less than minimum allowed (%d bytes)"), RECORD_START_OFFSET); + ret = R2_ERR_BROKEN_FILE; + goto validation_broken; } - *info2_version = GUINT32_FROM_LE (*info2_version); - fseek (inf, RECORD_SIZE_OFFSET, SEEK_SET); - status = fread (recordsize, sizeof (*recordsize), 1, inf); - if (status < 1) - { - /* TRANSLATOR COMMENT: the variable is function name */ - g_critical (_("%s(): fread() failed when reading recordsize"), - __func__); - return RIFIUTI_ERR_OPEN_FILE; + copy_field (&ver, VERSION, KEPT_ENTRY); + ver = GUINT32_FROM_LE (ver); + + /* total_entry only meaningful for 95 and NT4, on other versions + * it's junk memory data, don't bother copying */ + if ( ( ver == VERSION_NT4 ) || ( ver == VERSION_WIN95 ) ) { + copy_field (&meta.total_entry, TOTAL_ENTRY, RECORD_SIZE); + meta.total_entry = GUINT32_FROM_LE (meta.total_entry); } - *recordsize = GUINT32_FROM_LE (*recordsize); - /* Recordsize should be restricted to either 280 (v4) or 800 bytes (v5) */ - switch (*info2_version) + copy_field (&meta.recordsize, RECORD_SIZE, FILESIZE_SUM); + meta.recordsize = GUINT32_FROM_LE (meta.recordsize); + + g_free (buf); + + /* Turns out version is not reliable indicator. Use size instead */ + switch (meta.recordsize) { - case FORMAT_WIN98: - if (*recordsize != VERSION4_RECORD_SIZE) + case LEGACY_RECORD_SIZE: + + meta.has_unicode_path = FALSE; + + if ( ( ver != VERSION_ME_03 ) && /* ME still use 280 byte record */ + ( ver != VERSION_WIN98 ) && + ( ver != VERSION_WIN95 ) ) { - g_debug ("Size per record = %u, expect %u instead.", *recordsize, - VERSION4_RECORD_SIZE); - g_critical (_("Invalid record size for this version of INFO2")); - return RIFIUTI_ERR_BROKEN_FILE; + g_printerr (_("Unsupported file version, or probably not an INFO2 file at all.")); + g_printerr ("\n"); + ret = R2_ERR_BROKEN_FILE; + goto validation_broken; } + if (!legacy_encoding) { - g_printerr (_("This INFO2 file was produced on a Windows 98. " + g_printerr (_("This INFO2 file was produced on a legacy system " + "without Unicode file name (Windows ME or earlier). " "Please specify codepage of concerned system with " - "'-l' or '--legacy-filename' option.\n\n")); - /* TRANSLATOR COMMENT: use suitable example from YOUR language & code page */ - g_printerr (_("For example, if file name was expected to contain " - "accented latin characters, use '-l CP1252' option; " - "or in case of Japanese characters, '-l CP932'.\n\n" - "Code pages (or any other encodings) supported by " - "'iconv' can be used.\n")); - return RIFIUTI_ERR_ARG; + "'-l' or '--legacy-filename' option.")); + g_printerr ("\n\n"); + /* TRANSLATOR COMMENT: can choose example from YOUR language & code page */ + g_printerr (_("For example, if recycle bin is expected to come from West " + "European versions of Windows, use '-l CP1252' option; " + "or in case of Japanese Windows, use '-l CP932'.")); + g_printerr ("\n"); + + ret = R2_ERR_ARG; + goto validation_broken; + } + + switch (ver) + { + case VERSION_WIN95: meta.os_guess = OS_GUESS_95; break; + case VERSION_WIN98: meta.os_guess = OS_GUESS_98; break; + case VERSION_ME_03: meta.os_guess = OS_GUESS_ME; break; } + break; - case FORMAT_WIN2K: - if (*recordsize != VERSION5_RECORD_SIZE) + case UNICODE_RECORD_SIZE: + + meta.has_unicode_path = TRUE; + if ( ( ver != VERSION_ME_03 ) && ( ver != VERSION_NT4 ) ) { - g_debug ("Size per record = %u, expect %u instead.", *recordsize, - VERSION5_RECORD_SIZE); - g_critical (_("Invalid record size for this version of INFO2")); - return RIFIUTI_ERR_BROKEN_FILE; + g_printerr (_("Unsupported file version, or probably not an INFO2 file at all.")); + g_printerr ("\n"); + ret = R2_ERR_BROKEN_FILE; + goto validation_broken; } - /* only version 5 contains UTF-16 filename */ - has_unicode_filename = TRUE; + /* guess is not complete yet for latter case, see populate_record_data */ + meta.os_guess = (ver == VERSION_NT4) ? OS_GUESS_NT4 : OS_GUESS_2K_03; break; default: - g_printerr (_("File is not supported, or it is " - "probably not an INFO2 file.\n")); - return RIFIUTI_ERR_BROKEN_FILE; + ret = R2_ERR_BROKEN_FILE; + goto validation_broken; } - return 0; + + rewind (fp); + *infile = fp; + meta.version = (int64_t) ver; + + return EXIT_SUCCESS; + + validation_broken: + + fclose (fp); + return ret; } @@ -192,26 +193,23 @@ rbin_struct *record; uint64_t win_filetime; uint32_t drivenum; - long read, write; - - g_debug ("Start populating record..."); + size_t read; + char *legacy_fname; record = g_malloc0 (sizeof (rbin_struct)); - record->type = RECYCLE_BIN_TYPE_FILE; - /* Guarantees null-termination by allocating extra byte */ - record->legacy_filename = - (char *) g_malloc0 (RECORD_INDEX_OFFSET - LEGACY_FILENAME_OFFSET + 1); - memcpy (record->legacy_filename, buf + LEGACY_FILENAME_OFFSET, - RECORD_INDEX_OFFSET - LEGACY_FILENAME_OFFSET); + /* Guarantees null-termination by allocating extra byte; same goes with + * unicode filename */ + legacy_fname = g_malloc0 (RECORD_INDEX_OFFSET - LEGACY_FILENAME_OFFSET + 1); + copy_field (legacy_fname, LEGACY_FILENAME, RECORD_INDEX); - memcpy (&record->index_n, buf + RECORD_INDEX_OFFSET, - DRIVE_LETTER_OFFSET - RECORD_INDEX_OFFSET); + /* Index number associated with the record */ + copy_field (&record->index_n, RECORD_INDEX, DRIVE_LETTER); record->index_n = GUINT32_FROM_LE (record->index_n); g_debug ("index=%u", record->index_n); - memcpy (&drivenum, buf + DRIVE_LETTER_OFFSET, - FILETIME_OFFSET - DRIVE_LETTER_OFFSET); + /* Number representing drive letter */ + copy_field (&drivenum, DRIVE_LETTER, FILETIME); drivenum = GUINT32_FROM_LE (drivenum); g_debug ("drive=%u", drivenum); if (drivenum >= sizeof (driveletters) - 1) @@ -221,309 +219,255 @@ record->emptied = FALSE; /* first byte will be removed from filename if file is not in recycle bin */ - if (!*record->legacy_filename) + if (!*legacy_fname) { record->emptied = TRUE; - *record->legacy_filename = record->drive; + *legacy_fname = record->drive; } /* File deletion time */ - memcpy (&win_filetime, buf + FILETIME_OFFSET, - FILESIZE_OFFSET - FILETIME_OFFSET); + copy_field (&win_filetime, FILETIME, FILESIZE); win_filetime = GUINT64_FROM_LE (win_filetime); record->deltime = win_filetime_to_epoch (win_filetime); /* File size or occupied cluster size */ /* BEWARE! This is 32bit data casted to 64bit struct member */ - memcpy (&record->filesize, buf + FILESIZE_OFFSET, - UNICODE_FILENAME_OFFSET - FILESIZE_OFFSET); + copy_field (&record->filesize, FILESIZE, UNICODE_FILENAME); record->filesize = GUINT64_FROM_LE (record->filesize); g_debug ("filesize=%" G_GUINT64_FORMAT, record->filesize); - if (has_unicode_filename) + /* + * 1. Only bother populating legacy path if users need it, + * because otherwise we don't know which encoding to use + * 2. Enclose with angle brackets because they are not allowed + * in Windows file name, therefore stands out better that + * the escaped hex sequences are not part of real file name + */ + if (legacy_encoding) { - GError *error = NULL; - /* - * Added safeguard to memory buffer (2 bytes larger than necessary), - * so safely assume string is null terminated - */ - record->utf8_filename = - utf16le_to_utf8 ((gunichar2 *) (buf + UNICODE_FILENAME_OFFSET), - WIN_PATH_MAX + 1, &read, &write, &error); - g_debug ("utf16->8 r=%li w=%li", read, write); + record->legacy_path = conv_path_to_utf8_with_tmpl ( + legacy_fname, legacy_encoding, "<\\%02X>", &read, &exit_status); - if (error) - { - g_warning (_("Error converting file name from %s encoding to " - "UTF-8 encoding for record %u: %s"), - "UTF-16", record->index_n, error->message); - g_clear_error (&error); + if (record->legacy_path == NULL) { + g_warning (_("(Record %u) Error converting legacy path to UTF-8."), + record->index_n); + record->legacy_path = ""; } } - return record; -} -int -main (int argc, - char **argv) -{ - void *buf; - FILE *infile, *outfile; - int status; - GOptionGroup *textoptgroup; - GOptionContext *context; - GError *error = NULL; + g_free (legacy_fname); - GStatBuf st; - rbin_struct *record; - uint32_t recordsize, info2_version; - char *bug_report_str; + if (! meta.has_unicode_path) + return record; - setlocale (LC_ALL, ""); + /******************************************* + * Part below deals with unicode path only * + *******************************************/ - if (g_file_test (LOCALEDIR, G_FILE_TEST_IS_DIR)) - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); - else - { - /* searching current dir is more useful on Windows */ - char *d = g_path_get_dirname (argv[0]); - char *p = g_build_filename (d, "rifiuti-l10n", NULL); - if (g_file_test (p, G_FILE_TEST_IS_DIR)) - bindtextdomain (GETTEXT_PACKAGE, p); - g_free (p); - g_free (d); - } - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); - - context = g_option_context_new ("INFO2"); - g_option_context_set_summary - (context, _("Parse INFO2 file and dump recycle bin data.")); - bug_report_str = - g_strdup_printf (_("Report bugs to %s"), PACKAGE_BUGREPORT); - g_option_context_set_description (context, bug_report_str); - g_free (bug_report_str); - g_option_context_add_main_entries (context, mainoptions, "rifiuti"); - - textoptgroup = - g_option_group_new ("text", _("Plain text output options:"), - N_("Show plain text output options"), NULL, NULL); - g_option_group_set_translation_domain (textoptgroup, GETTEXT_PACKAGE); - g_option_group_add_entries (textoptgroup, textoptions); - g_option_context_add_group (context, textoptgroup); + record->uni_path = conv_path_to_utf8_with_tmpl ( + (char *) (buf + UNICODE_FILENAME_OFFSET), NULL, + "<\\u%04X>", &read, &exit_status); - /* Must be done before parsing arguments since argc will be modified later */ - if (argc <= 1) - { - char *msg = g_option_context_get_help (context, FALSE, NULL); + if (record->uni_path == NULL) { + g_warning (_("(Record %u) Error converting unicode path to UTF-8."), + record->index_n); + record->uni_path = ""; + } -#ifdef G_OS_WIN32 - g_set_print_handler (gui_message); -#endif - g_print ("%s", msg); - g_free (msg); - g_option_context_free (context); - exit (EXIT_SUCCESS); - } - - g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, my_debug_handler, NULL); - - { - gboolean i; - /* - * The user case where this code won't provide benefit is VERY rare, - * so don't bother doing fallback because it was always the case before. - * - * However this parsing doesn't work nice with path translation in MSYS; - * directory separator in the middle of path would be translated to root - * of MSYS folder if earlier path component is in pure non-ASCII. - */ -#if GLIB_CHECK_VERSION(2, 40, 0) && defined (G_OS_WIN32) - char **args; - - args = g_win32_get_command_line (); - i = g_option_context_parse_strv (context, &args, &error); - g_strfreev (args); -#else - i = g_option_context_parse (context, &argc, &argv, &error); -#endif - g_option_context_free (context); + /* + * We check for junk memory filling the padding area after + * unicode path, using it as the indicator of OS generating this + * INFO2 file. (server 2000 / 2003) + * + * The padding area after legacy path is no good; experiment + * shows that legacy path *always* contain non-zero bytes after + * null terminator if path contains double-byte character, + * regardless of OS. + * + * Those non-zero bytes resemble partial end of full path. + * Looks like an ANSI codepage full path is filled in + * legacy path field, then overwritten in place by a 8.3 + * version of path whenever applicable (which was always shorter). + */ + if (! meta.fill_junk) + { + void *ptr; - if (!i) + for (ptr = buf + UNICODE_FILENAME_OFFSET + read; + ptr < buf + UNICODE_RECORD_SIZE; ptr++) { - g_printerr (_("Error parsing options: %s\n"), error->message); - g_clear_error (&error); - exit (RIFIUTI_ERR_ARG); + if ( *(char *) ptr != '\0' ) + { + g_debug ("Junk detected at offset 0x%tx of unicode path", + ptr - buf - UNICODE_FILENAME_OFFSET); + meta.fill_junk = TRUE; + break; + } } } - if (do_print_version) - { - print_version(); - exit (EXIT_SUCCESS); - } + return record; +} - if (!fileargs || g_strv_length (fileargs) > 1) - { - g_printerr (_("Must specify exactly one INFO2 file as argument.\n\n")); - g_printerr (_("Run program with '-?' option for more info.\n")); - exit (RIFIUTI_ERR_ARG); - } - if (outfilename) - { - outfile = g_fopen (outfilename, "wb"); - if (NULL == outfile) - { - g_printerr (_("Error opening file '%s' for writing: %s\n"), - outfilename, strerror (errno)); - exit (RIFIUTI_ERR_OPEN_FILE); - } - } - else - outfile = stdout; +static void +parse_record_cb (char *index_file, + GSList **recordlist) +{ + rbin_struct *record; + FILE *infile; + size_t size; + void *buf = NULL; - if (xml_output) + exit_status = validate_index_file (index_file, &infile); + if ( exit_status != EXIT_SUCCESS ) { - output_format = OUTPUT_XML; - if (no_heading || always_utf8 || (NULL != delim)) - { - g_printerr (_("Plain text format options " - "can not be used in XML mode.\n")); - exit (RIFIUTI_ERR_ARG); - } + g_printerr (_("File '%s' fails validation."), index_file); + g_printerr ("\n"); + return; } - /* Is charset valid? */ - if (legacy_encoding) - { - GIConv try; - try = g_iconv_open (legacy_encoding, "UTF-8"); - if (try == (GIConv) - 1) - { - g_printerr (_("'%s' is not a valid code page or encoding. " - "Only those supported by 'iconv' can be used.\n"), - legacy_encoding); -#ifdef G_OS_WIN32 - g_printerr (_("Please visit following web page for a list " - "closely resembling encodings supported by " - "rifiuti:\n\n\t%s\n\n"), - "https://www.gnu.org/software/libiconv/"); -#endif -#ifdef G_OS_UNIX - g_printerr (_("Please execute 'iconv -l' for list " - "of supported encodings.\n")); -#endif - exit (RIFIUTI_ERR_ENCODING); - } - else - g_iconv_close (try); - } + g_debug ("Start populating record for '%s'...", index_file); - if (NULL == delim) - delim = g_strndup ("\t", 2); - else - { - char *d = filter_escapes (delim); - if (d != NULL) - { - g_free (delim); - delim = d; - } - } + /* + * Add padding bytes as null-termination of unicode file name. + * Normally Windows should have done the null termination within + * WIN_PATH_MAX limit, but on 98/ME/2000 programmers were sloppy + * and use junk memory as padding, so just play safe. + */ + buf = g_malloc0 (meta.recordsize + sizeof(gunichar2)); + + fseek (infile, RECORD_START_OFFSET, SEEK_SET); + meta.is_empty = TRUE; + while (meta.recordsize == (size = fread (buf, 1, meta.recordsize, infile))) { - char *i = delim; - GString *str = g_string_new (g_strdup ("filtered delimiter = ")); - do - { - if (((*i) <= 0x7E) && ((*i) >= 0x20)) - str = g_string_append_c (str, *i); - else - g_string_append_printf (str, "\\x%02X", (char) (*i)); - } - while ((char) (* (++i)) != '\0'); - g_debug (str->str); - g_string_free (str, TRUE); + record = populate_record_data (buf); + record->meta = &meta; + /* INFO2 already sort entries by time */ + *recordlist = g_slist_append (*recordlist, record); + meta.is_empty = FALSE; } + g_free (buf); - g_debug ("Start basic file checking..."); + /* do this only when all entries are scanned */ + if ( ! meta.is_empty && ( meta.os_guess == OS_GUESS_2K_03 ) ) + meta.os_guess = meta.fill_junk ? OS_GUESS_2K : OS_GUESS_XP_03; - if (!g_file_test (fileargs[0], G_FILE_TEST_EXISTS)) + if ( ferror (infile) ) { - g_printerr (_("'%s' does not exist.\n"), fileargs[0]); - exit (RIFIUTI_ERR_OPEN_FILE); + g_critical (_("Failed to read record at position %li: %s"), + ftell (infile), strerror (errno)); + exit_status = R2_ERR_OPEN_FILE; } - - if (!g_file_test (fileargs[0], G_FILE_TEST_IS_REGULAR)) + if ( feof (infile) && size && ( size < meta.recordsize ) ) { - g_printerr (_("'%s' is not a normal file.\n"), fileargs[0]); - exit (RIFIUTI_ERR_OPEN_FILE); + g_warning (_("Premature end of file, last record (%zu bytes) discarded"), size); + exit_status = R2_ERR_BROKEN_FILE; } - if (0 != g_stat (fileargs[0], &st)) - { - g_printerr (_("Error getting metadata of file '%s': %s\n"), fileargs[0], - strerror (errno)); - exit (RIFIUTI_ERR_OPEN_FILE); - } + fclose (infile); +} - if (!(infile = g_fopen (fileargs[0], "rb"))) - { - g_printerr (_("Error opening file '%s' for reading: %s\n"), fileargs[0], - strerror (errno)); - exit (RIFIUTI_ERR_OPEN_FILE); - } +int +main (int argc, + char **argv) +{ + GSList *filelist = NULL; + GSList *recordlist = NULL; + GOptionContext *context; + + extern char **fileargs; + + rifiuti_init (argv[0]); + + /* TRANSLATOR: appears in help text short summary */ + context = g_option_context_new (N_("INFO2")); + g_option_context_set_summary (context, N_( + "Parse INFO2 file and dump recycle bin data.")); + rifiuti_setup_opt_ctx (&context, RECYCLE_BIN_TYPE_FILE); + exit_status = rifiuti_parse_opt_ctx (&context, &argc, &argv); + if (exit_status != EXIT_SUCCESS) + goto cleanup; + + exit_status = check_file_args (fileargs[0], &filelist, RECYCLE_BIN_TYPE_FILE); + if (exit_status != EXIT_SUCCESS) + goto cleanup; - status = validate_index_file (infile, st.st_size, - &info2_version, &recordsize); - if (status != 0) - { - fclose (infile); - exit (status); - } + /* To be overwritten in parse_record_cb() when appropriate */ + meta.os_guess = OS_GUESS_UNKNOWN; - rewind (infile); - if (!no_heading) - print_header (outfile, fileargs[0], (int64_t) info2_version, TRUE); + /* + * TODO May be silly for single file, but would be useful in future + * when reading multiple files from live system + */ + g_slist_foreach (filelist, (GFunc) parse_record_cb, &recordlist); + meta.type = RECYCLE_BIN_TYPE_FILE; + meta.filename = fileargs[0]; /* - * Add 2 padding bytes as null-termination of unicode file name. Not so confident - * that file names created with Win2K or earlier are null terminated, because - * random memory fragments are copied to the padding bytes + * Keeping deleted entry is only available since 98 + * Note: always set this variable after parse_record_cb() because + * meta.version is not set beforehand */ - buf = g_malloc0 (recordsize + 2); + meta.keep_deleted_entry = ( meta.version >= VERSION_WIN98 ); - fseek (infile, RECORD_START_OFFSET, SEEK_SET); - while (TRUE) + if ( !meta.is_empty && (recordlist == NULL) ) { - status = fread (buf, recordsize, 1, infile); - if (status != 1) - { - if (!feof (infile)) - g_warning (_("Failed to read next record: %s"), - strerror (errno)); - break; + g_printerr ("%s", _("Recycle bin file has no valid record.\n")); + exit_status = R2_ERR_BROKEN_FILE; + goto cleanup; + } + + /* Print everything */ + { + r2status s = prepare_output_handle (); + if (s != EXIT_SUCCESS) { + exit_status = s; + goto cleanup; } + } - record = populate_record_data (buf); - print_record (record, outfile); + print_header (meta); + g_slist_foreach (recordlist, (GFunc) print_record_cb, NULL); + print_footer (); + + close_output_handle (); - g_free (record->utf8_filename); - g_free (record->legacy_filename); - g_free (record); + /* file descriptor should have been closed at this point */ + { + r2status s = move_temp_file (); + if ( s != EXIT_SUCCESS ) + exit_status = s; } - print_footer (outfile); + cleanup: - g_debug ("Cleaning up..."); + /* Last minute error messages for accumulated non-fatal errors */ + switch (exit_status) + { + case R2_ERR_USER_ENCODING: + if (legacy_encoding) { + g_printerr (_("Some entries could not be interpreted in %s encoding." + " The concerned characters are displayed in hex value instead." + " Very likely the (localised) Windows generating the recycle bin " + "artifact does not use specified codepage."), legacy_encoding); + } else { + g_printerr (_("Some entries could not be presented as correct " + "unicode path. The concerned characters are displayed " + "in escaped unicode sequences.")); + } + g_printerr ("\n"); + break; - fclose (infile); - fclose (outfile); + default: + break; + } + g_debug ("Cleaning up..."); - g_free (buf); + g_slist_free_full (recordlist, (GDestroyNotify) free_record_cb); + g_slist_free_full (filelist , (GDestroyNotify) g_free ); + free_vars (); - exit (EXIT_SUCCESS); + return exit_status; } - -/* vim: set sw=4 ts=4 noexpandtab : */ diff -Nru rifiuti2-0.6.1/src/rifiuti.h rifiuti2-0.7.0/src/rifiuti.h --- rifiuti2-0.6.1/src/rifiuti.h 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/src/rifiuti.h 2019-05-09 01:26:08.000000000 +0000 @@ -1,6 +1,7 @@ +/* vim: set sw=4 ts=4 noexpandtab : */ /* * Copyright (C) 2003, by Keith J. Jones. - * Copyright (C) 2007, 2015 Abel Cheung. + * Copyright (C) 2007-2019 Abel Cheung. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,7 +35,11 @@ #include "utils.h" /* These offsets are relative to file start */ +#define VERSION_OFFSET 0 +#define KEPT_ENTRY_OFFSET 4 +#define TOTAL_ENTRY_OFFSET 8 #define RECORD_SIZE_OFFSET 12 +#define FILESIZE_SUM_OFFSET 16 #define RECORD_START_OFFSET 20 /* Following offsets are relative to start of each record */ @@ -45,9 +50,7 @@ #define FILESIZE_OFFSET ((WIN_PATH_MAX) + 16) #define UNICODE_FILENAME_OFFSET ((WIN_PATH_MAX) + 20) -#define VERSION4_RECORD_SIZE ((WIN_PATH_MAX) + 20) /* 280 bytes */ -#define VERSION5_RECORD_SIZE ((WIN_PATH_MAX) * 3 + 20) /* 800 bytes */ +#define LEGACY_RECORD_SIZE ((WIN_PATH_MAX) + 20) /* 280 bytes */ +#define UNICODE_RECORD_SIZE ((WIN_PATH_MAX) * 3 + 20) /* 800 bytes */ #endif - -/* vim: set sw=4 ts=4 noexpandtab : */ diff -Nru rifiuti2-0.6.1/src/rifiuti-vista.c rifiuti2-0.7.0/src/rifiuti-vista.c --- rifiuti2-0.6.1/src/rifiuti-vista.c 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/src/rifiuti-vista.c 2019-05-09 01:26:08.000000000 +0000 @@ -1,5 +1,6 @@ +/* vim: set sw=4 ts=4 noexpandtab : */ /* - * Copyright (C) 2007, 2015 Abel Cheung. + * Copyright (C) 2007-2019 Abel Cheung. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,142 +34,134 @@ #include #include "utils.h" +#ifdef G_OS_WIN32 +# include "utils-win.h" +#endif #include #include #include "rifiuti-vista.h" - char *delim = NULL; -static char **fileargs = NULL; -static char *outfilename = NULL; - char *legacy_encoding = NULL; - int output_format = OUTPUT_CSV; -static gboolean no_heading = FALSE; -static gboolean xml_output = FALSE; - gboolean always_utf8 = FALSE; - gboolean has_unicode_filename = TRUE; - gboolean use_localtime = FALSE; -static gboolean do_print_version = FALSE; +static r2status exit_status = EXIT_SUCCESS; +static metarecord meta; -static GOptionEntry mainoptions[] = -{ - {"output", 'o', 0, G_OPTION_ARG_FILENAME, &outfilename, - N_("Write output to FILE"), N_("FILE")}, - {"xml", 'x', 0, G_OPTION_ARG_NONE, &xml_output, - N_("Output in XML format instead of tab-delimited values"), NULL}, - {"localtime", 'z', 0, G_OPTION_ARG_NONE, &use_localtime, - N_("Present deletion time in time zone of local system (default is UTC)"), - NULL}, - {"version", 'v', 0, G_OPTION_ARG_NONE, &do_print_version, - N_("Print version information and exit"), NULL}, - {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &fileargs, - N_("$Recycle.bin folder or file name"), NULL}, - {NULL} -}; -static GOptionEntry textoptions[] = +/*! + * Check if index file has sufficient amount of data for reading. + * If successful, its file content will be stored in buf. + * Return 0 if successful, non-zero on error + */ +static r2status +validate_index_file (const char *filename, + void **filebuf, + gsize *bufsize, + uint64_t *ver, + uint32_t *pathlen) { - {"delimiter", 't', 0, G_OPTION_ARG_STRING, &delim, - N_("String to use as delimiter (TAB by default)"), N_("STRING")}, - {"no-heading", 'n', 0, G_OPTION_ARG_NONE, &no_heading, - N_("Don't show header info"), NULL}, - {"always-utf8", '8', 0, G_OPTION_ARG_NONE, &always_utf8, - N_("Always display result in UTF-8 encoding"), NULL}, - {NULL} -}; + gsize expected; + int status; + GError *err = NULL; + char *buf; + g_debug ("Start file validation for '%s'...", filename); -/* Check if index file has sufficient amount of data for reading */ -/* 0 = success, all other return status = error */ -static int -validate_index_file (FILE *inf, - off_t size, - uint64_t *version, - uint32_t *namelength) -{ - off_t expected; - size_t status; + g_return_val_if_fail ( (filename != NULL) && (*filename != '\0'), + R2_ERR_INTERNAL ); + g_return_val_if_fail ( (filebuf != NULL), R2_ERR_INTERNAL ); + g_return_val_if_fail ( (bufsize != NULL), R2_ERR_INTERNAL ); + g_return_val_if_fail ( (ver != NULL), R2_ERR_INTERNAL ); + g_return_val_if_fail ( (pathlen != NULL), R2_ERR_INTERNAL ); + + if ( !g_file_get_contents (filename, &buf, bufsize, &err) ) + { + g_critical (_("%s(): failed to retrieve file content for '%s': %s"), + __func__, filename, err->message); + g_clear_error (&err); + status = R2_ERR_OPEN_FILE; + goto validation_error; + } - g_debug ("Start file validation..."); + g_debug ("Retrieval of '%s' is done, size = %" G_GSIZE_FORMAT, filename, *bufsize); - if (size <= VERSION1_FILENAME_OFFSET) /* file path can't possibly be empty */ + if (*bufsize <= VERSION1_FILENAME_OFFSET) { - g_debug ("file size = %i, expect larger than %i\n", (int) size, - VERSION1_FILENAME_OFFSET); - g_printerr (_("File is truncated, or probably not a $Recycle.bin index file.\n")); - return RIFIUTI_ERR_BROKEN_FILE; - } - - /* with file size check already done, fread fail probably mean serious problem */ - rewind (inf); - status = fread (version, sizeof (*version), 1, inf); - if (status < 1) - { - /* TRANSLATOR COMMENT: the variable is function name */ - g_critical (_("%s(): fread() failed when reading version info"), - __func__); - return RIFIUTI_ERR_OPEN_FILE; - } - *version = GUINT64_FROM_LE (*version); - g_debug ("version=%" G_GUINT64_FORMAT, *version); - - switch (*version) - { - case (uint64_t) FORMAT_VISTA: - expected = VERSION1_FILE_SIZE; - /* see populate_record_data() for reason */ - if ((size == expected) || (size == expected - 1)) - return 0; - break; - - case (uint64_t) FORMAT_WIN10: - g_return_val_if_fail ((size > VERSION2_FILENAME_OFFSET), FALSE); - fseek (inf, VERSION2_FILENAME_OFFSET - sizeof (*namelength), - SEEK_SET); - if (status < 1) - { - /* TRANSLATOR COMMENT: the variable is function name */ - g_critical (_("%s(): fread() failed when reading file name length"), - __func__); - return RIFIUTI_ERR_OPEN_FILE; - } - status = fread (namelength, sizeof (*namelength), 1, inf); - *namelength = GUINT32_FROM_LE (*namelength); + g_debug ("File size expected to be more than %" G_GSIZE_FORMAT, + (gsize) VERSION1_FILENAME_OFFSET); + g_printerr (_("File is truncated, or probably not a $Recycle.bin index file.")); + g_printerr ("\n"); + status = R2_ERR_BROKEN_FILE; + goto validation_error; + } + + copy_field (ver, VERSION, FILESIZE); + *ver = GUINT64_FROM_LE (*ver); + g_debug ("version = %" G_GUINT64_FORMAT, *ver); + + switch (*ver) + { + case VERSION_VISTA: + + expected = VERSION1_FILE_SIZE; + /* see populate_record_data() for reason */ + if ( (*bufsize != expected) && (*bufsize != expected - 1) ) + { + g_debug ("File size expected to be %" G_GSIZE_FORMAT + " or %" G_GSIZE_FORMAT, expected, expected - 1); + g_printerr (_("Index file expected size and real size do not match.")); + g_printerr ("\n"); + status = R2_ERR_BROKEN_FILE; + goto validation_error; + } + *pathlen = WIN_PATH_MAX; + break; - /* Fixed header length + file name length in UTF-16 encoding */ - expected = VERSION2_FILENAME_OFFSET + (*namelength) * 2; - if (size == expected) - return 0; - break; - - default: - g_printerr (_("File is not supported, or it is probably " - "not a $Recycle.bin index file.\n")); - return RIFIUTI_ERR_BROKEN_FILE; - } - - g_debug ("File size = %" G_GUINT64_FORMAT ", expected %" G_GUINT64_FORMAT, - (uint64_t) size, (uint64_t) expected); - g_printerr (_("Index file expected size and real size do not match.\n")); - return RIFIUTI_ERR_BROKEN_FILE; + case VERSION_WIN10: + + copy_field (pathlen, VERSION1_FILENAME, VERSION2_FILENAME); + *pathlen = GUINT32_FROM_LE (*pathlen); + + /* Header length + file name length in UTF-16 encoding */ + expected = VERSION2_FILENAME_OFFSET + (*pathlen) * 2; + if (*bufsize != expected) + { + g_debug ("File size expected to be %" G_GSIZE_FORMAT, expected); + g_printerr (_("Index file expected size and real size do not match.")); + g_printerr ("\n"); + status = R2_ERR_BROKEN_FILE; + goto validation_error; + } + break; + + default: + g_printerr (_("Unsupported file version, or probably not a $Recycle.bin index file.")); + g_printerr ("\n"); + status = R2_ERR_BROKEN_FILE; + goto validation_error; + } + + *filebuf = buf; + return EXIT_SUCCESS; + +validation_error: + + *filebuf = NULL; + return status; } static rbin_struct * populate_record_data (void *buf, uint64_t version, - uint32_t namelength, + uint32_t pathlen, gboolean erraneous) { - uint64_t win_filetime; - rbin_struct *record; - GError *error = NULL; - long read, write; + uint64_t win_filetime; + rbin_struct *record; + size_t read; record = g_malloc0 (sizeof (rbin_struct)); record->version = version; - record->type = RECYCLE_BIN_TYPE_DIR; /* * In rare cases, the size of index file is 543 bytes versus (normal) 544 bytes. @@ -177,11 +170,20 @@ * bug inside Windows. This is observed during deletion of dd.exe from Forensic * Acquisition Utilities (by George M. Garner Jr) in certain localized Vista. */ - /* TODO: Consider if the (possibly wrong) size should be printed or not */ memcpy (&record->filesize, buf + FILESIZE_OFFSET, - FILETIME_OFFSET - FILESIZE_OFFSET - (int) erraneous); - record->filesize = GUINT64_FROM_LE (record->filesize); - g_debug ("filesize=%" G_GUINT64_FORMAT, record->filesize); + FILETIME_OFFSET - FILESIZE_OFFSET - (int) erraneous); + if (erraneous) + { + g_debug ("filesize field broken, 56 bit only, val=0x%" G_GINT64_MODIFIER "X", + record->filesize); + /* not printing the value because it was wrong and misleading */ + record->filesize = G_MAXUINT64; + } + else + { + record->filesize = GUINT64_FROM_LE (record->filesize); + g_debug ("deleted file size = %" G_GUINT64_FORMAT, record->filesize); + } /* File deletion time */ memcpy (&win_filetime, buf + FILETIME_OFFSET - (int) erraneous, @@ -190,192 +192,95 @@ record->deltime = win_filetime_to_epoch (win_filetime); /* One extra char for safety, though path should have already been null terminated */ - g_debug ("namelength=%d", namelength); + g_debug ("pathlen = %d", pathlen); switch (version) { - case (uint64_t) FORMAT_VISTA: - record->utf8_filename = - utf16le_to_utf8 ((gunichar2 *) (buf + VERSION1_FILENAME_OFFSET - (int) erraneous), - namelength + 1, &read, &write, &error); - break; - case (uint64_t) FORMAT_WIN10: - record->utf8_filename = - utf16le_to_utf8 ((gunichar2 *) (buf + VERSION2_FILENAME_OFFSET), - namelength + 1, &read, &write, &error); - break; - } - g_debug ("utf16->8 r=%li w=%li", read, write); - - if (error) - { - g_warning (_("Error converting file name from %s encoding to " - "UTF-8 encoding: %s"), "UTF-16", error->message); - g_clear_error (&error); + case VERSION_VISTA: + record->uni_path = conv_path_to_utf8_with_tmpl ( + (const char *) (buf - erraneous + VERSION1_FILENAME_OFFSET), + NULL, "<\\u%04X>", &read, &exit_status); + break; + + case VERSION_WIN10: + record->uni_path = conv_path_to_utf8_with_tmpl ( + (const char *) (buf + VERSION2_FILENAME_OFFSET), + NULL, "<\\u%04X>", &read, &exit_status); + break; + + default: + g_assert_not_reached (); + } + + if (record->uni_path == NULL) { + g_warning (_("(Record %s) Error converting unicode path to UTF-8."), + record->index_s); + record->uni_path = ""; } + return record; } static void -parse_record (char *index_file, - GSList **recordlist) +parse_record_cb (char *index_file, + GSList **recordlist) { - FILE *infile; rbin_struct *record; char *basename; - uint64_t version; - uint32_t namelength = 0; - GStatBuf st; - void *buf; + uint64_t version = 0; + uint32_t pathlen = 0; + gsize bufsize; + void *buf = NULL; + r2status validate_st; basename = g_path_get_basename (index_file); - if (0 != g_stat (index_file, &st)) - { - g_printerr (_("Error getting metadata of file '%s': %s\n"), basename, - strerror (errno)); - goto parse_record_open_error; - } - - if (NULL == (infile = g_fopen (index_file, "rb"))) - { - g_printerr (_("Error opening file '%s' for reading: %s\n"), basename, - strerror (errno)); - goto parse_record_open_error; - } - - if (0 != validate_index_file (infile, st.st_size, &version, &namelength)) - { - g_printerr (_("File '%s' fails validation.\n"), basename); - goto parse_validation_error; - } - - rewind (infile); - /* Files are expected to be at most 0.5KB. Large files should have already been rejected. */ - buf = g_malloc0 (st.st_size + 2); - if (1 != fread (buf, st.st_size, 1, infile)) - { - /* - * TRANSLATOR COMMENT: 1st parameter is function name, - * 2nd is file name, 3rd is error message - */ - g_critical (_("%s(): fread() failed when " - "reading content of file '%s': %s\n"), - __func__, basename, strerror (errno)); - goto parse_validation_error; + validate_st = validate_index_file ( + index_file, &buf, &bufsize, &version, &pathlen); + if ( validate_st != EXIT_SUCCESS ) + { + g_printerr (_("File '%s' fails validation."), basename); + g_printerr ("\n"); + exit_status = validate_st; + goto parse_record_error; } g_debug ("Start populating record for '%s'...", basename); switch (version) { - case (uint64_t) FORMAT_VISTA: - /* see populate_record_data() for meaning of last parameter */ - record = populate_record_data (buf, version, (uint32_t) WIN_PATH_MAX, - (st.st_size == VERSION1_FILE_SIZE - 1)); - break; - - case (uint64_t) FORMAT_WIN10: - record = populate_record_data (buf, version, namelength, FALSE); - break; - - default: - /* VERY wrong if reaching here. Version info has already been filtered once */ - g_critical (_("Version info for '%s' still wrong " - "despite file validation."), basename); - goto parse_validation_error; + case VERSION_VISTA: + /* see populate_record_data() for meaning of last parameter */ + record = populate_record_data (buf, version, pathlen, + (bufsize == VERSION1_FILE_SIZE - 1)); + break; + + case VERSION_WIN10: + record = populate_record_data (buf, version, pathlen, FALSE); + break; + + default: + g_assert_not_reached(); } g_debug ("Parsing done for '%s'", basename); record->index_s = basename; + record->meta = &meta; *recordlist = g_slist_prepend (*recordlist, record); - fclose (infile); g_free (buf); return; - parse_validation_error: - fclose (infile); + parse_record_error: - parse_record_open_error: + g_free (buf); g_free (basename); } -/* Scan folder and add all "$Ixxxxxx.xxx" to filelist for parsing */ -static void -populate_index_file_list (GSList **list, - char *path) -{ - GDir *dir; - char *direntry, *fname; - GPatternSpec *pattern1, *pattern2; - GError *error = NULL; - - if (NULL == (dir = g_dir_open (path, 0, &error))) - { - g_printerr (_("Error opening directory '%s': %s\n"), path, - error->message); - g_clear_error (&error); - exit (RIFIUTI_ERR_OPEN_FILE); - } - - pattern1 = g_pattern_spec_new ("$I??????.*"); - pattern2 = g_pattern_spec_new ("$I??????"); - - while ((direntry = (char *) g_dir_read_name (dir)) != NULL) - { - if (!g_pattern_match_string (pattern1, direntry) && - !g_pattern_match_string (pattern2, direntry)) - continue; - fname = g_build_filename (path, direntry, NULL); - *list = g_slist_prepend (*list, fname); - } - - g_dir_close (dir); - - g_pattern_spec_free (pattern1); - g_pattern_spec_free (pattern2); -} - -/* Search for desktop.ini in folder for hint of recycle bin */ -static gboolean -found_desktop_ini (char *path) -{ - char *filename, *content, *found; - - filename = g_build_filename (path, "desktop.ini", NULL); - if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) - goto desktop_ini_error; - - /* assume desktop.ini is ASCII and not something spurious */ - if (!g_file_get_contents (filename, &content, NULL, NULL)) - goto desktop_ini_error; - - /* Don't bother parsing, we don't use the content at all */ - found = strstr (content, RECYCLE_BIN_CLSID); - g_free (content); - g_free (filename); - return (found != NULL); - - desktop_ini_error: - g_free (filename); - return FALSE; -} - - -static void -free_record (rbin_struct *record) -{ - g_free (record->index_s); - g_free (record->utf8_filename); - g_free (record); -} - - static int sort_record_by_time (rbin_struct *a, rbin_struct *b) { - /* time_t can be 32 or 64 bit, can't just return a-b :( */ + /* sort primary key: deletion time; secondary key: index file name */ return ((a->deltime < b->deltime) ? -1 : (a->deltime > b->deltime) ? 1 : strcmp (a->index_s, b->index_s)); @@ -386,248 +291,131 @@ main (int argc, char **argv) { - FILE *outfile; - GSList *filelist = NULL; - GSList *recordlist = NULL; - char *fname, *bug_report_str; - - GError *error = NULL; - GOptionContext *context; - GOptionGroup *textoptgroup; - - setlocale (LC_ALL, ""); + GSList *filelist = NULL; + GSList *recordlist = NULL; + GOptionContext *context; + + extern char **fileargs; + + rifiuti_init (argv[0]); + + /* TRANSLATOR: appears in help text short summary */ + context = g_option_context_new (N_("DIR_OR_FILE")); + g_option_context_set_summary (context, N_( + "Parse index files in C:\\$Recycle.bin style folder " + "and dump recycle bin data. Can also dump a single index file.")); + rifiuti_setup_opt_ctx (&context, RECYCLE_BIN_TYPE_DIR); + exit_status = rifiuti_parse_opt_ctx (&context, &argc, &argv); + if (exit_status != EXIT_SUCCESS) + goto cleanup; + + exit_status = check_file_args (fileargs[0], &filelist, RECYCLE_BIN_TYPE_DIR); + if (exit_status != EXIT_SUCCESS) + goto cleanup; + + g_slist_foreach (filelist, (GFunc) parse_record_cb, &recordlist); + + /* Fill in recycle bin metadata */ + meta.type = RECYCLE_BIN_TYPE_DIR; + meta.filename = fileargs[0]; + meta.keep_deleted_entry = FALSE; + meta.is_empty = (filelist == NULL); + meta.has_unicode_path = TRUE; - if (g_file_test (LOCALEDIR, G_FILE_TEST_IS_DIR)) - bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); - else - { - /* searching current dir is more useful on Windows */ - char *d = g_path_get_dirname (argv[0]); - char *p = g_build_filename (d, "rifiuti-l10n", NULL); - if (g_file_test (p, G_FILE_TEST_IS_DIR)) - bindtextdomain (GETTEXT_PACKAGE, p); - g_free (p); - g_free (d); - } - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); - - context = g_option_context_new (_("DIR_OR_FILE")); - g_option_context_set_summary - (context, _("Parse index files in C:\\$Recycle.bin style folder " - "and dump recycle bin data. Can also dump " - "a single index file.")); - bug_report_str = - g_strdup_printf (_("Report bugs to %s"), PACKAGE_BUGREPORT); - g_option_context_set_description (context, bug_report_str); - g_free (bug_report_str); - g_option_context_add_main_entries (context, mainoptions, "rifiuti"); - - textoptgroup = - g_option_group_new ("text", _("Plain text output options:"), - N_("Show plain text output options"), NULL, NULL); - g_option_group_set_translation_domain (textoptgroup, GETTEXT_PACKAGE); - g_option_group_add_entries (textoptgroup, textoptions); - g_option_context_add_group (context, textoptgroup); - - /* Must be done before parsing arguments since argc will be modified later */ - if (argc <= 1) + /* NULL filelist at this point means a valid empty $Recycle.bin */ + if ( !meta.is_empty && (recordlist == NULL) ) { - char *msg = g_option_context_get_help (context, FALSE, NULL); - -#ifdef G_OS_WIN32 - g_set_print_handler (gui_message); -#endif - g_print ("%s", msg); - g_free (msg); - g_option_context_free (context); - exit (EXIT_SUCCESS); + g_printerr (_("No valid recycle bin index file found.")); + g_printerr ("\n"); + exit_status = R2_ERR_BROKEN_FILE; + goto cleanup; } + recordlist = g_slist_sort (recordlist, (GCompareFunc) sort_record_by_time); - g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, my_debug_handler, NULL); - + /* detect global recycle bin version from versions of all files */ { - gboolean i; - - /* The user case where this code won't provide benefit is VERY rare, - * so don't bother doing fallback because it never worked for them - */ -#if GLIB_CHECK_VERSION(2, 40, 0) && defined (G_OS_WIN32) - char **args; - - args = g_win32_get_command_line (); - i = g_option_context_parse_strv (context, &args, &error); - g_strfreev (args); -#else - i = g_option_context_parse (context, &argc, &argv, &error); -#endif - g_option_context_free (context); - - if (!i) + GSList *l = recordlist; + if (!l) + meta.version = VERSION_NOT_FOUND; + else { - g_printerr (_("Error parsing options: %s\n"), error->message); - g_clear_error (&error); - exit (RIFIUTI_ERR_ARG); - } - } - - if (do_print_version) - { - print_version(); - exit (EXIT_SUCCESS); - } + meta.version = (int64_t) ((rbin_struct *) recordlist->data)->version; - if (!fileargs || g_strv_length (fileargs) > 1) - { - g_printerr (_("Must specify exactly one directory containing " - "$Recycle.bin index files, or one such index file, " - "as argument.\n\n")); - g_printerr (_("Run program with '-?' option for more info.\n")); - exit (RIFIUTI_ERR_ARG); - } + while (NULL != (l = l->next)) + { + if ((int64_t) ((rbin_struct *) l->data)->version != meta.version) + { + meta.version = VERSION_INCONSISTENT; + break; + } + } - if (outfilename) - { - outfile = g_fopen (outfilename, "wb"); - if (NULL == outfile) - { - g_printerr (_("Error opening file '%s' for writing: %s\n"), - outfilename, strerror (errno)); - exit (RIFIUTI_ERR_OPEN_FILE); + if (meta.version == VERSION_INCONSISTENT) + { + g_printerr (_("Index files come from multiple versions of Windows." + " Please check each file independently.")); + g_printerr ("\n"); + exit_status = R2_ERR_BROKEN_FILE; + goto cleanup; + } } } - else - outfile = stdout; - if (xml_output) + /* + * No attempt is made to distinguish difference for Vista - 8.1. + * The corrupt filesize artifact on Vista can't be reproduced, + * therefore must be very rare. + */ + switch (meta.version) { - output_format = OUTPUT_XML; - if (no_heading || always_utf8 || (NULL != delim)) - { - g_printerr (_("Plain text format options can not " - "be used in XML mode.\n")); - exit (RIFIUTI_ERR_ARG); - } + case VERSION_VISTA: meta.os_guess = OS_GUESS_VISTA; break; + case VERSION_WIN10: meta.os_guess = OS_GUESS_10; break; + default: meta.os_guess = OS_GUESS_UNKNOWN; } - if (NULL == delim) - delim = g_strndup ("\t", 2); - else + /* Print everything */ { - char *d = filter_escapes (delim); - if (d != NULL) - { - g_free (delim); - delim = d; + r2status s = prepare_output_handle (); + if (s != EXIT_SUCCESS) { + exit_status = s; + goto cleanup; } } - { - char *i = delim; - GString *str = g_string_new (g_strdup ("filtered delimiter = ")); - do - { - if (((*i) <= 0x7E) && ((*i) >= 0x20)) - str = g_string_append_c (str, *i); - else - g_string_append_printf (str, "\\x%02X", (char) (*i)); - } - while ((char) (* (++i)) != '\0'); - g_debug (str->str); - g_string_free (str, TRUE); - } + print_header (meta); + g_slist_foreach (recordlist, (GFunc) print_record_cb, NULL); + print_footer (); - g_debug ("Start basic file checking..."); + close_output_handle (); - if (!g_file_test (fileargs[0], G_FILE_TEST_EXISTS)) - { - g_printerr (_("'%s' does not exist.\n"), fileargs[0]); - exit (RIFIUTI_ERR_OPEN_FILE); - } - else if (g_file_test (fileargs[0], G_FILE_TEST_IS_DIR)) - { - populate_index_file_list (&filelist, fileargs[0]); - if (NULL == filelist) - { - /* last ditch effort: search for desktop.ini. Just print empty content - * representing empty recycle bin if found. - */ - if (!found_desktop_ini (fileargs[0])) - { - g_printerr (_("No files with name pattern '%s' are found in " - "directory. Probably not a $Recycle.bin directory.\n"), - "$Ixxxxxx.*"); - exit (RIFIUTI_ERR_OPEN_FILE); - } - } - } - else if (g_file_test (fileargs[0], G_FILE_TEST_IS_REGULAR)) + /* file descriptor should have been closed at this point */ { - fname = g_strdup (fileargs[0]); - filelist = g_slist_prepend (filelist, fname); - } - else - { - g_printerr (_("'%s' is not a normal file or directory.\n"), - fileargs[0]); - exit (RIFIUTI_ERR_OPEN_FILE); + r2status s = move_temp_file (); + if ( s != EXIT_SUCCESS ) + exit_status = s; } - g_slist_foreach (filelist, (GFunc) parse_record, &recordlist); - - /* NULL filelist at this point means a valid empty $Recycle.bin */ - if ((filelist != NULL) && (recordlist == NULL)) - { - g_printerr ("%s", _("No valid recycle bin index file found.\n")); - g_slist_foreach (filelist, (GFunc) g_free, NULL); - g_slist_free (filelist); - exit (RIFIUTI_ERR_BROKEN_FILE); - } - recordlist = g_slist_sort (recordlist, (GCompareFunc) sort_record_by_time); + cleanup: + /* Last minute error messages for accumulated non-fatal errors */ + switch (exit_status) { - GSList *l = recordlist; - int64_t ver; + case R2_ERR_USER_ENCODING: + g_printerr (_("Some entries could not be presented as correct " + "unicode path. The concerned characters are displayed " + "in escaped unicode sequences.")); + g_printerr ("\n"); + break; - if (!l) - ver = VERSION_NOT_FOUND; - else - { - ver = (int64_t) ((rbin_struct *) recordlist->data)->version; - for (; l != NULL; l = l->next) - if ((int64_t) ((rbin_struct *) l->data)->version != ver) - { - ver = VERSION_INCONSISTENT; - break; - } - } - if (!no_heading) - print_header (outfile, fileargs[0], ver, FALSE); + default: + break; } - /* TODO: store return status of each file, then exit the program with last non-zero status */ - /* TODO: store errors accumulated when parsing each file, then print a summary of errors - * after normal result, instead of dumping all errors on the spot */ - g_slist_foreach (recordlist, (GFunc) print_record, outfile); - - print_footer (outfile); - g_debug ("Cleaning up..."); - /* g_slist_free_full() available only since 2.28 */ - g_slist_foreach (recordlist, (GFunc) free_record, NULL); - g_slist_free (recordlist); - - g_slist_foreach (filelist, (GFunc) g_free, NULL); - g_slist_free (filelist); + g_slist_free_full (recordlist, (GDestroyNotify) free_record_cb); + g_slist_free_full (filelist , (GDestroyNotify) g_free ); + free_vars (); - fclose (outfile); - - g_strfreev (fileargs); - g_free (outfilename); - g_free (delim); - - exit (EXIT_SUCCESS); + return exit_status; } - -/* vim: set sw=4 ts=4 noexpandtab : */ diff -Nru rifiuti2-0.6.1/src/rifiuti-vista.h rifiuti2-0.7.0/src/rifiuti-vista.h --- rifiuti2-0.6.1/src/rifiuti-vista.h 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/src/rifiuti-vista.h 2019-05-09 01:26:08.000000000 +0000 @@ -1,5 +1,6 @@ +/* vim: set sw=4 ts=4 noexpandtab : */ /* - * Copyright (C) 2007, 2015 Abel Cheung. + * Copyright (C) 2007-2019 Abel Cheung. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,15 +33,12 @@ #include "utils.h" -#define VERSION1_FILE_SIZE 0x220 - +#define VERSION_OFFSET 0x0 #define FILESIZE_OFFSET 0x8 #define FILETIME_OFFSET 0x10 #define VERSION1_FILENAME_OFFSET 0x18 #define VERSION2_FILENAME_OFFSET 0x1C -#define RECYCLE_BIN_CLSID "645FF040-5081-101B-9F08-00AA002F954E" +#define VERSION1_FILE_SIZE ((VERSION1_FILENAME_OFFSET) + (WIN_PATH_MAX) * 2) #endif - -/* vim: set sw=4 ts=4 noexpandtab : */ diff -Nru rifiuti2-0.6.1/src/utils.c rifiuti2-0.7.0/src/utils.c --- rifiuti2-0.6.1/src/utils.c 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/src/utils.c 2019-05-09 01:26:08.000000000 +0000 @@ -1,5 +1,6 @@ +/* vim: set sw=4 ts=4 noexpandtab : */ /* - * Copyright (C) 2007, 2015 Abel Cheung. + * Copyright (C) 2007-2019 Abel Cheung. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,81 +30,666 @@ #include "config.h" +#include +#include #include +#if HAVE_SETLOCALE +#include +#endif #include "utils.h" #include +#include + +#ifdef G_OS_WIN32 +# include +# include "utils-win.h" +#endif + +/* These aren't intended for public */ +#define DECL_OPT_CALLBACK(func) \ +gboolean func (const gchar *opt_name, \ + const gchar *value, \ + gpointer data, \ + GError **err) + +static DECL_OPT_CALLBACK(_check_legacy_encoding); +static DECL_OPT_CALLBACK(_set_output_path); +static DECL_OPT_CALLBACK(_option_deprecated); +static DECL_OPT_CALLBACK(_set_opt_delim); +static DECL_OPT_CALLBACK(_set_opt_noheading); +static DECL_OPT_CALLBACK(_set_output_xml); + +/* WARNING: MUST match order of _os_guess enum */ +static char *os_strings[] = { + N_("Windows 95"), + N_("Windows NT 4.0"), + N_("Windows 98"), + N_("Windows ME"), + N_("Windows 2000"), + N_("Windows XP or 2003"), + N_("Windows 2000, XP or 2003"), + N_("Windows Vista - 8.1"), + N_("Windows 10 or above") +}; + +static int output_mode = OUTPUT_NONE; +static gboolean no_heading = FALSE; +static gboolean use_localtime = FALSE; + char *delim = NULL; + char *legacy_encoding = NULL; /*!< INFO2 only, or upon request */ + char *output_loc = NULL; + char *tmppath = NULL; /*!< used iff output_loc is defined */ + char **fileargs = NULL; + FILE *out_fh = NULL; /*!< unused for Windows console */ + +/*! These options are only effective for tab delimited mode output */ +static const GOptionEntry text_options[] = { + { + "delimiter", 't', 0, + G_OPTION_ARG_CALLBACK, _set_opt_delim, + N_("String to use as delimiter (TAB by default)"), N_("STRING") + }, + { + "no-heading", 'n', G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, _set_opt_noheading, + N_("Don't show column header and metadata"), NULL + }, + { + "always-utf8", '8', G_OPTION_FLAG_HIDDEN | G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, _option_deprecated, + N_("(This option is deprecated)"), NULL + }, + {NULL} +}; + +static const GOptionEntry main_options[] = { + { + "output", 'o', 0, + G_OPTION_ARG_CALLBACK, _set_output_path, + N_("Write output to FILE"), N_("FILE") + }, + { + "xml", 'x', G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, _set_output_xml, + N_("Output in XML format instead of tab-delimited values"), NULL + }, + { + "localtime", 'z', 0, + G_OPTION_ARG_NONE, &use_localtime, + N_("Present deletion time in time zone of local system (default is UTC)"), + NULL + }, + { + "version", 'v', G_OPTION_FLAG_NO_ARG, + G_OPTION_ARG_CALLBACK, (GOptionArgFunc) print_version_and_exit, + N_("Print version information and exit"), NULL + }, + { + G_OPTION_REMAINING, 0, 0, + G_OPTION_ARG_FILENAME_ARRAY, &fileargs, + N_("INFO2 file name"), NULL + }, + {NULL} +}; + +/*! Appended to main option group if program is INFO2 reader */ +const GOptionEntry rbinfile_options[] = { + { + "legacy-filename", 'l', 0, + G_OPTION_ARG_CALLBACK, _check_legacy_encoding, + N_("Show legacy (8.3) path if available and specify its CODEPAGE"), + N_("CODEPAGE") + }, + {NULL} +}; + +/* + * Option handling related routines + */ + +static gboolean +_set_output_mode (int mode, + GError **err) +{ + if (output_mode == mode) + return TRUE; + + if (output_mode == OUTPUT_NONE) { + output_mode = mode; + return TRUE; + } + + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Plain text format options can not be used in XML mode.")); + return FALSE; +} + +static gboolean +_set_output_xml (const gchar *opt_name, + const gchar *value, + gpointer data, + GError **err) +{ + return _set_output_mode (OUTPUT_XML, err); +} + +static gboolean +_set_opt_noheading (const gchar *opt_name, + const gchar *value, + gpointer data, + GError **err) +{ + no_heading = TRUE; + + return _set_output_mode (OUTPUT_CSV, err); +} + +/*! + * single/double quotes and backslashes have already been + * quoted / unquoted when parsing arguments. We need to + * interpret \\r, \\n etc separately + */ +static char * +_filter_escapes (const char *str) +{ + GString *result, *debug_str; + char *i = (char *) str; + + g_return_val_if_fail ( (str != NULL) && (*str != '\0'), NULL); + + result = g_string_new (NULL); + do + { + if ( *i != '\\' ) + { + result = g_string_append_c (result, *i); + continue; + } + + switch ( *(++i) ) + { + case 'r': + result = g_string_append_c (result, '\r'); break; + case 'n': + result = g_string_append_c (result, '\n'); break; + case 't': + result = g_string_append_c (result, '\t'); break; + case 'e': + result = g_string_append_c (result, '\x1B'); break; + default: + result = g_string_append_c (result, '\\'); i--; + } + } + while ( *(++i) ); + + debug_str = g_string_new ("filtered delimiter = "); + i = result->str; + do + { + if ( *i >= 0x20 && *i <= 0x7E ) /* problem during linking with g_ascii_isprint */ + debug_str = g_string_append_c (debug_str, *i); + else + g_string_append_printf (debug_str, "\\x%02X", *(unsigned char *) i); + } + while ( *(++i) ); + g_debug ("%s", debug_str->str); + g_string_free (debug_str, TRUE); + return g_string_free (result, FALSE); +} + +static gboolean +_set_opt_delim (const gchar *opt_name, + const gchar *value, + gpointer data, + GError **err) +{ + static gboolean seen = FALSE; + + if (seen) + { + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Multiple delimiter options disallowed.")); + return FALSE; + } + seen = TRUE; + + delim = (*value) ? _filter_escapes (value) : g_strdup (""); + + return _set_output_mode (OUTPUT_CSV, err); +} + +static gboolean +_set_output_path (const gchar *opt_name, + const gchar *value, + gpointer data, + GError **err) +{ + static gboolean seen = FALSE; + + if (seen) + { + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Multiple output destinations disallowed.")); + return FALSE; + } + seen = TRUE; + + if ( *value == '\0' ) + { + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Empty output filename disallowed.")); + return FALSE; + } + + if (g_file_test (value, G_FILE_TEST_EXISTS)) { + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Output destinations already exists.")); + return FALSE; + } + + output_loc = g_strdup (value); + return TRUE; +} + +static gboolean +_option_deprecated (const gchar *opt_name, + const gchar *unused, + gpointer data, + GError **err) +{ + g_printerr (_("NOTE: Option '%s' is deprecated and ignored."), opt_name); + g_printerr ("\n"); + return TRUE; +} + +static gboolean +_check_legacy_encoding (const gchar *opt_name, + const gchar *enc, + gpointer data, + GError **err) +{ + char *s; + gint e; + gboolean ret = FALSE; + static gboolean seen = FALSE; + GError *conv_err = NULL; + + if (seen) + { + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Multiple encoding options disallowed.")); + return FALSE; + } + seen = TRUE; + + if ( *enc == '\0' ) + { + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Empty encoding option disallowed.")); + return FALSE; + } + + s = g_convert ("C:\\", -1, "UTF-8", enc, NULL, NULL, &conv_err); + + if (conv_err == NULL) + { + if (strcmp ("C:\\", s) != 0) /* e.g. EBCDIC based code pages */ + { + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("'%s' can't possibly be a code page or compatible " + "encoding used by localized Windows."), enc); + } else { + legacy_encoding = g_strdup (enc); + ret = TRUE; + } + goto done_check_encoding; + } + + e = conv_err->code; + g_clear_error (&conv_err); + + switch (e) + { + case G_CONVERT_ERROR_NO_CONVERSION: + + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("'%s' encoding is not supported by glib library " + "on this system. If iconv program is present on " + "system, use 'iconv -l' for a list of possible " + "alternatives; otherwise check out following site for " + "a list of probable encodings to use:\n\n\t%s"), enc, +#ifdef G_OS_WIN32 + "https://github.com/win-iconv/win-iconv/blob/master/win_iconv.c" +#else + "https://www.gnu.org/software/libiconv/" +#endif + ); + break; + + /* Encodings not ASCII compatible can't possibly be ANSI/OEM code pages */ + case G_CONVERT_ERROR_ILLEGAL_SEQUENCE: + case G_CONVERT_ERROR_PARTIAL_INPUT: + + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("'%s' can't possibly be a code page or compatible " + "encoding used by localized Windows."), enc); + break; + + default: + g_assert_not_reached (); + } + +done_check_encoding: + + g_free (s); + return ret; +} + +static gboolean +_count_fileargs (GOptionContext *context, + GOptionGroup *group, + gpointer data, + GError **err) +{ + if (fileargs && g_strv_length (fileargs) == 1) + return TRUE; + + /* FIXME unable to pull user data, so only print generic mesg */ + g_set_error (err, G_OPTION_ERROR, G_OPTION_ERROR_FAILED, + _("Must specify exactly one file or folder argument.")); + +/* + _("Must specify exactly one INFO2 file as argument.")); + (_("Must specify exactly one directory containing " + "$Recycle.bin index files, or one such index file " + "as argument.")); */ + return FALSE; +} + +/* + * Charset conversion routines + */ + +size_t +ucs2_strnlen (const gunichar2 *str, size_t max_sz) +{ #ifdef G_OS_WIN32 -#include + + return wcsnlen_s ((const wchar_t *) str, max_sz); + +#else + + size_t i; + + if (str == NULL) + return 0; + + for (i=0; (itm_isdst; - output = g_string_sized_new (40); + output = g_string_sized_new (30); /* enough for appending numeric timezone */ len = strftime (output->str, output->allocated_len, "%Y-%m-%d %H:%M:%S", tm); if ( !len ) { g_string_free (output, TRUE); return NULL; } - - output->len = len; /* is this unorthodox? */ + output->len = len; return output; } -/* - * Turns out strftime is not so cross-platform, Windows one supports far - * less format strings than Unix counterpart. - * However, GDateTime is not available until 2.26, so bite the bullet. - * - * Returns ISO 8601 formatted time. - */ -static GString * -get_iso8601_datetime_str (time_t t) +/* Return full name of current timezone */ +static char * +get_timezone_name (struct tm *tm) { - GString *output; - extern gboolean use_localtime; - int is_dst; #ifdef G_OS_WIN32 - struct _timeb tstruct; - int offset; -#else - size_t len; - struct tm *tm; + + /* Impossible to use strftime() family on Windows, + * see get_win_timezone_name() for reason */ + + if (tm == NULL) + return g_strdup (_("Coordinated Universal Time (UTC)")); + + return get_win_timezone_name (); + +#else /* ! G_OS_WIN32 */ + + char buf[128]; + + if (tm == NULL) + return g_strdup (_("Coordinated Universal Time (UTC)")); + + if ( 0 == strftime (buf, sizeof (buf) - 1, "%Z", tm) ) + return g_strdup (_("(Failed to retrieve timezone name)")); + + return g_strdup (buf); + #endif +} - if ( ( output = get_datetime_str (t, &is_dst) ) == NULL ) - return NULL; +/*! Return ISO8601 numeric timezone, like "+0400" */ +static const char * +get_timezone_numeric (struct tm *tm) +{ + static char buf[10]; - output->str[10] = 'T'; - if ( !use_localtime ) - return g_string_append_c (output, 'Z'); + if (tm == NULL) + return "+0000"; /* ISO8601 forbids -0000 */ + /* + * Turns out strftime is not so cross-platform, Windows one supports far + * less format strings than that defined in Single Unix Specification. + * However, GDateTime is not available until 2.26, so bite the bullet. + */ #ifdef G_OS_WIN32 - - _ftime (&tstruct); + struct _timeb timeb; + _ftime (&timeb); /* * 1. timezone value is in opposite sign of what people expect * 2. it doesn't account for DST. @@ -112,25 +698,31 @@ * override timezone in C library other than $TZ, and it always use * US rule, so again, just give up and use the value */ - offset = MAX(is_dst, 0) * 60 - tstruct.timezone; - g_string_append_printf (output, "%+.2i%.2i", offset / 60, - abs(offset) % 60); - -#else + int offset = MAX(tm->tm_isdst, 0) * 60 - timeb.timezone; + g_snprintf (buf, sizeof(buf), "%+.2i%.2i", offset / 60, abs(offset) % 60); - tm = localtime (&t); - len = strftime (output->str + output->len, - output->allocated_len - output->len, "%z", tm); +#else /* !def G_OS_WIN32 */ + size_t len = strftime (buf, sizeof(buf), "%z", tm); if ( !len ) - { - g_string_free (output, TRUE); + return "+????"; +#endif + return (const char *) (&buf); +} + +/*! Return ISO 8601 formatted time with timezone */ +static GString * +get_iso8601_datetime_str (struct tm *tm) +{ + GString *output; + + if ( ( output = get_datetime_str (tm) ) == NULL ) return NULL; - } - output->len += len; -#endif + output->str[10] = 'T'; + if ( !use_localtime ) + return g_string_append_c (output, 'Z'); - return output; + return g_string_append (output, get_timezone_numeric(tm)); } time_t @@ -147,9 +739,173 @@ return (time_t) (epoch & 0xFFFFFFFF); } -/* +void +rifiuti_init (const char *progpath) +{ + if (NULL != g_getenv ("RIFIUTI_DEBUG")) + g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + my_debug_handler, NULL); + + setlocale (LC_ALL, ""); + +#ifdef G_OS_WIN32 + { + /* + * Setting GETTEXT_MUI is not enough. Though it successfully + * pick up user default locale under Windows, glib internally + * only considers g_win32_getlocale() for decision making, + * which in turn only considers thread locale. + * So we need to override g_win32_getlocale() result. And + * overriding that with LC_* would render GETTEXT_MUI useless. + */ + /* _putenv_s ("GETTEXT_MUI", "1"); */ + char *loc = get_win32_locale(); + + if (0 == _putenv_s ("LC_MESSAGES", loc)) { + g_debug ("(Windows) Use LC_MESSAGES = %s", loc); + } else { + g_warning ("Failed setting LC_MESSAGES variable, " + "move on as if no translation is used."); + } + g_free (loc); + } +#endif + + { + /* searching current dir is more useful on Windows */ + char *d = g_path_get_dirname (progpath); + char *p = g_build_filename (d, LOCALEDIR_PORTABLE, NULL); + + if (g_file_test (p, G_FILE_TEST_IS_DIR)) + { + g_debug ("Portable LOCALEDIR = %s", p); + bindtextdomain (PACKAGE, p); + } + else + bindtextdomain (PACKAGE, LOCALEDIR); + + g_free (p); + g_free (d); + } + + bind_textdomain_codeset (PACKAGE, "UTF-8"); + textdomain (PACKAGE); +} + +void +rifiuti_setup_opt_ctx (GOptionContext **context, + rbin_type type) +{ + char *bug_report_str; + GOptionGroup *group; + + g_option_context_set_translation_domain (*context, PACKAGE); + + bug_report_str = g_strdup_printf ( + /* TRANSLATOR COMMENT: argument is bug report webpage */ + _("Report bugs to %s"), PACKAGE_BUGREPORT); + g_option_context_set_description (*context, bug_report_str); + g_free (bug_report_str); + + /* main group */ + group = g_option_group_new (NULL, NULL, NULL, NULL, NULL); + + g_option_group_add_entries (group, main_options); + switch (type) + { + case RECYCLE_BIN_TYPE_FILE: + g_option_group_add_entries (group, rbinfile_options); + break; + default: break; + /* There will be option for recycle bin dir later */ + } + + g_option_group_set_parse_hooks (group, NULL, _count_fileargs); + g_option_group_set_translation_domain (group, PACKAGE); + g_option_context_set_main_group (*context, group); + + /* text group */ + /* FIXME For unknown reason, short description of option + * groups are not translated at all if using N_() */ + group = g_option_group_new ("text", + _("Plain text output options:"), + N_("Show plain text output options"), NULL, NULL); + + g_option_group_add_entries (group, text_options); + g_option_group_set_translation_domain (group, PACKAGE); + g_option_context_add_group (*context, group); + + g_option_context_set_help_enabled (*context, TRUE); +} + +r2status +rifiuti_parse_opt_ctx (GOptionContext **context, + int *argc, + char ***argv) +{ + GError *err = NULL; + char *help_msg; + gboolean ret, do_print_help = FALSE; + + /* Must be done before parsing, since argc might be modified later */ + if (*argc <= 1) + do_print_help = TRUE; + +#if GLIB_CHECK_VERSION (2, 40, 0) + { + char **args; + +# ifdef G_OS_WIN32 + args = g_win32_get_command_line (); +# else + args = g_strdupv (*argv); +# endif + ret = g_option_context_parse_strv (*context, &args, &err); + g_strfreev (args); + } +#else /* glib < 2.40 */ + ret = g_option_context_parse (*context, argc, argv, &err); +#endif + + help_msg = g_option_context_get_help (*context, FALSE, NULL); + + g_option_context_free (*context); + + if (do_print_help) + { +#ifdef G_OS_WIN32 + g_set_print_handler (gui_message); +#endif + g_print ("%s", help_msg); + g_free (help_msg); + + exit (EXIT_SUCCESS); + } + + g_free (help_msg); + + if ( !ret ) + { + g_printerr (_("Error parsing options: %s"), err->message); + g_printerr ("\n"); + g_error_free (err); + return R2_ERR_ARG; + } + + /* Some fallback values after successful option parsing... */ + if (delim == NULL) + delim = g_strdup ("\t"); + + if (output_mode == OUTPUT_NONE) + output_mode = OUTPUT_CSV; + + return EXIT_SUCCESS; +} + + +/*! * Wrapper of g_utf16_to_utf8 for big endian system. - * Always assume string is nul-terminated. + * Always assume string is nul-terminated. (Unused now?) */ char * utf16le_to_utf8 (const gunichar2 *str, @@ -174,360 +930,564 @@ #endif } -/* - * single/double quotes and backslashes have already been - * quoted / unquoted when parsing arguments. We need to - * interpret \r, \n etc separately - */ -char * -filter_escapes (const char *str) + +void +my_debug_handler (const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer data) +{ + g_printerr ("DEBUG: %s\n", message); +} + +static r2status +_get_tempfile (void) { - GString *result; - char *i = (char *)str; + int fd, e = 0; + FILE *h; + char *t; + + /* segfaults if string is pre-allocated in stack */ + t = g_strdup ("rifiuti-XXXXXX"); + + if ( -1 == ( fd = g_mkstemp (t) ) ) { + e = errno; + goto tempfile_fail; + } - if ((str == NULL) || (*str == '\0')) return NULL; + h = fdopen (fd, "wb"); + if (h == NULL) { + e = errno; + close (fd); + goto tempfile_fail; + } - result = g_string_new (NULL); - for (; *i != '\0'; i++) + out_fh = h; + tmppath = t; + return EXIT_SUCCESS; + + tempfile_fail: + + g_printerr (_("Error opening temp file for writing: %s"), + g_strerror (e)); + g_printerr ("\n"); + return R2_ERR_OPEN_FILE; +} + +/*! Scan folder and add all "$Ixxxxxx.xxx" to filelist for parsing */ +static gboolean +_populate_index_file_list (GSList **list, + const char *path) +{ + GDir *dir; + const char *direntry; + char *fname; + GPatternSpec *pattern1, *pattern2; + GError *error = NULL; + + /* + * g_dir_open returns cryptic error message or even succeeds on Windows, + * when in fact the directory content is inaccessible. + */ +#ifdef G_OS_WIN32 + if ( !can_list_win32_folder (path) ) + return FALSE; +#endif + + if (NULL == (dir = g_dir_open (path, 0, &error))) { - if (*i != '\\') - { - result = g_string_append_c (result, *i); + g_printerr (_("Error opening directory '%s': %s"), path, error->message); + g_printerr ("\n"); + g_clear_error (&error); + return FALSE; + } + + pattern1 = g_pattern_spec_new ("$I??????.*"); + pattern2 = g_pattern_spec_new ("$I??????"); + + while ((direntry = g_dir_read_name (dir)) != NULL) + { + if (!g_pattern_match_string (pattern1, direntry) && + !g_pattern_match_string (pattern2, direntry)) continue; - } - switch ((char) (*(i+1))) - { - case 'r': - result = g_string_append_c (result, '\r'); i++; break; - case 'n': - result = g_string_append_c (result, '\n'); i++; break; - case 't': - result = g_string_append_c (result, '\t'); i++; break; - case 'v': - result = g_string_append_c (result, '\v'); i++; break; - case 'f': - result = g_string_append_c (result, '\f'); i++; break; - case 'e': - result = g_string_append_c (result, '\x1B'); i++; break; - default: - result = g_string_append_c (result, '\\'); - } + fname = g_build_filename (path, direntry, NULL); + *list = g_slist_prepend (*list, fname); } - return g_string_free (result, FALSE); + + g_dir_close (dir); + + g_pattern_spec_free (pattern1); + g_pattern_spec_free (pattern2); + + return TRUE; } -void -my_debug_handler (const char *log_domain, - GLogLevelFlags log_level, - const char *message, - gpointer data) + +/*! Search for desktop.ini in folder for hint of recycle bin */ +static gboolean +found_desktop_ini (const char *path) { - if (log_level != G_LOG_LEVEL_DEBUG) return; + char *filename, *content, *found; - const char *val = g_getenv ("RIFIUTI_DEBUG"); - if (val != NULL) - g_printerr ("DEBUG: %s\n", message); + filename = g_build_filename (path, "desktop.ini", NULL); + if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) + goto desktop_ini_error; + + /* assume desktop.ini is ASCII and not something spurious */ + if (!g_file_get_contents (filename, &content, NULL, NULL)) + goto desktop_ini_error; + + /* Don't bother parsing, we don't use the content at all */ + found = strstr (content, RECYCLE_BIN_CLSID); + g_free (content); + g_free (filename); + return (found != NULL); + + desktop_ini_error: + g_free (filename); + return FALSE; } -void -maybe_convert_fprintf (FILE *file, - const char *format, ...) + +/*! Add potentially valid file(s) to list */ +int +check_file_args (const char *path, + GSList **list, + rbin_type type) { - va_list args; - char *utf_str; - const char *charset; - extern gboolean always_utf8; + g_debug ("Start basic file checking..."); + + g_return_val_if_fail ( (path != NULL) && (list != NULL), R2_ERR_INTERNAL ); + + if ( !g_file_test (path, G_FILE_TEST_EXISTS) ) + { + g_printerr (_("'%s' does not exist."), path); + g_printerr ("\n"); + return R2_ERR_OPEN_FILE; + } + else if ( (type == RECYCLE_BIN_TYPE_DIR) && + g_file_test (path, G_FILE_TEST_IS_DIR) ) + { + if ( ! _populate_index_file_list (list, path) ) + return R2_ERR_OPEN_FILE; + /* + * last ditch effort: search for desktop.ini. Just print empty content + * representing empty recycle bin if found. + */ + if ( !*list && !found_desktop_ini (path) ) + { + g_printerr (_("No files with name pattern '%s' " + "are found in directory."), "$Ixxxxxx.*"); + g_printerr ("\n"); + return R2_ERR_OPEN_FILE; + } + } + else if ( g_file_test (path, G_FILE_TEST_IS_REGULAR) ) + *list = g_slist_prepend ( *list, g_strdup (path) ); + else + { + g_printerr ( (type == RECYCLE_BIN_TYPE_DIR) ? + _("'%s' is not a normal file or directory.") : + _("'%s' is not a normal file."), path); + g_printerr ("\n"); + return R2_ERR_OPEN_FILE; + } + return EXIT_SUCCESS; +} + + +static gboolean +_local_printf (const char *format, ...) +{ + va_list args; + char *str; + + g_return_val_if_fail (format != NULL, FALSE); va_start (args, format); - utf_str = g_strdup_vprintf (format, args); + str = g_strdup_vprintf (format, args); va_end (args); - g_return_if_fail (g_utf8_validate (utf_str, -1, NULL)); + if ( !g_utf8_validate (str, -1, NULL)) { + g_critical (_("Supplied format or arguments not in UTF-8 encoding")); + g_free (str); + return FALSE; + } + +#ifdef G_OS_WIN32 + /* + * Use Windows API only if: + * 1. On Windows console + * 2. Output is not piped nor redirected + * See init_wincon_handle(). + */ + if (out_fh == NULL) + { + GError *err = NULL; + wchar_t *wstr = g_utf8_to_utf16 (str, -1, NULL, NULL, &err); + + if (err != NULL) { + g_critical (_("Error converting output from UTF-8 to UTF-16: %s"), err->message); + g_clear_error (&err); + wstr = g_utf8_to_utf16 ("(Original message failed to be displayed in UTF-16)", + -1, NULL, NULL, NULL); + } - if (always_utf8 || g_get_charset (&charset)) - fputs (utf_str, file); + puts_wincon (wstr); + g_free (wstr); + } + else +#endif + fputs (str, out_fh); + + g_free (str); + return TRUE; +} + + +r2status +prepare_output_handle (void) +{ + r2status s = EXIT_SUCCESS; + + if (output_loc) + s = _get_tempfile (); else { - char *locale_str = - g_convert_with_fallback (utf_str, -1, charset, "UTF-8", NULL, - NULL, NULL, NULL); - fputs (locale_str, file); - g_free (locale_str); +#ifdef G_OS_WIN32 + if (!init_wincon_handle()) +#endif + out_fh = stdout; } - g_free (utf_str); + return s; } void -print_header (FILE *outfile, - char *infilename, - int64_t version, - gboolean is_info2) -{ - char *utf8_filename, *ver_string; - extern int output_format; - extern char *delim; +print_header (metarecord meta) +{ + char *rbin_path; - g_return_if_fail (infilename != NULL); - g_return_if_fail (outfile != NULL); + if (no_heading) return; + + g_return_if_fail (meta.filename != NULL); g_debug ("Entering %s()", __func__); - utf8_filename = g_filename_display_name (infilename); + rbin_path = g_filename_display_name (meta.filename); - switch (output_format) + switch (output_mode) { - case OUTPUT_CSV: - maybe_convert_fprintf (outfile, _("Recycle bin path: '%s'"), - utf8_filename); - fputs ("\n", outfile); - switch (version) - { - case VERSION_NOT_FOUND: - /* TRANSLATOR COMMENT: Error when trying to determine recycle bin version */ - ver_string = g_strdup (_("??? (empty folder)")); - break; - case VERSION_INCONSISTENT: - /* TRANSLATOR COMMENT: Error when trying to determine recycle bin version */ - ver_string = g_strdup (_("??? (version inconsistent)")); + case OUTPUT_CSV: + + _local_printf (_("Recycle bin path: '%s'"), rbin_path); + _local_printf ("\n"); + + { + char *ver; + if (meta.version == VERSION_NOT_FOUND) { + /* TRANSLATOR COMMENT: Empty folder, no file avaiable for analysis */ + ver = g_strdup (_("??? (empty folder)")); + } else + ver = g_strdup_printf ("%" G_GUINT64_FORMAT, meta.version); + + _local_printf (_("Version: %s"), ver); + _local_printf ("\n"); + g_free (ver); + } + + if (( meta.type == RECYCLE_BIN_TYPE_FILE ) && ( ! meta.keep_deleted_entry )) + { + _local_printf (_("Total entries ever existed: %d"), meta.total_entry); + _local_printf ("\n"); + } + + if (meta.os_guess == OS_GUESS_UNKNOWN) + _local_printf (_("OS detection failed")); + else + _local_printf (_("OS Guess: %s"), gettext (os_strings[meta.os_guess]) ); + + _local_printf ("\n"); + + { + char *tz_name; + const char *tz_numeric; + + if (use_localtime) + { + struct tm _tm; + time_t t = time (NULL); + + localtime_r (&t, &_tm); + tz_name = get_timezone_name (&_tm); + tz_numeric = get_timezone_numeric (&_tm); + } + else + { + tz_name = get_timezone_name (NULL); + tz_numeric = get_timezone_numeric (NULL); + } + + _local_printf (_("Time zone: %s [%s]"), tz_name, tz_numeric); + _local_printf ("\n"); + + g_free (tz_name); + } + + _local_printf ("\n"); + + { + GArray *a; + char **c, *headerline; + char *colhead[] = { + /* TRANSLATOR COMMENT: appears in column header */ + N_("Index"), N_("Deleted Time"), N_("Size"), N_("Path"), NULL + }; + + a = g_array_sized_new (TRUE, TRUE, sizeof (gpointer), 5); + c = colhead; + while (*c != NULL) { + const char *t = gettext (*c++); + g_array_append_val (a, t); + } + if (meta.keep_deleted_entry) { + /* TRANSLATOR COMMENT: appears in column header, means file is restored or purged */ + char *t = _("Gone?"); + g_array_insert_val (a, 2, t); + } + + headerline = g_strjoinv (delim, (char **) a->data); + _local_printf ("%s", headerline); + _local_printf ("\n"); + + g_free (headerline); + g_array_free (a, TRUE); + } + break; - default: - ver_string = g_strdup_printf ("%" G_GUINT64_FORMAT, version); - } - maybe_convert_fprintf (outfile, _("Version: %s"), ver_string); - g_free (ver_string); - fputs ("\n\n", outfile); - - if (is_info2) - /* TRANSLATOR COMMENT: "Gone" means file is permanently deleted */ - maybe_convert_fprintf (outfile, - _("Index%sDeleted Time%sGone?%sSize%sPath"), - delim, delim, delim, delim); - else - maybe_convert_fprintf (outfile, - _("Index%sDeleted Time%sSize%sPath"), - delim, delim, delim); - fputs ("\n", outfile); - break; - - case OUTPUT_XML: - fputs ("\n", outfile); - /* No proper way to report wrong version info yet */ - fprintf (outfile, - "\n", - (is_info2 ? "file" : "dir"), MAX (version, 0)); - fprintf (outfile, " %s\n", utf8_filename); - break; - default: - g_warn_if_reached (); + case OUTPUT_XML: + /* No proper way to report wrong version info yet */ + _local_printf ( + "\n" + "\n" + " \n", + ( meta.type == RECYCLE_BIN_TYPE_FILE ) ? "file" : "dir", + MAX (meta.version, 0), rbin_path); + break; + + default: + g_assert_not_reached(); } - g_free (utf8_filename); + g_free (rbin_path); g_debug ("Leaving %s()", __func__); } void -print_record (rbin_struct *record, - FILE *outfile) +print_record_cb (rbin_struct *record) { - char *utf8_filename, *timestr = NULL; - GString *temp_timestr; - GError *error = NULL; - gboolean is_info2; - char *index; - - extern char *legacy_encoding; - extern gboolean has_unicode_filename; - extern int output_format; - extern char *delim; - extern gboolean always_utf8; + char *out_fname, *index, *size = NULL; + char *outstr = NULL, *deltime = NULL; + GString *t; + struct tm del_tm; g_return_if_fail (record != NULL); - g_return_if_fail (outfile != NULL); - is_info2 = (record->type == RECYCLE_BIN_TYPE_FILE); + index = (record->meta->type == RECYCLE_BIN_TYPE_FILE) ? + g_strdup_printf ("%u", record->index_n) : + g_strdup (record->index_s); - index = is_info2 ? g_strdup_printf ("%u", record->index_n) : - g_strdup (record->index_s); + /* + * Used to check TZ environment variable here, but no more. + * Problems with that approach is now documented elsewhere. + */ + + /* + * g_warning() further down below is an inline func that makes use of + * localtime(), and if localtime/gmtime is used here (it used to be), + * the value would be overwritten upon the g_warning call, into current + * machine time. Nasty. + * + * But why is the behavior occuring on MinGW-w64, but not even on MSYS2 + * itself or any other OS; and why only manifesting now but not earlier? + * -- 2019-03-19 + */ + if (use_localtime) + localtime_r (&(record->deltime), &del_tm); + else + gmtime_r (&(record->deltime), &del_tm); - if (has_unicode_filename && !legacy_encoding) + if ( record->legacy_path != NULL ) + out_fname = g_strdup (record->legacy_path); + else { - utf8_filename = record->utf8_filename ? - g_strdup (record->utf8_filename) : + out_fname = record->uni_path ? + g_strdup (record->uni_path) : g_strdup (_("(File name not representable in UTF-8 encoding)")); } - else /* this part is info2 only */ - { - /* - * On Windows, conversion from the file path's legacy charset to display codepage - * charset is most likely not supported unless the 2 legacy charsets happen to be - * equal. Try -> UTF-8 -> and see which step fails. - */ - utf8_filename = - g_convert (record->legacy_filename, -1, "UTF-8", legacy_encoding, - NULL, NULL, &error); - if (error) - { - g_warning (_("Error converting file name from %s encoding " - "to UTF-8 for index %s: %s"), - legacy_encoding, index, error->message); - g_clear_error (&error); - utf8_filename = - g_strdup (_("(File name not representable in UTF-8 encoding)")); - } - } - switch (output_format) + switch (output_mode) { - case OUTPUT_CSV: + case OUTPUT_CSV: - if ( NULL == ( temp_timestr = get_datetime_str (record->deltime, NULL) ) ) - { - g_warning (_("Error formatting file deletion time for record index %s."), - index); - timestr = g_strdup ("???"); - } - else - timestr = g_string_free (temp_timestr, FALSE); + if ((t = get_datetime_str (&del_tm)) != NULL) + deltime = g_string_free (t, FALSE); + else + { + g_warning (_("Error formatting file deletion time for record index %s."), + index); + deltime = g_strdup ("???"); + } - fprintf (outfile, "%s%s%s%s", index, delim, timestr, delim); - if (is_info2) - maybe_convert_fprintf (outfile, "%s%s", - record->emptied ? _("Yes") : _("No"), - delim); - fprintf (outfile, "%" G_GUINT64_FORMAT "%s", - (uint64_t) record->filesize, delim); + if ( record->filesize == G_MAXUINT64 ) /* faulty */ + size = g_strdup ("???"); + else + size = g_strdup_printf ("%" G_GUINT64_FORMAT, record->filesize); - if (always_utf8) - fprintf (outfile, "%s\n", utf8_filename); - else - { - char *shown = - g_locale_from_utf8 (utf8_filename, -1, NULL, NULL, &error); - if (error) + if (record->meta->keep_deleted_entry) { - g_warning (_("Error converting path name to display for record %s: %s"), - index, error->message); - g_clear_error (&error); - shown = g_locale_from_utf8 ( - _("(File name not representable in current language)"), - -1, NULL, NULL, NULL); + const char *purged = record->emptied ? _("Yes") : _("No"); + outstr = g_strjoin (delim, index, deltime, purged, size, out_fname, NULL); } - fprintf (outfile, "%s\n", shown); - g_free (shown); - } - break; + else + outstr = g_strjoin (delim, index, deltime, size, out_fname, NULL); - case OUTPUT_XML: + _local_printf ("%s\n", outstr); + + break; - if ( NULL == ( temp_timestr = get_iso8601_datetime_str (record->deltime) ) ) + case OUTPUT_XML: { - g_warning (_("Error formatting file deletion time for record index %s."), - index); - timestr = g_strdup ("???"); - } - else - timestr = g_string_free (temp_timestr, FALSE); + GString *s = g_string_new (NULL); + + if ((t = get_iso8601_datetime_str (&del_tm)) != NULL) + deltime = g_string_free (t, FALSE); + else + { + g_warning (_("Error formatting file deletion time for record index %s."), + index); + deltime = g_strdup ("???"); + } + + g_string_printf (s, " emptied ? 'Y' : 'N'); - fprintf (outfile, - "size=\"%" G_GUINT64_FORMAT "\">\n" - " %s\n" - " \n", - record->filesize, utf8_filename); - break; + if (record->meta->keep_deleted_entry) + g_string_append_printf (s, " emptied=\"%c\"", record->emptied ? 'Y' : 'N'); - default: - g_warn_if_reached (); + if ( record->filesize == G_MAXUINT64 ) /* faulty */ + size = g_strdup_printf (" size=\"-1\""); + else + size = g_strdup_printf (" size=\"%" G_GUINT64_FORMAT "\"", record->filesize); + s = g_string_append (s, (const gchar*) size); + + g_string_append_printf (s, + ">\n" + " \n" + " \n", out_fname); + + outstr = g_string_free (s, FALSE); + _local_printf ("%s", outstr); + } + break; + + default: + g_assert_not_reached(); } - g_free (utf8_filename); - g_free (timestr); + g_free (outstr); + g_free (out_fname); + g_free (deltime); + g_free (size); g_free (index); } void -print_version () +print_footer (void) { - maybe_convert_fprintf (stdout, "%s %s\n", PACKAGE, VERSION); - /* TRANSLATOR COMMENT: %s is software name */ - maybe_convert_fprintf (stdout, - _("%s is distributed under the " - "BSD 3-Clause License.\n"), PACKAGE); - /* TRANSLATOR COMMENT: 1st argument is software name, 2nd is official URL */ - maybe_convert_fprintf (stdout, _("Information about %s can be found on\n\n\t%s\n"), - PACKAGE, PACKAGE_URL); -} + switch (output_mode) + { + case OUTPUT_CSV: + /* do nothing */ + break; + case OUTPUT_XML: + _local_printf ("%s", "\n"); + break; + + default: + g_assert_not_reached(); + } +} void -print_footer ( - FILE *outfile) +close_output_handle (void) { - extern int output_format; + if (out_fh != NULL) + fclose (out_fh); - g_return_if_fail (outfile != NULL); +#ifdef G_OS_WIN32 + close_wincon_handle(); +#endif +} - g_debug ("Entering %s()", __func__); +r2status +move_temp_file (void) +{ + int e; - switch (output_format) - { - case OUTPUT_CSV: - /* do nothing */ - break; - - case OUTPUT_XML: - fputs ("\n", outfile); - break; - - default: - g_return_if_reached (); - break; - } - g_debug ("Leaving %s()", __func__); -} + if ( !tmppath || !output_loc ) + return EXIT_SUCCESS; -/* GUI message box */ -#ifdef G_OS_WIN32 + if ( 0 == g_rename (tmppath, output_loc) ) + return EXIT_SUCCESS; -#include + e = errno; -static char * -convert_with_fallback (const char *string, - const char *fallback) -{ - GError *err = NULL; - char *output = g_locale_from_utf8 (string, -1, NULL, NULL, &err); - if (err != NULL) - { - g_critical ("Failed to convert message to display: %s\n", err->message); - g_clear_error (&err); - return g_strdup (fallback); - } + /* TRANSLATOR COMMENT: argument is system error message */ + g_printerr (_("Error moving output data to desinated file: %s"), + g_strerror(e)); + g_printerr ("\n"); - return output; + /* TRANSLATOR COMMENT: argument is temp file location */ + g_printerr (_("Output content is left in '%s'."), tmppath); + g_printerr ("\n"); + + return R2_ERR_WRITE_FILE; } void -gui_message (const char *message) +print_version_and_exit (void) { - char *title, *output; + fprintf (stdout, "%s %s\n", PACKAGE, VERSION); + /* TRANSLATOR COMMENT: %s is software name */ + fprintf (stdout, _("%s is distributed under the " + "BSD 3-Clause License.\n"), PACKAGE); + /* TRANSLATOR COMMENT: 1st argument is software name, 2nd is official URL */ + fprintf (stdout, _("Information about %s can be found on\n\n\t%s\n"), + PACKAGE, PACKAGE_URL); - title = convert_with_fallback (_("This is a command line application"), - "This is a command line application"); - output = convert_with_fallback (message, - "Fail to display help message. Please " - "invoke program with '--help' option."); - - MessageBox (NULL, output, title, MB_OK | MB_ICONINFORMATION | MB_TOPMOST); - g_free (title); - g_free (output); + exit (EXIT_SUCCESS); } -#endif -/* vim: set sw=4 ts=4 noexpandtab : */ +void +free_record_cb (rbin_struct *record) +{ + if ( record->meta->type == RECYCLE_BIN_TYPE_DIR ) + g_free (record->index_s); + g_free (record->uni_path); + g_free (record->legacy_path); + g_free (record); +} + + +void +free_vars (void) +{ + g_strfreev (fileargs); + g_free (output_loc); + g_free (legacy_encoding); + g_free (delim); + g_free (tmppath); +} \ No newline at end of file diff -Nru rifiuti2-0.6.1/src/utils.h rifiuti2-0.7.0/src/utils.h --- rifiuti2-0.6.1/src/utils.h 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/src/utils.h 2019-05-09 01:26:08.000000000 +0000 @@ -1,5 +1,6 @@ +/* vim: set sw=4 ts=4 noexpandtab : */ /* - * Copyright (C) 2007, 2015 Abel Cheung. + * Copyright (C) 2007-2019 Abel Cheung. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,21 +28,34 @@ * SUCH DAMAGE. */ -#ifndef _UTILS_H -#define _UTILS_H +#ifndef _RIFIUTI_UTILS_H +#define _RIFIUTI_UTILS_H + +/* + * Rifiuti itself only need _POSIX_C_SOURCE == 1 for usage of + * localtime_r(); however glib2's usage of siginfo_t pushes + * the requirement further. It's undefined in some Unices. + */ +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 199309L +#endif #include #include #include #include -enum +/* Error and exit status */ +typedef enum { - RIFIUTI_ERR_ARG = 1, - RIFIUTI_ERR_OPEN_FILE, - RIFIUTI_ERR_BROKEN_FILE, - RIFIUTI_ERR_ENCODING -}; + R2_OK = 0, /* as synonym of EXIT_SUCCESS */ + R2_ERR_ARG, + R2_ERR_OPEN_FILE, + R2_ERR_BROKEN_FILE, /* file format validation failure */ + R2_ERR_WRITE_FILE, + R2_ERR_USER_ENCODING, + R2_ERR_INTERNAL = 64 +} r2status; typedef enum { @@ -49,93 +63,167 @@ RECYCLE_BIN_TYPE_DIR, } rbin_type; +/* The first 4 or 8 bytes of recycle bin index files */ enum { /* negative number means error when retrieving version info */ VERSION_INCONSISTENT = -2, VERSION_NOT_FOUND, - FORMAT_VISTA = 1, - FORMAT_WIN10, - - FORMAT_WIN98 = 4, - FORMAT_WIN2K, + /* $Recycle.bin */ + VERSION_VISTA = 1, + VERSION_WIN10, + + /* INFO / INFO2 */ + VERSION_WIN95 = 0, + VERSION_NT4 = 2, + VERSION_WIN98 = 4, + VERSION_ME_03, }; +/* + * The following enum is different from the versions above. + * This is more detailed breakdown, and for detection of exact + * Windows version from various recycle bin artifacts. + * WARNING: MUST match os_strings string array + */ +typedef enum +{ + OS_GUESS_UNKNOWN = -1, + OS_GUESS_95, + OS_GUESS_NT4, + OS_GUESS_98, + OS_GUESS_ME, + OS_GUESS_2K, + OS_GUESS_XP_03, + OS_GUESS_2K_03, /* Empty recycle bin, full detection impossible */ + OS_GUESS_VISTA, /* includes everything up to 8.1 */ + OS_GUESS_10 +} _os_guess; + enum { + OUTPUT_NONE, OUTPUT_CSV, OUTPUT_XML }; -struct _rbin_struct +/*! \struct _rbin_meta + * \brief Metadata for recycle bin + */ +typedef struct _rbin_meta { rbin_type type; - uint64_t version; /* $Recycle.bin only */ + const char *filename; + _os_guess os_guess; + int64_t version; + uint32_t recordsize; /*!< INFO2 only */ + uint32_t total_entry; /*!< 95/NT4 only */ + gboolean keep_deleted_entry; /*!< 98-03 only, add extra output column */ + gboolean is_empty; + gboolean has_unicode_path; + gboolean fill_junk; /*!< TRUE for 98/ME/2000 only, some fields padded + with junk data instaed of zeroed */ +} metarecord; + +/*! \struct _rbin_struct + * \brief Struct for single recycle bin item + */ +typedef struct _rbin_struct +{ + /*! For $Recycle.bin, version of each index file is kept here, + * while meta.version keeps the global status of whole dir */ + uint64_t version; /* $Recycle.bin only */ + + /*! Each record links to metadata for more convenient access */ + const metarecord *meta; + + /*! \brief Number is for INFO2, file name for $Recycle.bin */ union - { /* number or file name */ - uint32_t index_n; - char *index_s; + { + uint32_t index_n; /* INFO2 only */ + char *index_s; /* $Recycle.bin only */ }; - gboolean emptied; /* INFO2 only */ - unsigned char drive; /* INFO2 only */ - time_t deltime; - uint64_t filesize; - char *utf8_filename; - char *legacy_filename; /* INFO2 only */ -}; -typedef struct _rbin_struct rbin_struct; + /*! Item delection time */ + time_t deltime; -/* Glib doc is lying; GStatBuf not available until 2.25. - * Use the definition as of 2.44 */ -#if !GLIB_CHECK_VERSION(2,25,0) -# if (defined (__MINGW64_VERSION_MAJOR) || defined (_MSC_VER)) && !defined(_WIN64) -typedef struct _stat32 GStatBuf; -# else -typedef struct stat GStatBuf; -# endif -#endif + /*! Can mean cluster size or actual file/folder size */ + uint64_t filesize; + + /* despite var names, all filenames are converted to UTF-8 upon parsing */ + char *uni_path; + char *legacy_path; /* INFO2 only */ + + gboolean emptied; /* INFO2 only */ + unsigned char drive; /* INFO2 only */ +} rbin_struct; + +/* convenience macro */ +#define copy_field(field, off1, off2) memcpy((field), \ + buf + off1 ## _OFFSET, off2 ## _OFFSET - off1 ## _OFFSET) -/* Most versions of recycle bin use full PATH_MAX (260 char) to represent file paths, +/*! Every Windows use this GUID in recycle bin desktop.ini */ +#define RECYCLE_BIN_CLSID "645FF040-5081-101B-9F08-00AA002F954E" + +/* + * Most versions of recycle bin use full PATH_MAX (260 char) to store file paths, * in either ANSI or Unicode variations, except Windows 10 which uses variable size. + * However we don't want to use PATH_MAX directly since on Linux/Unix it's + * another thing. */ -#define WIN_PATH_MAX 0x104 +#define WIN_PATH_MAX 260 /* shared functions */ -time_t win_filetime_to_epoch (uint64_t win_filetime ); +void rifiuti_init (const char *progpath); -char * utf16le_to_utf8 (const gunichar2 *str , - glong len , - glong *items_read , - glong *items_written , - GError **error ); +void rifiuti_setup_opt_ctx (GOptionContext **context, + rbin_type type); -char * filter_escapes (const char *str ); +r2status rifiuti_parse_opt_ctx (GOptionContext **context, + int *argc, + char ***argv); -void print_header (FILE *outfile , - char *infilename , - int64_t version , - gboolean is_info2 ); +time_t win_filetime_to_epoch (uint64_t win_filetime); -void print_record (rbin_struct *record , - FILE *outfile ); +char * utf16le_to_utf8 (const gunichar2 *str, + glong len, + glong *items_read, + glong *items_written, + GError **error) + G_GNUC_UNUSED; -void print_footer (FILE *outfile ); +int check_file_args (const char *path, + GSList **list, + rbin_type type); -void print_version (); +r2status prepare_output_handle (void); -void my_debug_handler (const char *log_domain , - GLogLevelFlags log_level , - const char *message , - gpointer data ); +void close_output_handle (void); -void maybe_convert_fprintf (FILE *file , - const char *format , ...); -#ifdef G_OS_WIN32 -void gui_message (const char *message ); -#endif +void print_header (metarecord meta); -#endif +void print_record_cb (rbin_struct *record); -/* vim: set sw=4 ts=4 noexpandtab : */ +void print_footer (void); + +r2status move_temp_file (void); + +void print_version_and_exit (void) G_GNUC_NORETURN; + +void free_record_cb (rbin_struct *record); + +void my_debug_handler (const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer data); + +char * conv_path_to_utf8_with_tmpl (const char *str, + const char *from_enc, + const char *tmpl, + size_t *read, + r2status *st); + +void free_vars (void); + +#endif diff -Nru rifiuti2-0.6.1/src/utils-win.c rifiuti2-0.7.0/src/utils-win.c --- rifiuti2-0.6.1/src/utils-win.c 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/src/utils-win.c 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,420 @@ +/* vim: set sw=4 ts=4 noexpandtab : */ +/* + * Copyright (C) 2015-2019 Abel Cheung. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "config.h" +#include "utils-win.h" + +#include +#include +#include +#include + +#include +#include + + +HANDLE wincon_fh = NULL; + + +/* GUI message box */ +void +gui_message (const char *message) +{ + gunichar2 *title, *body; + GError *error = NULL; + + title = g_utf8_to_utf16 (_("This is a command line application"), + -1, NULL, NULL, &error); + if (error) { + g_clear_error (&error); + title = g_utf8_to_utf16 ("This is a command line application", + -1, NULL, NULL, NULL); + } + + body = g_utf8_to_utf16 (message, -1, NULL, NULL, &error); + if (error) { + g_clear_error (&error); + body = g_utf8_to_utf16 ("(Original message failed to be displayed in UTF-16)", + -1, NULL, NULL, NULL); + } + + /* Takes advantage of the fact that LPCWSTR (wchar_t) is actually 16bit on Windows */ + MessageBoxW (NULL, (LPCWSTR) body, (LPCWSTR) title, + MB_OK | MB_ICONINFORMATION | MB_TOPMOST); + g_free (title); + g_free (body); +} + +/*! + * A copy of latter part of g_win32_getlocale() + */ +#ifndef SUBLANG_SERBIAN_LATIN_BA +#define SUBLANG_SERBIAN_LATIN_BA 0x06 +#endif + +static const char * +get_win32_locale_script (int primary, + int sub) +{ + switch (primary) + { + case LANG_AZERI: + switch (sub) + { + case SUBLANG_AZERI_LATIN: return "@Latn"; + case SUBLANG_AZERI_CYRILLIC: return "@Cyrl"; + } + break; + + case LANG_SERBIAN: /* LANG_CROATIAN == LANG_SERBIAN */ + switch (sub) + { + case SUBLANG_SERBIAN_LATIN: + case SUBLANG_SERBIAN_LATIN_BA: /* Serbian (Latin) - Bosnia and Herzegovina */ + return "@Latn"; + } + break; + case LANG_UZBEK: + switch (sub) + { + case SUBLANG_UZBEK_LATIN: return "@Latn"; + case SUBLANG_UZBEK_CYRILLIC: return "@Cyrl"; + } + break; + } + return NULL; +} + +/*! + * We can't use [`g_win32_getlocale()`][1] directly. + * + * There are 3 possible source for language UI settings: + * - [`GetThreadLocale()`][2] (used by g_win32_getlocale) + * - User default language + * - System default language + * + * For GUI applications, thread locale is good enough; this is the + * default for gettext and glib on Windows. But rifiuti2 is a CLI + * program, where the caller is a console. In such case thread locale + * is solely determined by console code page, not always equal to user + * preferred language. + * + * For example, multiple West European Windows use console codepage 850, + * which GetLocaleInfo() returns en_US, not any West European languages. + * + * Here we attempt to pick up user default first, followed by thread + * locale; and do the dirty work in a manner almost identical to + * g_win32_getlocale(). + * + * [1]: https://developer.gnome.org/glib/stable/glib-Windows-Compatibility-Functions.html#g-win32-getlocale + * [2]: https://docs.microsoft.com/en-us/windows/desktop/api/winnls/nf-winnls-getthreadlocale + */ +char * +get_win32_locale (void) +{ + LCID lcid; + LANGID langid; + char *ev; + char iso639[10]; + char iso3166[10]; + const char *script; + + /* Allow user overriding locale env */ + if (((ev = getenv ("LC_ALL")) != NULL && ev[0] != '\0') || + ((ev = getenv ("LC_MESSAGES")) != NULL && ev[0] != '\0') || + ((ev = getenv ("LANG")) != NULL && ev[0] != '\0')) + return g_strdup (ev); + + lcid = LOCALE_USER_DEFAULT; + if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME , iso639 , sizeof (iso639)) || + !GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof (iso3166))) + { + lcid = GetThreadLocale(); + if (!GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME , iso639 , sizeof (iso639)) || + !GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof (iso3166))) + return g_strdup ("C"); + } + + /* Strip off the sorting rules, keep only the language part. */ + langid = LANGIDFROMLCID (lcid); + + /* Get script based on language and territory */ + script = get_win32_locale_script (PRIMARYLANGID (langid), SUBLANGID (langid)); + + return g_strconcat (iso639, "_", iso3166, script, NULL); +} + +/*! + * `strftime()` on Windows can show garbage timezone name, because its + * encoding does not match console codepage. For example, strftime %Z result + * is in CP936 encoding for zh-HK, while console codepage is CP950. + * + * OTOH, `wcsftime() %Z` would not return anything if console codepage is + * set to any non-default codepage for current system. + * + * `GetTimeZoneInformation()` returns sensible result regardless of + * console codepage setting. + */ +char * +get_win_timezone_name (void) +{ + TIME_ZONE_INFORMATION tzinfo; + wchar_t *name; + DWORD id; + char *ret; + GError *err = NULL; + + id = GetTimeZoneInformation (&tzinfo); + + switch (id) + { + case TIME_ZONE_ID_UNKNOWN: + case TIME_ZONE_ID_STANDARD: + name = tzinfo.StandardName; + break; + case TIME_ZONE_ID_DAYLIGHT: + name = tzinfo.DaylightName; + break; + default: + ret = g_win32_error_message (GetLastError ()); + g_critical ("%s", ret); + g_free (ret); + return g_strdup (_("(Failed to retrieve timezone name)")); + break; + } + + ret = g_utf16_to_utf8 ( (const gunichar2 *) name, -1, NULL, NULL, &err); + if (err == NULL) + return ret; + + g_warning ("%s", err->message); + g_error_free (err); + return g_strdup (_("(Failed to retrieve timezone name)")); +} + + +/*! + * Retrieve current user name and convert it to SID + * + * Following functions originates from [example of `GetEffectiveRightsFromAcl()`][1], + * which is not about the function itself but a _replacement_ of it (shrug). + * + * [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa446637(v=vs.85).aspx + */ +static PSID +get_user_sid (void) +{ + gboolean status; + char username[UNLEN + 1], *errmsg; + DWORD err = 0, bufsize = UNLEN + 1, sidsize = 0, domainsize = 0; + PSID sid; + LPTSTR domainname; + SID_NAME_USE sidtype; + + if ( !GetUserName (username, &bufsize) ) + { + errmsg = g_win32_error_message (GetLastError()); + g_critical (_("Failed to get current user name: %s"), errmsg); + goto getsid_fail; + } + + status = LookupAccountName (NULL, username, NULL, &sidsize, + NULL, &domainsize, &sidtype); + if ( !status ) + err = GetLastError(); + g_debug ("1st LookupAccountName(): status = %d", (int) status); + + if ( err != ERROR_INSUFFICIENT_BUFFER ) + { + errmsg = g_win32_error_message (err); + g_critical (_("LookupAccountName() failed: %s"), errmsg); + goto getsid_fail; + } + + sid = (PSID) g_malloc (sidsize); + domainname = (LPTSTR) g_malloc (domainsize); + + status = LookupAccountName (NULL, username, sid, &sidsize, + domainname, &domainsize, &sidtype); + err = status ? 0 : GetLastError(); + g_debug ("2nd LookupAccountName(): status = %d", (int) status); + g_free (domainname); /* unused */ + + if ( status != 0 ) + return sid; /* success */ + + errmsg = g_win32_error_message (err); + g_critical (_("LookupAccountName() failed: %s"), errmsg); + g_free (sid); + + getsid_fail: + g_free (errmsg); + return NULL; +} + +/*! + * Fetch ACL access mask using Authz API + */ +gboolean +can_list_win32_folder (const char *path) +{ + char *errmsg = NULL; + gunichar2 *wpath; + gboolean ret = FALSE; + PSID sid; + DWORD dw, dw2; + PSECURITY_DESCRIPTOR sec_desc; + ACCESS_MASK mask; + AUTHZ_RESOURCE_MANAGER_HANDLE authz_manager; + AUTHZ_CLIENT_CONTEXT_HANDLE authz_ctxt = NULL; + AUTHZ_ACCESS_REQUEST authz_req = { MAXIMUM_ALLOWED, NULL, NULL, 0, NULL }; + AUTHZ_ACCESS_REPLY authz_reply; + + if ( NULL == ( sid = get_user_sid() ) ) + return FALSE; + + wpath = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL); + if (wpath == NULL) + return FALSE; + + if ( !AuthzInitializeResourceManager (AUTHZ_RM_FLAG_NO_AUDIT, + NULL, NULL, NULL, NULL, &authz_manager) ) + { + errmsg = g_win32_error_message (GetLastError()); + g_printerr (_("AuthzInitializeResourceManager() failed: %s"), errmsg); + g_printerr ("\n"); + goto traverse_fail; + } + + dw = GetNamedSecurityInfoW ((wchar_t *)wpath, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION, + NULL, NULL, NULL, NULL, &sec_desc); + if ( dw != ERROR_SUCCESS ) + { + errmsg = g_win32_error_message (dw); + g_printerr (_("Failed to retrieve Discretionary ACL info for '%s': %s"), path, errmsg); + g_printerr ("\n"); + goto traverse_getacl_fail; + } + + if ( !AuthzInitializeContextFromSid (0, sid, authz_manager, + NULL, (LUID) {0} /* unused */, NULL, &authz_ctxt) ) + { + errmsg = g_win32_error_message (GetLastError()); + g_printerr (_("AuthzInitializeContextFromSid() failed: %s"), errmsg); + g_printerr ("\n"); + goto traverse_getacl_fail; + } + + authz_reply = (AUTHZ_ACCESS_REPLY) { 1, &mask, &dw2, &dw }; /* last 2 param unused */ + if ( !AuthzAccessCheck (0, authz_ctxt, &authz_req, NULL, sec_desc, + NULL, 0, &authz_reply, NULL ) ) + { + errmsg = g_win32_error_message (GetLastError()); + g_printerr (_("AuthzAccessCheck() failed: %s"), errmsg); + g_printerr ("\n"); + } + else + { + /* + * We only need permission to list directory; even directory traversal is + * not needed, because we are going to access the files directly later. + * Unlike Unix, no read permission on parent folder is needed to list + * files within. + */ + if ( (mask & FILE_LIST_DIRECTORY) == FILE_LIST_DIRECTORY && + (mask & FILE_READ_EA) == FILE_READ_EA ) + ret = TRUE; + else { + g_printerr (_("Error listing directory: Insufficient permission.")); + g_printerr ("\n"); + } + + /* use glib type to avoid including more header */ + g_debug ("Access Mask hex for '%s': 0x%X", path, (guint32) mask); + } + + AuthzFreeContext (authz_ctxt); + + traverse_getacl_fail: + LocalFree (sec_desc); + AuthzFreeResourceManager (authz_manager); + + traverse_fail: + g_free (sid); + g_free (errmsg); + g_free (wpath); + return ret; +} + +/*! + * Initialize console handle under Windows + * + * Used only when output is Windows native console. For all other cases + * unix-style file stream is used. + */ +gboolean +init_wincon_handle (void) +{ + HANDLE h = GetStdHandle (STD_OUTPUT_HANDLE); + + /* + * FILE_TYPE_CHAR only happens when output is a native Windows cmd + * console. For Cygwin and Msys shell environments (and output redirection), + * GetFileType() would return FILE_TYPE_PIPE. In those cases printf + * family outputs UTF-8 data properly. Only Windows console needs to be + * dealt with using wide char API. + */ + if (GetFileType (h) == FILE_TYPE_CHAR) { + wincon_fh = h; + return TRUE; + } + return FALSE; +} + +void +close_wincon_handle (void) +{ + if (wincon_fh != NULL) + CloseHandle (wincon_fh); + return; +} + +gboolean +puts_wincon (const wchar_t *wstr) +{ + g_return_val_if_fail (wstr != NULL, FALSE); + g_return_val_if_fail (wincon_fh != NULL, FALSE); + + if (WriteConsoleW (wincon_fh, wstr, wcslen (wstr), NULL, NULL)) + return TRUE; + else + return FALSE; +} diff -Nru rifiuti2-0.6.1/src/utils-win.h rifiuti2-0.7.0/src/utils-win.h --- rifiuti2-0.6.1/src/utils-win.h 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/src/utils-win.h 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,44 @@ +/* vim: set sw=4 ts=4 noexpandtab : */ +/* + * Copyright (C) 2007-2019 Abel Cheung. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _RIFIUTI_UTILS_WIN_H +#define _RIFIUTI_UTILS_WIN_H + +#include + +void gui_message (const char *message ); +char * get_win32_locale (void); +char * get_win_timezone_name (void); +gboolean can_list_win32_folder (const char *dir ); +gboolean init_wincon_handle (void); +void close_wincon_handle (void); +gboolean puts_wincon (const wchar_t *wstr ); + +#endif diff -Nru rifiuti2-0.6.1/test/atlocal.in rifiuti2-0.7.0/test/atlocal.in --- rifiuti2-0.6.1/test/atlocal.in 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/atlocal.in 2019-05-09 01:26:08.000000000 +0000 @@ -1,21 +1,61 @@ # -*- shell-script -*- +# vim: set filetype=sh sw=4 ts=4 noexpandtab : # @configure_input@ # Configurable variable values for Autoconf test suite. # prog checks +iconv="@ICONV@" has_iconv="test 'x@ICONV@' != 'x'" -has_xmllint="test 'x@XMLLINT@' != 'x'" -has_perl="test 'x@PERL@' != 'x'" xmllint="@XMLLINT@" +has_xmllint="test 'x@XMLLINT@' != 'x'" +awk="@AWK@" # detect & set env -is_mingw="test 'x@os_mingw@' != 'x'" -is_bsd="test 'x@os_bsd@' != 'x'" -getcharset="$abs_buildddir/getcharset" -set_ascii="env CHARSET=ASCII" -set_utf8="env CHARSET=UTF-8" +is_mingw="test x@is_mingw@ != x" +is_cygwin="test x@is_cygwin@ != x" # abbrev progf="$abs_top_builddir/src/rifiuti" progd="$abs_top_builddir/src/rifiuti-vista" sample="$abs_srcdir/samples" + +# +# For some systems, glib may or may not be using system iconv +# (e.g. Solaris and FreeBSD), therefore simply finding out +# supported encoding from iconv program is not enough. +# +# In general, there is no cross platform way of specifying +# encoding names. To rub salt into wounds, even glib itself +# appears to append different encoding aliases on different +# OSes... (such as using win_iconv on Windows) +# +# Have to give up specifying charset names according to OS, and +# resort to checking runtime behavior instead. +# +for i in IBM-037 IBM037 CP037; do + if ./test_glib_iconv "$i"; then + ebcdic_latin1_name="$i" + break + fi +done + +for i in CP936 MS936 Windows-936 GBK csGBK; do + if ./test_glib_iconv "$i"; then + gbk_name="$i" + break + fi +done + +for i in CP932 Windows-932 IBM-943 SJIS JIS_X0208 SHIFT_JIS SHIFT-JIS; do + if ./test_glib_iconv "$i"; then + sjis_name="$i" + break + fi +done + +for i in CP950 Windows-950 Big5 BIG5; do + if ./test_glib_iconv "$i"; then + big5_name="$i" + break + fi +done \ No newline at end of file diff -Nru rifiuti2-0.6.1/test/ax_at_check_pattern.m4 rifiuti2-0.7.0/test/ax_at_check_pattern.m4 --- rifiuti2-0.6.1/test/ax_at_check_pattern.m4 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/ax_at_check_pattern.m4 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,178 @@ +# SYNOPSIS +# +# AX_AT_CHECK_PATTERN(COMMANDS, [STATUS], [STDOUT-RE], [STDERR-RE], [RUN-IF-FAIL], [RUN-IF-PASS]) +# AX_AT_DIFF_PATTERN(PATTERN-FILE, TEST-FILE, [STATUS=0], [DIFFERENCES]) +# AX_AT_DATA_CHECK_PATTERN_AWK(FILENAME) +# +# DESCRIPTION +# +# AX_AT_CHECK_PATTERN() executes a test similar to AT_CHECK(), except +# that stdout and stderr are awk regular expressions (REs). +# +# NOTE: as autoconf uses [] for quoting, the use of [brackets] in the RE +# arguments STDOUT-RE and STDERR-RE can be awkward and require careful +# extra quoting, or quadrigraphs '@<:@' (for '[') and '@:>@' (for ']'). +# +# awk is invoked via $AWK, which defaults to "awk" if unset or empty. +# +# Implemented using AT_CHECK() with a custom value for $at_diff that +# invokes diff with an awk post-processor. +# +# +# AX_AT_DIFF_PATTERN() checks that the PATTERN-FILE applies to TEST-FILE. +# If there are differences, STATUS will be 1 and they should be DIFFERENCES. +# +# +# AX_AT_DATA_CHECK_PATTERN_AWK() creates FILENAME with the contents of +# the awk script used. +# +# +# The latest version of this macro and a supporting test suite are at: +# https://github.com/lukem/pyrediff +# +# +# LICENSE +# +# Copyright (c) 2013-2017 Luke Mewburn +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. This file is offered as-is, +# without any warranty. + +#serial 13 + +m4_define([_AX_AT_CHECK_PATTERN_AWK], +[[BEGIN { exitval=0 } + +function set_mode(m) +{ + mode=m + lc=0 + rc=0 +} + +function mismatch() +{ + print mode + for (i = 0; i < lc; i++) { + print ll@<:@i@:>@ + } + print "---" + for (i = 0; i < rc; i++) { + print rl@<:@i@:>@ + } + set_mode("") + exitval=1 +} + +function change_mode(m) +{ + if (lc > rc) { + mismatch() + } + set_mode(m) +} + +@S|@1 ~ /^@<:@0-9@:>@+(,@<:@0-9@:>@+)?@<:@ad@:>@@<:@0-9@:>@+(,@<:@0-9@:>@+)?@S|@/ { + change_mode("") + print + exitval=1 + next +} + +@S|@1 ~ /^@<:@0-9@:>@+(,@<:@0-9@:>@+)?@<:@c@:>@@<:@0-9@:>@+(,@<:@0-9@:>@+)?@S|@/ { + change_mode(@S|@1) + next +} + +mode == "" { + print @S|@0 + next +} + +@S|@1 == "<" { + ll@<:@lc@:>@ = @S|@0 + lc = lc + 1 + next +} + +@S|@1 == "---" { + next +} + +@S|@1 == ">" { + rl@<:@rc@:>@ = @S|@0 + rc = rc + 1 + if (rc > lc) { + mismatch() + next + } + pat = "^" substr(ll@<:@rc-1@:>@, 3) "@S|@" + str = substr(@S|@0, 3) + if (str !~ pat) { + mismatch() + } + next +} + +{ + print "UNEXPECTED LINE: " @S|@0 + exit 10 +} + +END { + change_mode("") + exit exitval +} +]]) + + +m4_defun([_AX_AT_CHECK_PATTERN_PREPARE], [dnl +dnl Can't use AC_PROG_AWK() in autotest. +AS_VAR_IF([AWK], [], [AWK=awk]) + +AS_REQUIRE_SHELL_FN([ax_at_diff_pattern], + [AS_FUNCTION_DESCRIBE([ax_at_diff_pattern], [PATTERN OUTPUT], + [Diff PATTERN OUTPUT and elide change lines where the RE pattern matches])], + [diff "$[]1" "$[]2" | $AWK '_AX_AT_CHECK_PATTERN_AWK']) +])dnl _AX_AT_CHECK_PATTERN_PREPARE + + +m4_defun([AX_AT_CHECK_PATTERN], [dnl +AS_REQUIRE([_AX_AT_CHECK_PATTERN_PREPARE]) +_ax_at_check_pattern_prepare_original_at_diff="$at_diff" +at_diff='ax_at_diff_pattern' +AT_CHECK(m4_expand([$1]), [$2], m4_expand([$3]), m4_expand([$4]), + [at_diff="$_ax_at_check_pattern_prepare_original_at_diff";$5], + [at_diff="$_ax_at_check_pattern_prepare_original_at_diff";$6]) +])dnl AX_AT_CHECK_PATTERN + + +m4_defun([AX_AT_DIFF_PATTERN], [dnl +AS_REQUIRE([_AX_AT_CHECK_PATTERN_PREPARE]) +AT_CHECK([ax_at_diff_pattern $1 $2], [$3], [$4]) +])dnl AX_AT_DIFF_PATTERN + + +m4_defun([AX_AT_DATA_CHECK_PATTERN_AWK], [dnl +m4_if([$1], [], [m4_fatal([$0: argument 1: empty filename])])dnl +AT_DATA([$1], [dnl +# check_pattern.awk +# Generated by AX_AT_DATA_CHECK_PATTERN_AWK() +# from https://github.com/lukem/pyrediff +# +# awk script to process the output of "diff PATTERN OUTPUT" removing lines +# where the difference is a PATTERN line that exactly matches an OUTPUT line. +# +# +# Copyright (c) 2013-2017 Luke Mewburn +# +# Copying and distribution of this file, with or without modification, +# are permitted in any medium without royalty provided the copyright +# notice and this notice are preserved. This file is offered as-is, +# without any warranty. +# +] +_AX_AT_CHECK_PATTERN_AWK) +])dnl AX_AT_DATA_CHECK_PATTERN_AWK diff -Nru rifiuti2-0.6.1/test/getcharset.c rifiuti2-0.7.0/test/getcharset.c --- rifiuti2-0.6.1/test/getcharset.c 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/getcharset.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -#include -#include -#include - -int main() -{ - const char *charset; - setlocale(LC_ALL, ""); - g_get_charset(&charset); - puts (charset); -} diff -Nru rifiuti2-0.6.1/test/Makefile.am rifiuti2-0.7.0/test/Makefile.am --- rifiuti2-0.6.1/test/Makefile.am 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/Makefile.am 2019-05-09 01:26:08.000000000 +0000 @@ -1,3 +1,5 @@ +# vim: set sw=4 ts=4 noexpandtab : +# # Copyright (C) 2015 Abel Cheung. # All rights reserved. # @@ -36,56 +38,62 @@ echo 'm4_define([AT_PACKAGE_STRING], [$(PACKAGE_STRING)])' && \ echo 'm4_define([AT_PACKAGE_BUGREPORT], [$(PACKAGE_BUGREPORT)])'; \ echo 'm4_define([AT_PACKAGE_URL], [$(PACKAGE_URL)])'; \ - } >'$(srcdir)/package.m4' + } > $@ -TESTSUITE = $(srcdir)/testsuite +noinst_PROGRAMS = test_glib_iconv -EXTRA_DIST = \ - rifiuti.dtd \ - samples \ - $(srcdir)/package.m4 \ - testsuite.at \ - $(TESTSUITE) \ - atlocal.in +test_glib_iconv_LDADD = $(GLIB_LIBS) +test_glib_iconv_CFLAGS = $(GLIB_CFLAGS) + +TESTSUITE = $(srcdir)/testsuite -AM_CPPFLAGS = $(GLIB_CFLAGS) +INC_AT_FILES = \ + ax_at_check_pattern.m4 \ + test_cli_option.at \ + test_common.at \ + test_encoding.at \ + test_faulty_dir.at \ + test_parse_dir.at \ + test_parse_info2.at \ + test_read_write.at \ + test_xml.at -noinst_PROGRAMS = getcharset +AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te +AUTOTEST = $(AUTOM4TE) --language=autotest -getcharset_LDADD = $(GLIB_LIBS) +# Autotest won't use AC_ macros, therefore pass the result +# of AC_CHECK_PROGS as external variables +TEST_FLAGS_EXTRA = 'AWK=$(AWK)' check-local: atconfig atlocal $(TESTSUITE) - $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) + $(SHELL) '$(TESTSUITE)' $(TESTSUITEFLAGS) $(TEST_FLAGS_EXTRA) installcheck-local: atconfig atlocal $(TESTSUITE) $(SHELL) '$(TESTSUITE)' AUTOTEST_PATH='$(bindir)' \ - $(TESTSUITEFLAGS) + $(TESTSUITEFLAGS) $(TEST_FLAGS_EXTRA) clean-local: test ! -f '$(TESTSUITE)' || $(SHELL) '$(TESTSUITE)' --clean -rm -f *.tmp -AUTOM4TE = $(SHELL) $(top_srcdir)/missing --run autom4te -AUTOTEST = $(AUTOM4TE) --language=autotest $(TESTSUITE): $(srcdir)/testsuite.at $(srcdir)/package.m4 $(INC_AT_FILES) $(AUTOTEST) -I '$(srcdir)' -o $@.tmp $@.at mv $@.tmp $@ -INC_AT_FILES = \ - test_cli_option.at \ - test_common.at \ - test_encoding.at \ - test_parse_info2.at \ - test_parse_dir.at \ - test_read_write.at \ - test_xml.at - -EXTRA_DIST += $(INC_AT_FILES) +EXTRA_DIST = \ + rifiuti.dtd \ + samples \ + $(srcdir)/package.m4 \ + testsuite.at \ + $(TESTSUITE) \ + atlocal.in \ + $(INC_AT_FILES) \ + $(NULL) DISTCLEANFILES = atconfig atlocal CLEANFILES = testsuite.log -MAINTAINERCLEANFILES = testsuite $(srcdir)/package.m4 - -GITIGNOREFILES = testsuite.dir - --include $(top_srcdir)/git.mk +MAINTAINERCLEANFILES = \ + $(TESTSUITE) \ + $(srcdir)/package.m4 \ + $(srcdir)/Makefile.in \ + $(NULL) Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/dir-2019-uncpath/$I4OZLXW.bmp and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/dir-2019-uncpath/$I4OZLXW.bmp differ Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/dir-2019-uncpath/$IW0RYW0.rtf and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/dir-2019-uncpath/$IW0RYW0.rtf differ Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/dir-2019-uncpath/$IYDW1CC.rtf and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/dir-2019-uncpath/$IYDW1CC.rtf differ diff -Nru rifiuti2-0.6.1/test/samples/dir-2019-uncpath.txt rifiuti2-0.7.0/test/samples/dir-2019-uncpath.txt --- rifiuti2-0.6.1/test/samples/dir-2019-uncpath.txt 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/dir-2019-uncpath.txt 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,9 @@ +Recycle bin path: 'dir-2019-uncpath' +Version: 2 +OS Guess: Windows 10 or above +Time zone: Coordinated Universal Time (UTC) [+0000] + +Index Deleted Time Size Path +$IW0RYW0.rtf 2019-05-07 20:56:04 7 \\WIN-163RLA0PH3N\somewhere\hahaha.rtf +$I4OZLXW.bmp 2019-05-07 21:01:01 1714662 \\WIN-163RLA0PH3N\somewhere\পরীক্ষা.bmp +$IYDW1CC.rtf 2019-05-07 21:08:55 7 \\WIN-163RLA0PH3N\somewhere\hahaha.rtf diff -Nru rifiuti2-0.6.1/test/samples/dir-bad-uni/desktop.ini rifiuti2-0.7.0/test/samples/dir-bad-uni/desktop.ini --- rifiuti2-0.6.1/test/samples/dir-bad-uni/desktop.ini 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/dir-bad-uni/desktop.ini 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,3 @@ +[.ShellClassInfo] +CLSID={645FF040-5081-101B-9F08-00AA002F954E} +LocalizedResourceName=@%SystemRoot%\system32\shell32.dll,-8964 Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/dir-bad-uni/$I77T7B0.ahk and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/dir-bad-uni/$I77T7B0.ahk differ Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/dir-bad-uni/$I77T7B1.ahk and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/dir-bad-uni/$I77T7B1.ahk differ Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/dir-bad-uni/$I77T7B2.ahk and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/dir-bad-uni/$I77T7B2.ahk differ Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/dir-bad-uni/$I77T7B3.ahk and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/dir-bad-uni/$I77T7B3.ahk differ diff -Nru rifiuti2-0.6.1/test/samples/dir-bad-uni.txt rifiuti2-0.7.0/test/samples/dir-bad-uni.txt --- rifiuti2-0.6.1/test/samples/dir-bad-uni.txt 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/dir-bad-uni.txt 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,10 @@ +Recycle bin path: 'dir-bad-uni' +Version: 1 +OS Guess: Windows Vista - 8.1 +Time zone: Coordinated Universal Time (UTC) [+0000] + +Index Deleted Time Size Path +$I77T7B0.ahk 2019-04-14 11:44:43 324 D:\𐂂𐌰𐎅𐠔𨋢.ahk +$I77T7B1.ahk 2019-04-14 11:44:43 324 D:\𐂂𐌰𐎅𐠔<\uD860>.<\uDEE2>.ahk +$I77T7B2.ahk 2019-04-14 11:44:43 324 D:\<\uDC82><\uD800>𐌰𐎅𐠔𨋢.ahk +$I77T7B3.ahk 2019-04-14 11:44:43 324 D:\𐂂𐌰<\u05FF>𐎅𐠔𨋢.ahk diff -Nru rifiuti2-0.6.1/test/samples/dir-empty.txt rifiuti2-0.7.0/test/samples/dir-empty.txt --- rifiuti2-0.6.1/test/samples/dir-empty.txt 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/dir-empty.txt 2019-05-09 01:26:08.000000000 +0000 @@ -1,4 +1,6 @@ Recycle bin path: 'dir-empty' Version: ??? (empty folder) +OS detection failed +Time zone: Coordinated Universal Time (UTC) [+0000] Index Deleted Time Size Path diff -Nru rifiuti2-0.6.1/test/samples/dir-mixed.txt rifiuti2-0.7.0/test/samples/dir-mixed.txt --- rifiuti2-0.6.1/test/samples/dir-mixed.txt 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/dir-mixed.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -Recycle bin path: 'dir-mixed' -Version: ??? (version inconsistent) - -Index Deleted Time Size Path -$IEQWWMF.exe 2007-09-21 08:38:30 679936 C:\Users\student\Downloads\fau-1.3.0.2355(rc3)\fau\FAU.x86\fmdata.exe -$IDNLPD4.exe 2015-04-19 10:50:51 872448 C:\Temp\FAU\FAU.x86\dd.exe diff -Nru rifiuti2-0.6.1/test/samples/dir-sample1.txt rifiuti2-0.7.0/test/samples/dir-sample1.txt --- rifiuti2-0.6.1/test/samples/dir-sample1.txt 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/dir-sample1.txt 2019-05-09 01:26:08.000000000 +0000 @@ -1,11 +1,13 @@ Recycle bin path: 'dir-sample1' Version: 1 +OS Guess: Windows Vista - 8.1 +Time zone: Coordinated Universal Time (UTC) [+0000] Index Deleted Time Size Path $IUVFB0M.rtf 2007-09-21 06:32:46 155 C:\Users\student\Desktop\New Rich Text Document.rtf $I0JGHX7 2007-09-21 06:47:49 0 C:\Users\student\Desktop\New Folder 1 $I1IS2OK.txt 2007-09-21 06:48:13 0 C:\Users\student\Desktop\New Text Document blah.txt -$IYAR1YY.exe 2007-09-21 07:54:23 20480 C:\dd.exe +$IYAR1YY.exe 2007-09-21 07:54:23 ??? C:\dd.exe $I95CUKU 2007-09-21 08:02:59 4096 C:\Users\student\Downloads\fau-1.3.0.2355(rc3)\fau\FAU.x86\sparsefile $IHMU3NR.zip 2007-09-21 08:17:19 5025829 C:\Users\student\Downloads\fau-1.3.0.2355(rc3).zip $I7FV8IY.exe 2007-09-21 08:23:18 153478296 C:\Users\student\Downloads\VMware-server-installer-1.0.4-56528.exe @@ -15,5 +17,5 @@ $IEQWWMF.exe 2007-09-21 08:38:30 679936 C:\Users\student\Downloads\fau-1.3.0.2355(rc3)\fau\FAU.x86\fmdata.exe $IFRN1CZ.exe 2007-09-21 08:38:30 110592 C:\Users\student\Downloads\fau-1.3.0.2355(rc3)\fau\FAU.x86\wipe.exe $IW527XU.exe 2007-09-21 08:38:30 331776 C:\Users\student\Downloads\fau-1.3.0.2355(rc3)\fau\FAU.x86\volume_dump.exe -$IC6GEAW.exe 2007-09-21 08:50:16 20480 C:\Users\student\Downloads\fau-1.3.0.2355(rc3)\fau\FAU.x86\dd.exe +$IC6GEAW.exe 2007-09-21 08:50:16 ??? C:\Users\student\Downloads\fau-1.3.0.2355(rc3)\fau\FAU.x86\dd.exe $IZUFRX4.vmdk 2007-09-21 09:22:25 10737418240 C:\Virtual Machines\Windows XP Professional\Windows XP Professional-flat.vmdk diff -Nru rifiuti2-0.6.1/test/samples/dir-win10-01.txt rifiuti2-0.7.0/test/samples/dir-win10-01.txt --- rifiuti2-0.6.1/test/samples/dir-win10-01.txt 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/dir-win10-01.txt 2019-05-09 01:26:08.000000000 +0000 @@ -1,5 +1,7 @@ Recycle bin path: 'dir-win10-01' Version: 2 +OS Guess: Windows 10 or above +Time zone: Coordinated Universal Time (UTC) [+0000] Index Deleted Time Size Path $IKEGS1G 2015-04-04 17:19:52 0 C:\Users\tester\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/INFO2-03-tw-uncpath and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/INFO2-03-tw-uncpath differ diff -Nru rifiuti2-0.6.1/test/samples/INFO2-03-tw-uncpath.txt rifiuti2-0.7.0/test/samples/INFO2-03-tw-uncpath.txt --- rifiuti2-0.6.1/test/samples/INFO2-03-tw-uncpath.txt 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO2-03-tw-uncpath.txt 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,10 @@ +Recycle bin path: 'INFO2-03-tw-uncpath' +Version: 5 +OS Guess: Windows XP or 2003 +Time zone: Coordinated Universal Time (UTC) [+0000] + +Index Deleted Time Gone? Size Path +1 2019-05-05 17:08:49 No 3153920 \\Vm-2003-r2-tw\Temp\文件\Downloads\HxDSetup.zip +2 2019-05-05 17:12:32 Yes 16384 \\Vm-2003-r2-tw\Temp\文件\Downloads\README.html +3 2019-05-05 17:14:38 Yes 24576 \\Vm-2003-r2-tw\Temp\文件\Downloads\____PC__FONTS.bin +4 2019-05-05 17:14:46 No 708608 \\Vm-2003-r2-tw\Temp\文件\Downloads\ws_ftple.exe Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/INFO2-2k-cht-1 and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/INFO2-2k-cht-1 differ diff -Nru rifiuti2-0.6.1/test/samples/INFO2-2k-cht-1.txt rifiuti2-0.7.0/test/samples/INFO2-2k-cht-1.txt --- rifiuti2-0.6.1/test/samples/INFO2-2k-cht-1.txt 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO2-2k-cht-1.txt 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,11 @@ +Recycle bin path: 'INFO2-2k-cht-1' +Version: 5 +OS Guess: Windows 2000 +Time zone: Coordinated Universal Time (UTC) [+0000] + +Index Deleted Time Gone? Size Path +1 2019-03-31 18:27:32 No 4096 C:\Documents and Settings\Nobody\桌面\ABC新增文字文件.txt +2 2019-03-31 18:27:53 No 4096 C:\Documents and Settings\Nobody\桌面\Mozilla Firefox.lnk +3 2019-03-31 18:32:24 No 958464 C:\Documents and Settings\Nobody\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.bmp +4 2019-03-31 19:40:16 No 0 C:\temp\Ödüllü 混合中文字 تشكيل.doc +5 2019-03-31 19:42:58 No 0 C:\temp\تشكيل.doc Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/INFO2-2k-tw-uncpath and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/INFO2-2k-tw-uncpath differ diff -Nru rifiuti2-0.6.1/test/samples/INFO2-2k-tw-uncpath.txt rifiuti2-0.7.0/test/samples/INFO2-2k-tw-uncpath.txt --- rifiuti2-0.6.1/test/samples/INFO2-2k-tw-uncpath.txt 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO2-2k-tw-uncpath.txt 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,10 @@ +Recycle bin path: 'INFO2-2k-tw-uncpath' +Version: 5 +OS Guess: Windows 2000 +Time zone: Coordinated Universal Time (UTC) [+0000] + +Index Deleted Time Gone? Size Path +1 2019-05-06 00:46:50 No 16384 \\Vm-2k-tw\哈囉\DOWNLO~1\HEXTOO~1.ZIP +2 2019-05-06 00:46:56 Yes 3231744 \\Vm-2k-tw\哈囉\DOWNLO~1\FILEZI~1.EXE +3 2019-05-06 00:46:59 No 0 \\Vm-2k-tw\哈囉\DOWNLO~1\bin +4 2019-05-06 00:47:58 No 0 \\Vm-2k-tw\哈囉\DOWNLO~1\冏.doc diff -Nru rifiuti2-0.6.1/test/samples/INFO2-empty.txt rifiuti2-0.7.0/test/samples/INFO2-empty.txt --- rifiuti2-0.6.1/test/samples/INFO2-empty.txt 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO2-empty.txt 2019-05-09 01:26:08.000000000 +0000 @@ -1,4 +1,6 @@ Recycle bin path: 'INFO2-empty' Version: 5 +OS Guess: Windows 2000, XP or 2003 +Time zone: Coordinated Universal Time (UTC) [+0000] Index Deleted Time Gone? Size Path Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/INFO2-ME-en-1 and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/INFO2-ME-en-1 differ diff -Nru rifiuti2-0.6.1/test/samples/INFO2-ME-en-1.txt rifiuti2-0.7.0/test/samples/INFO2-ME-en-1.txt --- rifiuti2-0.6.1/test/samples/INFO2-ME-en-1.txt 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO2-ME-en-1.txt 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,12 @@ +Recycle bin path: 'INFO2-ME-en-1' +Version: 5 +OS Guess: Windows ME +Time zone: Coordinated Universal Time (UTC) [+0000] + +Index Deleted Time Gone? Size Path +1 2015-05-10 12:43:36 No 4096 C:\WINDOWS\Desktop\Windows Media Player.lnk +2 2015-05-10 12:45:41 No 0 C:\My Documents\Temp Folder é à ä ç +3 2015-05-18 22:15:32 Yes 495616 C:\My Documents\Copy of My Music +3 2015-05-18 23:38:34 Yes 4096 C:\My Documents\bin-me.zip +4 2015-05-18 23:38:53 Yes 4096 C:\My Documents\bin-me.zip +5 2015-05-18 23:39:31 No 8192 C:\WINDOWS\Desktop\New WordPad Document.doc Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/INFO2-me-en-uncpath and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/INFO2-me-en-uncpath differ diff -Nru rifiuti2-0.6.1/test/samples/INFO2-me-en-uncpath.txt rifiuti2-0.7.0/test/samples/INFO2-me-en-uncpath.txt --- rifiuti2-0.6.1/test/samples/INFO2-me-en-uncpath.txt 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO2-me-en-uncpath.txt 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,9 @@ +Recycle bin path: 'INFO2-me-en-uncpath' +Version: 5 +OS Guess: Windows ME +Time zone: Coordinated Universal Time (UTC) [+0000] + +Index Deleted Time Gone? Size Path +1 2019-05-02 16:31:45 No 420864 \\Vm-me\temp\Documents\langastas.xpi +2 2019-05-02 16:38:56 No 4773888 \\Vm-me\temp\Documents\3DPacmanSetupTrial.exe +3 2019-05-02 16:43:15 Yes 4608 \\Vm-me\temp\Documents\fgabida.lst diff -Nru rifiuti2-0.6.1/test/samples/INFO2-sample1-alt.txt rifiuti2-0.7.0/test/samples/INFO2-sample1-alt.txt --- rifiuti2-0.6.1/test/samples/INFO2-sample1-alt.txt 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO2-sample1-alt.txt 2019-05-09 01:26:08.000000000 +0000 @@ -1,5 +1,7 @@ Recycle bin path: 'INFO2-sample1' Version: 5 +OS Guess: Windows XP or 2003 +Time zone: Coordinated Universal Time (UTC) [+0000] Index Deleted Time Gone? Size Path 44 2008-10-28 15:53:42 No 4096 C:\DOCUME~1\ALLUSE~1\Desktop\有道桌~1.LNK diff -Nru rifiuti2-0.6.1/test/samples/INFO2-sample1.txt rifiuti2-0.7.0/test/samples/INFO2-sample1.txt --- rifiuti2-0.6.1/test/samples/INFO2-sample1.txt 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO2-sample1.txt 2019-05-09 01:26:08.000000000 +0000 @@ -1,5 +1,7 @@ Recycle bin path: 'INFO2-sample1' Version: 5 +OS Guess: Windows XP or 2003 +Time zone: Coordinated Universal Time (UTC) [+0000] Index Deleted Time Gone? Size Path 44 2008-10-28 15:53:42 No 4096 C:\Documents and Settings\All Users\Desktop\有道桌面词典.lnk diff -Nru rifiuti2-0.6.1/test/samples/INFO2-sample2.txt rifiuti2-0.7.0/test/samples/INFO2-sample2.txt --- rifiuti2-0.6.1/test/samples/INFO2-sample2.txt 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO2-sample2.txt 2019-05-09 01:26:08.000000000 +0000 @@ -1,5 +1,7 @@ Recycle bin path: 'INFO2-sample2' Version: 4 +OS Guess: Windows 98 +Time zone: Coordinated Universal Time (UTC) [+0000] Index Deleted Time Gone? Size Path 0 2015-04-20 00:07:36 No 32768 C:\WINDOWS\All Users\Desktop\Connect to the Internet.LNK diff -Nru rifiuti2-0.6.1/test/samples/INFO2-sample2-wrong-enc.txt rifiuti2-0.7.0/test/samples/INFO2-sample2-wrong-enc.txt --- rifiuti2-0.6.1/test/samples/INFO2-sample2-wrong-enc.txt 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO2-sample2-wrong-enc.txt 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,13 @@ +Recycle bin path: 'INFO2-sample2' +Version: 4 +OS Guess: Windows 98 +Time zone: Coordinated Universal Time (UTC) [+0000] + +Index Deleted Time Gone? Size Path +0 2015-04-20 00:07:36 No 32768 C:\WINDOWS\All Users\Desktop\Connect to the Internet.LNK +1 2015-04-20 00:07:42 No 32768 C:\WINDOWS\Desktop\Online Services +2 2015-04-20 00:09:43 Yes 524288 C:\WINDOWS\Desktop\IE9-WindowsVista-x64-enu.exe +3 2015-04-20 01:04:33 No 32768 C:\My Documents\R駸um<\E9>.txt.txt +4 2015-04-20 01:05:01 No 6258688 C:\WINDOWS\Desktop\winzip100.exe +5 2015-04-20 01:05:41 Yes 32768 C:\WINDOWS\Desktop\111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111 +6 2015-04-20 01:06:12 No 32768 C:\WINDOWS\Desktop\1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345 Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/INFO-95-ja-1 and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/INFO-95-ja-1 differ diff -Nru rifiuti2-0.6.1/test/samples/INFO-95-ja-1.txt rifiuti2-0.7.0/test/samples/INFO-95-ja-1.txt --- rifiuti2-0.6.1/test/samples/INFO-95-ja-1.txt 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO-95-ja-1.txt 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,18 @@ +Recycle bin path: 'INFO-95-ja-1' +Version: 0 +Total entries ever existed: 16 +OS Guess: Windows 95 +Time zone: Coordinated Universal Time (UTC) [+0000] + +Index Deleted Time Size Path +1 2015-05-11 05:59:49 32768 D:\WINDOWS\デスクトップ\The Microsoft Network のセットアップ.lnk +2 2015-05-11 06:00:25 950272 D:\WINDOWS\デスクトップ\新規ビットマップ イメージ.bmp +3 2015-05-11 07:19:25 32768 D:\WINDOWS\デスクトップ\新規テキスト文書.txt +4 2015-05-11 09:48:21 589824 D:\My Documents\DirectX-V8.0a\bda.cab +5 2015-05-11 09:48:21 589824 D:\My Documents\DirectX-V8.0a\bdant.cab +6 2015-05-11 09:48:21 65536 D:\My Documents\DirectX-V8.0a\cfgmgr32.dll +11 2015-05-11 09:48:23 163840 D:\My Documents\DirectX-V8.0a\dxsetup.exe +12 2015-05-11 09:48:23 360448 D:\My Documents\DirectX-V8.0a\setupapi.dll +13 2015-05-11 09:59:19 32768 D:\WINDOWS\デスクトップ\Connect to the Internet.LNK +14 2015-05-11 09:59:22 32768 D:\WINDOWS\デスクトップ\Outlook Express.lnk +15 2015-05-18 00:45:09 32768 D:\WINDOWS\デスクトップ\新規テキスト文書.txt Binary files /tmp/tmpSw8L6h/x8ohLoylyV/rifiuti2-0.6.1/test/samples/INFO-NT-en-1 and /tmp/tmpSw8L6h/7R4YuS0qzX/rifiuti2-0.7.0/test/samples/INFO-NT-en-1 differ diff -Nru rifiuti2-0.6.1/test/samples/INFO-NT-en-1.txt rifiuti2-0.7.0/test/samples/INFO-NT-en-1.txt --- rifiuti2-0.6.1/test/samples/INFO-NT-en-1.txt 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/INFO-NT-en-1.txt 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,13 @@ +Recycle bin path: 'INFO-NT-en-1' +Version: 2 +Total entries ever existed: 18 +OS Guess: Windows NT 4.0 +Time zone: Coordinated Universal Time (UTC) [+0000] + +Index Deleted Time Size Path +12 2015-05-23 01:50:28 89355264 C:\WINNT\Profiles\Administrator\Desktop\IE 5.5 SP2 Full +13 2015-05-23 01:50:31 6048256 C:\WINNT\Profiles\Administrator\Desktop\Firefox Setup 2[1].0.0.20.exe +14 2015-05-23 01:50:31 2615296 C:\WINNT\Profiles\Administrator\Desktop\coreftplite[1].ansi.exe +15 2015-05-23 01:50:31 3682816 C:\WINNT\Profiles\Administrator\Desktop\ie55sp2_nt.zip +16 2015-05-23 01:50:49 20809216 C:\TEMP\ie6 +17 2015-05-23 01:50:49 8637952 C:\TEMP\ie6-standalone diff -Nru rifiuti2-0.6.1/test/samples/japanese-path-dir.txt rifiuti2-0.7.0/test/samples/japanese-path-dir.txt --- rifiuti2-0.6.1/test/samples/japanese-path-dir.txt 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/japanese-path-dir.txt 2019-05-09 01:26:08.000000000 +0000 @@ -1,4 +1,6 @@ Recycle bin path: './ごみ箱/dir-empty' Version: ??? (empty folder) +OS detection failed +Time zone: Coordinated Universal Time (UTC) [+0000] Index Deleted Time Size Path diff -Nru rifiuti2-0.6.1/test/samples/japanese-path-file.txt rifiuti2-0.7.0/test/samples/japanese-path-file.txt --- rifiuti2-0.6.1/test/samples/japanese-path-file.txt 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/samples/japanese-path-file.txt 2019-05-09 01:26:08.000000000 +0000 @@ -1,4 +1,6 @@ Recycle bin path: './ごみ箱/INFO2-empty' Version: 5 +OS Guess: Windows 2000, XP or 2003 +Time zone: Coordinated Universal Time (UTC) [+0000] Index Deleted Time Gone? Size Path diff -Nru rifiuti2-0.6.1/test/test_cli_option.at rifiuti2-0.7.0/test/test_cli_option.at --- rifiuti2-0.6.1/test/test_cli_option.at 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/test_cli_option.at 2019-05-09 01:26:08.000000000 +0000 @@ -1,60 +1,88 @@ dnl -*- mode: m4; -*- +dnl vim: set filetype=m4 ts=4 sw=4 noexpandtab : +dnl dnl Test suite for rifiuti2 AT_SETUP([Short Options]) AT_KEYWORDS([option]) -_r2_check_empty_option([-?]) -_r2_check_empty_option([-v]) -_r2_check_option([-t :]) -_r2_check_option([-n]) -_r2_check_option([-8]) +_r2_chk_status_only([-?]) +_r2_chk_status_only([-v]) +_r2_chk_opt_status_only([-t :]) +_r2_chk_opt_status_only([-n]) AT_CLEANUP AT_SETUP([Long Options]) AT_KEYWORDS([option]) -_r2_check_empty_option([--help-all]) -_r2_check_option([--delimiter=:]) -_r2_check_option([--no-heading]) -_r2_check_option([--always-utf8]) +_r2_chk_status_only([--help-all]) +_r2_chk_opt_status_only([--delimiter=:]) +_r2_chk_opt_status_only([--no-heading]) AT_CLEANUP AT_SETUP([Invalid options]) AT_KEYWORDS([option]) -_r2_check_empty_option([--invalid-option],[1]) -AT_CHECK([$progf -l foobar $sample/INFO2-sample2], [4], [], [ignore]) +_r2_chk_opt_status_only([--invalid-option], [1]) +AX_AT_CHECK_PATTERN([dnl + $progf -l foobar $sample/INFO2-sample2], [1],, [dnl +Error parsing options: 'foobar' encoding is not supported by glib library .* + + https://.* +]) +AT_CLEANUP + +AT_SETUP([Duplicative options]) +AT_KEYWORDS([option]) +AT_CHECK([dnl + $progf -l ASCII -l CP1252 $sample/INFO2-sample2], [1],, [dnl +Error parsing options: Multiple encoding options disallowed. +]) +_r2_chk_opt_err([-t : -t ,], [1], [dnl +Error parsing options: Multiple delimiter options disallowed. +]) +_r2_chk_opt_err([-o file1 -o file2], [1], [dnl +Error parsing options: Multiple output destinations disallowed. +]) AT_CLEANUP +AT_SETUP([Options with empty arg]) +AT_KEYWORDS([option]) +_r2_chk_opt_err([-o ""], [1], [dnl +Error parsing options: Empty output filename disallowed. +]) +AT_CHECK([dnl + $progf -l "" $sample/INFO2-sample2], [1],, [dnl +Error parsing options: Empty encoding option disallowed. +]) +AT_CLEANUP + +dnl TODO check stderr too AT_SETUP([Wrong combinations]) AT_KEYWORDS([option]) -_r2_check_option([-x -t :], [1]) -_r2_check_option([-x -n], [1]) -_r2_check_option([-x -8], [1]) +_r2_chk_opt_status_only([-x -t :], [1]) +_r2_chk_opt_status_only([-x -n], [1]) AT_CLEANUP +dnl TODO check stderr too AT_SETUP([Err on no file args]) AT_KEYWORDS([file option]) -_r2_check_ignore_err([-8], [1]) +_r2_chk_status_only([-x], [1]) AT_CLEANUP +dnl TODO check stderr too AT_SETUP([Err on multiple file args]) AT_KEYWORDS([file option]) -_r2_check_ignore_err([foo bar baz], [1]) +_r2_chk_status_only([foo bar baz], [1]) AT_CLEANUP AT_SETUP([Escaped char in delimiter]) AT_KEYWORDS([option]) -dnl FreeBSD sed does not support escaped chars -AT_CHECK([$has_perl || exit 77]) AT_CHECK([ - perl -p -e 's/\t/\n\t/g' < $sample/INFO2-sample1.txt > expout + $awk '{ gsub("\t","\n\t"); print; }' < $sample/INFO2-sample1.txt > expout cd $sample - $set_ascii $progf -8 -t "\n\t" INFO2-sample1 + $progf -t "\n\t" INFO2-sample1 ],, [expout], []) AT_CHECK([ - perl -p -e 's/\t/\n\t/g' < $sample/dir-sample1.txt > expout + $awk '{ gsub("\t","\n\t"); print; }' < $sample/dir-sample1.txt > expout cd $sample - $set_ascii $progd -8 -t "\n\t" dir-sample1 + $progd -t "\n\t" dir-sample1 ],, [expout], []) AT_CLEANUP - -dnl vim: set ts=4 sw=4 noexpandtab : diff -Nru rifiuti2-0.6.1/test/test_common.at rifiuti2-0.7.0/test/test_common.at --- rifiuti2-0.6.1/test/test_common.at 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/test_common.at 2019-05-09 01:26:08.000000000 +0000 @@ -1,26 +1,28 @@ dnl -*- mode: m4; -*- +dnl vim: set filetype=m4 ts=4 sw=4 noexpandtab : +dnl dnl Test suite for rifiuti2 -m4_defun([_r2_check_empty_option], [ +m4_defun([_r2_chk_status_only], [dnl AT_CHECK([$progf $1], [$2], [ignore], [ignore]) AT_CHECK([$progd $1], [$2], [ignore], [ignore]) ]) -m4_defun([_r2_check_option], [ +m4_defun([_r2_chk_opt_status_only], [dnl AT_CHECK([$progf $1 $sample/INFO2-empty], [$2], [ignore], [ignore]) - AT_CHECK([$progd $1 $sample/dir-empty], [$2], [ignore], [ignore]) + AT_CHECK([$progd $1 $sample/dir-empty] , [$2], [ignore], [ignore]) ]) -m4_defun([_r2_check_ignore_err], [ - AT_CHECK([$progf $1], [$2], [$3], [ignore]) - AT_CHECK([$progd $1], [$2], [$3], [ignore]) +m4_defun([_r2_chk_opt_err], [dnl + AT_CHECK([$progf $1 $sample/INFO2-empty], [$2], [], [$3]) + AT_CHECK([$progd $1 $sample/dir-empty] , [$2], [], [$3]) ]) m4_defun([_r2_basic_compare], [ AT_CHECK([ rm -f expout - cp $sample/$2 expout + $as_ln_s $sample/$2 expout cd $sample - $set_ascii $1 - ],, [expout]) + $1 + ], 0, [expout]) ]) diff -Nru rifiuti2-0.6.1/test/test_encoding.at rifiuti2-0.7.0/test/test_encoding.at --- rifiuti2-0.6.1/test/test_encoding.at 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/test_encoding.at 2019-05-09 01:26:08.000000000 +0000 @@ -1,76 +1,81 @@ dnl -*- coding: utf-8; mode: m4; -*- +dnl vim: set fileencoding=utf-8 filetype=m4 ts=4 sw=4 noexpandtab : +dnl dnl Test suite for rifiuti2 dnl dnl ごみ箱 is 'recycle bin' in Japanese dnl -AT_SETUP([UTF-8 - No Fallback For Path]) +AT_SETUP([Unicode characters in file argument]) AT_KEYWORDS([encoding]) AT_CHECK([ - cp $sample/japanese-path-file.txt expout + $as_ln_s $sample/japanese-path-file.txt expout cd $sample - $set_utf8 $progf ./ごみ箱/INFO2-empty + $progf ./ごみ箱/INFO2-empty ],, [expout]) AT_CHECK([ rm -f expout - cp $sample/japanese-path-dir.txt expout + $as_ln_s $sample/japanese-path-dir.txt expout cd $sample - $set_utf8 $progd ./ごみ箱/dir-empty + $progd ./ごみ箱/dir-empty ],, [expout]) - AT_CLEANUP -dnl -dnl For the case when entire non-ASCII path can't be displayed -dnl -AT_SETUP([Fallback For Path 1]) -AT_KEYWORDS([encoding]) - -AT_CHECK([${has_perl} || exit 77]) +AT_SETUP([Legacy path encoding - correct]) +AT_SKIP_IF([test -z "${gbk_name}"]) +AT_KEYWORDS([info2 encoding]) +_r2_basic_compare([$progf -l "${gbk_name}" INFO2-sample1], [INFO2-sample1-alt.txt]) +AT_CLEANUP -dnl 1. Curse those quadrigraphs -dnl 2. I hate writing regex with m4! Takes so much time to make it work, -dnl and fragile as hell -dnl 3. Path need to have ./ as prefix, without that and the middle slash -dnl will be transformed into root of MinGW folder. This only happens -dnl when folder with non-ASCII name is prepended before slash. Looks like -dnl bug in g_win32_get_command_line() -AT_CHECK([ - PERLIO=":utf8" perl -C -p -e 's/@{:@@<:@\p{Han}\p{Hiragana}@:>@@:}@/sprintf "\\u%x", ord@{:@@S|@1@:}@/ge' < $sample/japanese-path-file.txt > expout - cd $sample/ - $set_ascii $progf ./ごみ箱/INFO2-empty -],, [expout], []) +AT_SETUP([Legacy path encoding - illegal (1)]) +AT_KEYWORDS([info2 encoding]) +AX_AT_CHECK_PATTERN([ + cd $sample + $progf -l xxx INFO2-sample1 +], 1,, [dnl +.* encoding is not supported by glib library on this system\..* -AT_CHECK([ - PERLIO=":utf8" perl -C -p -e 's/@{:@@<:@\p{Han}\p{Hiragana}@:>@@:}@/sprintf "\\u%x", ord@{:@@S|@1@:}@/ge' < $sample/japanese-path-dir.txt > expout - cd $sample/ - $set_ascii $progd ./ごみ箱/dir-empty -],, [expout], []) + https://.* +]) AT_CLEANUP +dnl +dnl Demands ASCII-compatible encoding, EBCDIC not one of those +dnl +AT_SETUP([Legacy path encoding - illegal (2)]) +AT_KEYWORDS([info2 encoding]) +AT_SKIP_IF([test -z "${ebcdic_latin1_name}"]) +AX_AT_CHECK_PATTERN([ + cd $sample + $progf -l "${ebcdic_latin1_name}" INFO2-sample1 +], 1,, [dnl +.* can't possibly be a code page or compatible encoding .* +]) +AT_CLEANUP dnl -dnl For the case when part of non-ASCII path can't be displayed -dnl in CP950, only the character 箱 can be displayed as is +dnl Original file in Windows ANSI (CP1252), but users attempts +dnl to treat it as Shift-JIS, and got hex escapes as result dnl -AT_SETUP([Fallback For Path 2]) +AT_SETUP([Legacy path encoding - wrong]) AT_KEYWORDS([encoding]) - -AT_CHECK([${has_perl} || exit 77]) -AT_CHECK([${has_iconv} || exit 77]) - +AT_SKIP_IF([test -z "${sjis_name}"]) AT_CHECK([ - PERLIO=":utf8" perl -C -p -e 's/@{:@@<:@\p{Hiragana}@:>@@:}@/sprintf "\\u%x", ord@{:@@S|@1@:}@/ge' < $sample/japanese-path-file.txt | iconv -f utf-8 -t cp950 > expout - cd $sample/ - env CHARSET=CP950 $progf ./ごみ箱/INFO2-empty -],, [expout], []) + $as_ln_s $sample/INFO2-sample2-wrong-enc.txt expout + cd $sample + $progf -l "${sjis_name}" INFO2-sample2 +], 5, [expout], [ignore]) +AT_CLEANUP +dnl +dnl Test for: single surrogate, swapped surrogate pair, unassigned codepoint +dnl +AT_SETUP([Bad unicode path]) +AT_KEYWORDS([encoding]) AT_CHECK([ - PERLIO=":utf8" perl -C -p -e 's/@{:@@<:@\p{Hiragana}@:>@@:}@/sprintf "\\u%x", ord@{:@@S|@1@:}@/ge' < $sample/japanese-path-dir.txt | iconv -f utf-8 -t cp950 > expout - cd $sample/ - env CHARSET=CP950 $progd ./ごみ箱/dir-empty -],, [expout], []) + $as_ln_s $sample/dir-bad-uni.txt expout + cd $sample + $progd dir-bad-uni +], 5, [expout], [ignore]) AT_CLEANUP - -dnl vim: set fileencoding=utf-8 ts=4 sw=4 noexpandtab : diff -Nru rifiuti2-0.6.1/test/test_faulty_dir.at rifiuti2-0.7.0/test/test_faulty_dir.at --- rifiuti2-0.6.1/test/test_faulty_dir.at 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/test_faulty_dir.at 2019-05-09 01:26:08.000000000 +0000 @@ -1,9 +1,51 @@ dnl -*- mode: m4; -*- +dnl vim: set filetype=m4 ts=4 sw=4 noexpandtab : +dnl dnl Test suite for rifiuti2 AT_SETUP([Index files of different versions]) AT_KEYWORDS([recycledir crafted]) -_r2_basic_compare([$progd dir-mixed], [dir-mixed.txt]) +AX_AT_CHECK_PATTERN([ + cd $sample + $progd dir-mixed +], [3],, [dnl +Index files come from multiple versions of Windows\..* +]) AT_CLEANUP -dnl vim: set ts=4 sw=4 noexpandtab : +AT_SETUP([Directory w/o Windows access permission]) +AT_KEYWORDS([recycledir crafted windows]) +AT_CHECK([which icacls.exe > /dev/null || exit 77])dnl need 2003 SP2 or later +AT_SKIP_IF([! $is_mingw]) +dnl +dnl MSYS2_ARG_CONV_EXCL would prevent command line options automagically +dnl treated as unix paths (and translated into windows paths or drive letters) +dnl +AT_CHECK([ + cp -pR $sample/dir-win10-01 dir1 + icacls.exe dir1 /inheritance:r > /dev/null + icacls.exe dir1 /grant:r "Users:(OI)(CI)(S,REA)" > /dev/null + $progd dir1 +], + [2],, [Error listing directory: Insufficient permission. +], + [MSYS2_ARG_CONV_EXCL="/reset" icacls.exe dir1 /reset > /dev/null], + [MSYS2_ARG_CONV_EXCL="/reset" icacls.exe dir1 /reset > /dev/null]) +AT_CLEANUP + +dnl MinGW mounts root with noacl option by default. Though user can change +dnl mount options, it is safer to skip this test altogether +AT_SETUP([Directory w/o Unix access permission]) +AT_KEYWORDS([recycledir crafted unix]) +AT_SKIP_IF([$is_mingw]) +dnl in front of root, permission is futile +AT_SKIP_IF([test "`id -u`" = "0"]) +AX_AT_CHECK_PATTERN([ + cp -pR $sample/dir-win10-01 dir1 + chmod u= dir1 + $progd dir1 +], + [2],, [Error opening directory 'dir1': Error opening directory .+dir1.+: Permission denied +], + [chmod u=rwx dir1]) +AT_CLEANUP diff -Nru rifiuti2-0.6.1/test/test_glib_iconv.c rifiuti2-0.7.0/test/test_glib_iconv.c --- rifiuti2-0.6.1/test/test_glib_iconv.c 1970-01-01 00:00:00.000000000 +0000 +++ rifiuti2-0.7.0/test/test_glib_iconv.c 2019-05-09 01:26:08.000000000 +0000 @@ -0,0 +1,23 @@ +#include + +int main (int argc, char **argv) +{ + GIConv cd; + + if (argc < 2 || *(argv[1]) == '\0') { + return 2; + } + + /* + * In rifiuti2 we only need → UTF-8 conversion, + * so we only test for this case. There are some odd OSes + * where even identity conversion can be unsupported (say + * CP936 → CP936), such as on Solaris. + */ + cd = g_iconv_open ("UTF-8", argv[1]); + + if (cd != (GIConv) -1) + g_iconv_close (cd); + + return (cd == (GIConv) -1); +} diff -Nru rifiuti2-0.6.1/test/test_parse_dir.at rifiuti2-0.7.0/test/test_parse_dir.at --- rifiuti2-0.6.1/test/test_parse_dir.at 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/test_parse_dir.at 2019-05-09 01:26:08.000000000 +0000 @@ -1,4 +1,6 @@ dnl -*- mode: m4; -*- +dnl vim: set filetype=m4 ts=4 sw=4 noexpandtab : +dnl dnl Test suite for rifiuti2 AT_SETUP([Empty $Recycle.bin]) @@ -6,22 +8,25 @@ _r2_basic_compare([$progd dir-empty], [dir-empty.txt]) AT_CLEANUP -AT_SETUP([$Recycle.bin Sample 1]) +AT_SETUP([$Recycle.bin Sample 1 - Vista]) AT_KEYWORDS([recycledir]) -_r2_basic_compare([$progd -8 dir-sample1], [dir-sample1.txt]) +_r2_basic_compare([$progd dir-sample1], [dir-sample1.txt]) AT_CLEANUP -AT_SETUP([$Recycle.bin Sample 2]) +AT_SETUP([$Recycle.bin Sample 2 - Win 10]) AT_KEYWORDS([recycledir]) -_r2_basic_compare([$progd -8 dir-win10-01], [dir-win10-01.txt]) +_r2_basic_compare([$progd dir-win10-01], [dir-win10-01.txt]) AT_CLEANUP -AT_SETUP([$Recycle.bin single file]) +AT_SETUP([$Recycle.bin single index file]) AT_KEYWORDS([recycledir]) AT_CHECK([ grep 'IHO61YT' $sample/dir-win10-01.txt > expout - $set_ascii $progd -n -8 $sample/dir-win10-01/\$IHO61YT + $progd -n $sample/dir-win10-01/\$IHO61YT ],, [expout], []) AT_CLEANUP - -dnl vim: set ts=4 sw=4 noexpandtab : + +AT_SETUP([$Recycle.bin UNC path]) +AT_KEYWORDS([recycledir unc]) +_r2_basic_compare([$progd dir-2019-uncpath], [dir-2019-uncpath.txt]) +AT_CLEANUP diff -Nru rifiuti2-0.6.1/test/test_parse_info2.at rifiuti2-0.7.0/test/test_parse_info2.at --- rifiuti2-0.6.1/test/test_parse_info2.at 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/test_parse_info2.at 2019-05-09 01:26:08.000000000 +0000 @@ -1,4 +1,6 @@ dnl -*- mode: m4; -*- +dnl vim: set filetype=m4 ts=4 sw=4 noexpandtab : +dnl dnl Test suite for rifiuti2 AT_SETUP([Empty INFO2]) @@ -6,19 +8,48 @@ _r2_basic_compare([$progf INFO2-empty], [INFO2-empty.txt]) AT_CLEANUP -AT_SETUP([INFO2 Sample 1]) +AT_SETUP([INFO2 Sample 1 - 95]) AT_KEYWORDS([info2]) -_r2_basic_compare([$progf -8 INFO2-sample1], [INFO2-sample1.txt]) +AT_SKIP_IF([test -z "${sjis_name}"]) +_r2_basic_compare([$progf -l "${sjis_name}" INFO-95-ja-1], [INFO-95-ja-1.txt]) AT_CLEANUP -AT_SETUP([INFO2 Sample 2]) +AT_SETUP([INFO2 Sample 2 - NT4]) AT_KEYWORDS([info2]) -_r2_basic_compare([$progf -8 -l latin1 INFO2-sample2], [INFO2-sample2.txt]) +_r2_basic_compare([$progf INFO-NT-en-1], [INFO-NT-en-1.txt]) AT_CLEANUP -AT_SETUP([Legacy path names]) +AT_SETUP([INFO2 Sample 3 - 98]) AT_KEYWORDS([info2]) -_r2_basic_compare([$progf -8 -l CP936 INFO2-sample1], [INFO2-sample1-alt.txt]) +_r2_basic_compare([$progf -l CP1252 INFO2-sample2], [INFO2-sample2.txt]) AT_CLEANUP -dnl vim: set ts=4 sw=4 noexpandtab : +AT_SETUP([INFO2 Sample 4 - ME]) +AT_KEYWORDS([info2]) +_r2_basic_compare([$progf -l CP1252 INFO2-ME-en-1], [INFO2-ME-en-1.txt]) +AT_CLEANUP + +AT_SETUP([INFO2 Sample 5 - 2000]) +AT_KEYWORDS([info2]) +_r2_basic_compare([$progf INFO2-2k-cht-1], [INFO2-2k-cht-1.txt]) +AT_CLEANUP + +AT_SETUP([INFO2 Sample 6 - XP/03]) +AT_KEYWORDS([info2]) +_r2_basic_compare([$progf INFO2-sample1], [INFO2-sample1.txt]) +AT_CLEANUP + +AT_SETUP([INFO2 - UNC legacy path 1]) +AT_KEYWORDS([info2 unc]) +_r2_basic_compare([$progf -l ASCII INFO2-me-en-uncpath], [INFO2-me-en-uncpath.txt]) +AT_CLEANUP + +AT_SETUP([INFO2 - UNC legacy path 2]) +AT_KEYWORDS([info2 unc]) +_r2_basic_compare([$progf -l ${big5_name} INFO2-2k-tw-uncpath], [INFO2-2k-tw-uncpath.txt]) +AT_CLEANUP + +AT_SETUP([INFO2 - UNC unicode path]) +AT_KEYWORDS([info2 unc]) +_r2_basic_compare([$progf INFO2-03-tw-uncpath], [INFO2-03-tw-uncpath.txt]) +AT_CLEANUP \ No newline at end of file diff -Nru rifiuti2-0.6.1/test/test_read_write.at rifiuti2-0.7.0/test/test_read_write.at --- rifiuti2-0.6.1/test/test_read_write.at 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/test_read_write.at 2019-05-09 01:26:08.000000000 +0000 @@ -1,41 +1,37 @@ dnl -*- mode: m4; -*- +dnl vim: set filetype=m4 ts=4 sw=4 noexpandtab : +dnl dnl Test suite for rifiuti2 AT_SETUP([Err on non-existent input]) AT_KEYWORDS([file]) -_r2_check_ignore_err([foobar], [2]) +_r2_chk_status_only([foobar], [2]) AT_CLEANUP -AT_SETUP([Err on non-regular file (Unix)]) -AT_KEYWORDS([file]) -AT_CHECK([if $is_mingw; then exit 77; fi]) -_r2_check_ignore_err([/dev/zero], [2]) +AT_SETUP([Err on non-regular file]) +AT_KEYWORDS([file unix]) +_r2_chk_status_only([/dev/zero], [2]) AT_CLEANUP -dnl g_file_test() was not so usable on Windows: NUL, CON etc are considered as regular files. -dnl Therefore file can't be determined early as unusable, they have to be actually parsed. +dnl g_file_test() was not so usable on Windows: NUL, CON etc are considered +dnl as regular files. Therefore they can't be determined ahead as unusable, +dnl they have to be actually parsed. Only MinGW can access the special files; +dnl cygwin hides them from user. AT_SETUP([Err on non-regular file (Windows)]) -AT_KEYWORDS([file]) -AT_CHECK([if $is_mingw; then :; else exit 77; fi]) -_r2_check_ignore_err([nul], [3]) +AT_KEYWORDS([file windows]) +AT_SKIP_IF([! $is_mingw]) +_r2_chk_status_only([nul], [3]) AT_CLEANUP AT_SETUP([UTF-8 console/file output are equal]) AT_KEYWORDS([file]) AT_CHECK([ - $progf -8 -o expout $sample/INFO2-sample1 - $progf -8 $sample/INFO2-sample1 + $progf -o expout $sample/INFO2-sample1 + $progf $sample/INFO2-sample1 ],, [expout]) AT_CHECK([ - $progd -8 -o expout $sample/dir-sample1 - $progd -8 $sample/dir-sample1 + rm -f expout + $progd -o expout $sample/dir-sample1 + $progd $sample/dir-sample1 ],, [expout]) AT_CLEANUP - -AT_SETUP([Read non-ASCII path]) -AT_KEYWORDS([file encoding]) -AT_CHECK([$progf $sample/ごみ箱/INFO2-empty],, [ignore], [ignore]) -AT_CHECK([$progd $sample/ごみ箱/dir-empty],, [ignore], [ignore]) -AT_CLEANUP - -dnl vim: set ts=4 sw=4 noexpandtab : diff -Nru rifiuti2-0.6.1/test/testsuite.at rifiuti2-0.7.0/test/testsuite.at --- rifiuti2-0.6.1/test/testsuite.at 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/testsuite.at 2019-05-09 01:26:08.000000000 +0000 @@ -1,9 +1,24 @@ dnl -*- mode: m4; -*- +dnl vim: set filetype=m4 ts=4 sw=4 noexpandtab : +dnl dnl Test suite for rifiuti2 AT_INIT -AT_COPYRIGHT([Copyright (c) 2015, Abel Cheung]) +AT_COPYRIGHT([ +Copyright (c) 2015-2019, Abel Cheung +All rights reserved. + +Makes use of macros from pyrediff project, which carries following notice: + + Copyright (c) 2013-2017 Luke Mewburn luke@mewburn.net + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. This file is offered as-is, + without any warranty. +]) + +m4_include([ax_at_check_pattern.m4]) m4_include([test_common.at]) @@ -27,5 +42,3 @@ AT_BANNER([XML Validation]) m4_include([test_xml.at]) - -dnl vim: set ts=4 sw=4 noexpandtab : diff -Nru rifiuti2-0.6.1/test/test_xml.at rifiuti2-0.7.0/test/test_xml.at --- rifiuti2-0.6.1/test/test_xml.at 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/test/test_xml.at 2019-05-09 01:26:08.000000000 +0000 @@ -1,9 +1,11 @@ dnl -*- mode: m4; -*- +dnl vim: set filetype=m4 ts=4 sw=4 noexpandtab : +dnl dnl Test suite for rifiuti2 AT_SETUP([INFO2: XML validation]) AT_KEYWORDS([info2 xml]) -AT_CHECK([${has_xmllint} || exit 77]) +AT_SKIP_IF([! $has_xmllint]) AT_CHECK([ $progf -x $sample/INFO2-sample1 | \ $xmllint --noout - @@ -12,7 +14,7 @@ AT_SETUP([INFO2: DTD validation]) AT_KEYWORDS([info2 xml]) -AT_CHECK([${has_xmllint} || exit 77]) +AT_SKIP_IF([! $has_xmllint]) AT_CHECK([ $progf -x $sample/INFO2-sample1 | \ $xmllint --noout --dtdvalid $srcdir/rifiuti.dtd - @@ -21,7 +23,7 @@ AT_SETUP([$Recycle.bin: XML validation]) AT_KEYWORDS([recycledir xml]) -AT_CHECK([${has_xmllint} || exit 77]) +AT_SKIP_IF([! $has_xmllint]) AT_CHECK([ $progd -x $sample/dir-sample1 | \ $xmllint --noout - @@ -30,11 +32,9 @@ AT_SETUP([$Recycle.bin: DTD validation]) AT_KEYWORDS([recycledir xml]) -AT_CHECK([${has_xmllint} || exit 77]) +AT_SKIP_IF([! $has_xmllint]) AT_CHECK([ $progd -x $sample/dir-sample1 | \ $xmllint --noout --dtdvalid $srcdir/rifiuti.dtd - ]) AT_CLEANUP - -dnl vim: set ts=4 sw=4 noexpandtab : diff -Nru rifiuti2-0.6.1/.travis.yml rifiuti2-0.7.0/.travis.yml --- rifiuti2-0.6.1/.travis.yml 2015-05-21 05:42:00.000000000 +0000 +++ rifiuti2-0.7.0/.travis.yml 2019-05-09 01:26:08.000000000 +0000 @@ -1,12 +1,29 @@ language: c -compiler: gcc +compiler: clang branches: - except: - - gh-pages + except: + - gh-pages os: - - linux -install: - - sudo apt-get install -qq libglib2.0-dev + - linux +dist: xenial +addons: + apt: + update: true + packages: + - libglib2.0-dev + - libxml2-utils + - autopoint +before_script: + - autoreconf -f -i -v script: - - ./autogen.sh && ./configure && make && sudo make install - - make check TESTSUITEFLAGS='-v' + - ./configure && make distcheck +deploy: + provider: releases + api_key: + secure: "BHyNz+01netkQ0knn17Fgj27YUnwtuu/ensNNfMgUaPiobsLHOhXg6W6PCugjCGcBldA5NeLtTf2b1ee64iyGO8Fhh6m4PE0GwkAIjWhbYHHwlvA7G9PWXVhFadXbLezH2iAVzYU8S1pC92qlBwBeBQSnKCSK9wJduaPA35Lz+k=" + file: "rifiuti2-*.tar.xz" + file_glob: "true" + skip_cleanup: true + draft: true + on: + tags: true