[msitools] tools: add msidiff and msidump



commit c1a2a42dab3edf1a01de8d2aedfebfbbc4d03cd9
Author: Marc-Andrà Lureau <marcandre lureau gmail com>
Date:   Wed Feb 6 03:54:12 2013 +0100

    tools: add msidiff and msidump

 Makefile.am      |    5 +-
 TODO             |    1 -
 configure.ac     |    5 +
 tools/msidiff.in |  219 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/msidump.in |  131 ++++++++++++++++++++++++++++++++
 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



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]