summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@gmail.com>2013-02-06 03:54:12 +0100
committerMarc-André Lureau <marcandre.lureau@gmail.com>2013-02-06 03:54:42 +0100
commitc1a2a42dab3edf1a01de8d2aedfebfbbc4d03cd9 (patch)
tree2e90913f33431e75c3853e215534ab313925418b
parent95234b55312b0f5dd6236b02e2472b4fc5a1a51e (diff)
downloadmsitools-c1a2a42dab3edf1a01de8d2aedfebfbbc4d03cd9.tar.gz
msitools-c1a2a42dab3edf1a01de8d2aedfebfbbc4d03cd9.tar.xz
msitools-c1a2a42dab3edf1a01de8d2aedfebfbbc4d03cd9.zip
tools: add msidiff and msidump
-rw-r--r--Makefile.am5
-rw-r--r--TODO1
-rw-r--r--configure.ac5
-rwxr-xr-xtools/msidiff.in219
-rwxr-xr-xtools/msidump.in131
5 files changed, 359 insertions, 2 deletions
diff --git a/Makefile.am b/Makefile.am
index 33279fb..c2137da 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,5 @@
NULL =
+CLEANFILES =
EXTRA_DIST =
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = include data libmsi tests po .
@@ -21,6 +22,8 @@ AM_CPPFLAGS = -Iinclude -I$(srcdir)/include \
AM_LDFLAGS = -Llibmsi
# Low-level tools
+bin_SCRIPTS = tools/msidump tools/msidiff
+CLEANFILES += $(bin_SCRIPTS)
bin_PROGRAMS = msibuild msiinfo
@@ -151,7 +154,7 @@ EXTRA_DIST += $(completion_DATA)
dist_noinst_DATA = tests/testsuite.at tests/wixl.at tests/package.m4 tests/testsuite
DISTCLEANFILES = atconfig atlocal
-CLEANFILES = testsuite.log
+CLEANFILES += testsuite.log
check-local: $(srcdir)/tests/testsuite atconfig atlocal
$(SHELL) $(srcdir)/tests/testsuite AUTOTEST_PATH=. $(TESTSUITEFLAGS)
diff --git a/TODO b/TODO
index 9d37366..521d0c1 100644
--- a/TODO
+++ b/TODO
@@ -1,4 +1,3 @@
-- write a msidiff tool
- document an easy way to hack on wixl, using WiX and msi diff
- verify gsf conversion, some tests fail on Windows but not POSIX?
- make a SummaryInformation API that does not suck (including converting
diff --git a/configure.ac b/configure.ac
index 9b42f1d..51a9214 100644
--- a/configure.ac
+++ b/configure.ac
@@ -59,7 +59,12 @@ AC_CONFIG_FILES([
po/Makefile.in
libmsi/Makefile
libmsi/libmsi-1.0.pc
+ tools/msidump
+ tools/msidiff
tests/Makefile
+],[
+ chmod +x tools/msidump
+ chmod +x tools/msidiff
])
AC_OUTPUT
diff --git a/tools/msidiff.in b/tools/msidiff.in
new file mode 100755
index 0000000..b3ef35e
--- /dev/null
+++ b/tools/msidiff.in
@@ -0,0 +1,219 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+
+# msidiff - compare two MSI files table content with diff
+# (originally based on rpmdev-diff)
+#
+# Copyright (c) 2004-2010 Ville Skyttä <ville.skytta@iki.fi>
+# Copyright (c) 2013 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+set -e
+
+unset CDPATH
+tmpdir=
+diffopts=
+list=
+long=
+tables=
+diffcopts=-Nup
+diffoopts=-U0
+
+trap cleanup EXIT
+cleanup()
+{
+ set +e
+ [ -z "$tmpdir" -o ! -d "$tmpdir" ] || rm -rf "$tmpdir"
+}
+
+version()
+{
+ cat <<EOF
+@PACKAGE_VERSION@
+EOF
+}
+
+help()
+{
+ cat <<EOF
+msidiff diffs contents of two MSI files.
+EOF
+ usage
+ echo ""
+ echo "Report bugs to <@PACKAGE_BUGREPORT@>."
+}
+
+usage()
+{
+ cat <<EOF
+Usage: msidiff [OPTION]... [DIFF-OPTIONS] FROM-MSI TO-MSI
+
+Options:
+ -t, --tables Diff MSI tables as text. This is the default.
+ -l, --list Diff lists of files.
+ -L, --long-list Diff long lists (akin to 'find -ls') of files.
+ -h, --help Print help message and exit.
+ -v, --version Print version information and exit.
+ diff-options Options passed to diff(1). The first repeated argument of
+ the above or the first argument starting with a '-' but not
+ one of the above starts diff-options, the first one not
+ starting with it ends them. Default: $diffcopts for contents
+ (in addition to -r which will always be passed), -U0 for
+ others.
+
+More than one of -t, -l or -L may be specified.
+EOF
+}
+
+while true ; do
+ case $1 in
+ -t|--tables)
+ if [[ $tables$diffopts ]] ; then
+ diffopts+=" $1"
+ else
+ tables=true
+ fi
+ ;;
+ -l|--list)
+ if [[ $list$diffopts ]] ; then
+ diffopts+=" $1"
+ else
+ list=true
+ fi
+ ;;
+ -L|--long-list)
+ if [[ $long$diffopts ]] ; then
+ diffopts+=" $1"
+ else
+ long=true
+ fi
+ ;;
+ -h|--help)
+ if [[ $diffopts ]] ; then
+ diffopts+=" $1"
+ else
+ help
+ exit 0
+ fi
+ ;;
+ -v|--version)
+ if [[ $diffopts ]] ; then
+ diffopts+=" $1"
+ else
+ version
+ exit 0
+ fi
+ ;;
+ -*)
+ diffopts+=" $1"
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+if [[ $# -lt 2 ]] ; then
+ usage
+ exit 1
+fi
+for file in "$1" "$2" ; do
+ if [[ ! -f $file ]] ; then
+ [[ -e $file ]] && \
+ echo "Error: not a regular file: '$file'" >&2 ||
+ echo "Error: file does not exist: '$file'" >&2
+ exit 1
+ fi
+done
+
+tmpdir=`mktemp -d ${TMPDIR:-/tmp}/msidiff.XXXXXX`
+
+mkdir "$tmpdir/old" "$tmpdir/new"
+msidump --tables --directory "$tmpdir/old" $1 >/dev/null
+msidump --tables --directory "$tmpdir/new" $2 >/dev/null
+if ${list:-false} || ${long:-false} ; then
+ msiextract --directory "$tmpdir/old/files" $1 >/dev/null
+ msiextract --directory "$tmpdir/new/files" $2 >/dev/null
+fi
+
+cd "$tmpdir"
+
+# Did the archives uncompress into base dirs?
+if [[ $(ls -1d old/* | wc -l) -eq 1 ]] ; then
+ old=$(ls -1d old/*)
+else
+ old=old
+fi
+if [[ $(ls -1d new/* | wc -l) -eq 1 ]] ; then
+ new=$(ls -1d new/*)
+else
+ new=new
+fi
+
+# Fixup base dirs to the same level.
+if [[ $(basename "$old") != $(basename "$new") ]] ; then
+ if [[ $old != old ]] ; then
+ mv "$old" .
+ old=`basename "$old"`
+ fi
+ if [[ $new != new ]] ; then
+ mv "$new" .
+ new=`basename "$new"`
+ fi
+fi
+
+# Tables mode is the default.
+if [[ -z $list$tables$long ]] ; then
+ tables=true
+else
+ tables=${tables:-false}
+fi
+list=${list:-false}
+long=${long:-false}
+
+# Here we go.
+
+if $tables ; then
+ set +e
+ diff -r ${diffopts:-$diffcopts} "$old" "$new"
+ [[ $? -eq 0 || $? -eq 1 ]] || exit $?
+ set -e
+fi
+
+
+if $list ; then
+ find "$old/files" | sort | cut -d/ -f 3- -s > "$old.files"
+ find "$new/files" | sort | cut -d/ -f 3- -s > "$new.files"
+ set +e
+ diff ${diffopts:-$diffoopts} "$old.files" "$new.files"
+ [[ $? -eq 0 || $? -eq 1 ]] || exit $?
+ set -e
+fi
+
+if $long ; then
+ find "$old/files" -ls | \
+ perl -pe "s|^(?:[\d\s]*)(\S+)(?:\s+\d+)(.+)$|\1\2| ;
+ s|.*\s\Q$old/files\E$|| ; s|(\s)\Q$old/files/\E|\1|" | \
+ sort > "$old.files"
+ find "$new/files" -ls | \
+ perl -pe "s|^(?:[\d\s]*)(\S+)(?:\s+\d+)(.+)$|\1\2| ;
+ s|.*\s\Q$new/files\E$|| ; s|(\s)\Q$new/files/\E|\1|" | \
+ sort > "$new.files"
+ set +e
+ diff ${diffopts:-$diffoopts} "$old.files" "$new.files"
+ [[ $? -eq 0 || $? -eq 1 ]] || exit $?
+ set -e
+fi
diff --git a/tools/msidump.in b/tools/msidump.in
new file mode 100755
index 0000000..c675f02
--- /dev/null
+++ b/tools/msidump.in
@@ -0,0 +1,131 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+
+# msidump - dump raw MSI tables and stream content
+#
+# Copyright (c) 2013 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+set -e
+
+tables=
+streams=
+destdir=.
+
+version()
+{
+ cat <<EOF
+@PACKAGE_VERSION@
+EOF
+}
+
+help()
+{
+ cat <<EOF
+msidump dumps MSI tables as idt text and streams
+EOF
+ usage
+ echo ""
+ echo "Report bugs to <@PACKAGE_BUGREPORT@>."
+}
+
+usage()
+{
+ cat <<EOF
+Usage: msidump [OPTION]... MSI-FILE
+
+Options:
+ -t, --tables Dump tables. This is the default.
+ -s, --streams Dump streams
+ -d, --directory DIR Dump to given directory DIR
+ -h, --help Print help message and exit.
+ -v, --version Print version information and exit.
+
+More than one of -t or -s may be specified.
+EOF
+}
+
+while true ; do
+ case $1 in
+ -t|--tables)
+ tables=true
+ ;;
+ -s|--streams)
+ streams=true
+ ;;
+ -d|--directory)
+ destdir=$2
+ shift
+ ;;
+ -h|--help)
+ help
+ exit 0
+ ;;
+ -v|--version)
+ version
+ exit 0
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+if [[ $# -lt 1 ]] ; then
+ usage
+ exit 1
+fi
+for file in "$1" ; do
+ if [[ ! -f $file ]] ; then
+ [[ -e $file ]] && \
+ echo "Error: not a regular file: '$file'" >&2 ||
+ echo "Error: file does not exist: '$file'" >&2
+ exit 1
+ fi
+done
+
+if [[ ! -d $destdir ]] ; then
+ echo "Error: directory does not exist: '$destdir'" >&2
+ exit 1
+fi
+
+# Tables mode is the default.
+if [[ -z $tables$streams ]] ; then
+ tables=true
+else
+ tables=${tables:-false}
+fi
+streams=${streams:-false}
+
+
+# Here we go
+
+if $tables ; then
+ TABLES=$(msiinfo tables "$1")
+ for i in $TABLES; do
+ echo "Exporting table $i..."
+ msiinfo export "$1" "$i" > "$destdir/$i.idt"
+ done
+fi
+
+if $streams ; then
+ mkdir -p "$destdir/_Streams"
+ STREAMS=$(msiinfo streams "$1")
+ for i in $STREAMS; do
+ echo "Exporting stream $i..."
+ msiinfo extract "$1" "$i" > "$destdir/_Streams/$i"
+ done
+fi