$ diffoscope --new-file /nonexistent ../build-area/diffoscope_33_all.deb --- /nonexistent +++ ../build-area/diffoscope_33_all.deb ├── metadata │ @@ -0,0 +1,3 @@ │ +rw-r--r-- 0/0 4 Sep 4 08:21 2015 debian-binary │ +rw-r--r-- 0/0 2572 Sep 4 08:21 2015 control.tar.gz │ +rw-r--r-- 0/0 37692 Sep 4 08:21 2015 data.tar.xz │ --- /dev/null ├── +++ control.tar.gz │ │ --- /dev/null │ ├── +++ control.tar │ │ ├── metadata │ │ │ @@ -0,0 +1,5 @@ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:40 ./ │ │ │ +-rw-r--r-- root/root 4630 2015-09-04 08:21:40 ./md5sums │ │ │ +-rw-r--r-- root/root 1186 2015-09-04 08:21:40 ./control │ │ │ +-rwxr-xr-x root/root 255 2015-09-04 08:21:40 ./prerm │ │ │ +-rwxr-xr-x root/root 158 2015-09-04 08:21:40 ./postinst │ │ │ --- /dev/null │ │ ├── +++ ./control │ │ │ @@ -0,0 +1,21 @@ │ │ │ +Package: diffoscope │ │ │ +Version: 33 │ │ │ +Architecture: all │ │ │ +Maintainer: Reproducible builds folks │ │ │ +Installed-Size: 223 │ │ │ +Depends: python, python-debian, python-libarchive-c, python-magic, python-rpm, python-tlsh, python:any (<< 2.8), python:any (>= 2.7.5-5~) │ │ │ +Recommends: acl, binutils-multiarch, bzip2, cpio, default-jdk | java-sdk, fontforge-extras, genisoimage, gettext, ghc, gnupg, mono-utils, pdftk, poppler-utils, rpm2cpio, sng, sqlite3, squashfs-tools, unzip, vim-common, xz-utils │ │ │ +Breaks: debbindiff (<< 29) │ │ │ +Replaces: debbindiff (<< 29) │ │ │ +Section: devel │ │ │ +Priority: optional │ │ │ +Homepage: http://diffoscope.org/ │ │ │ +Description: in-depth comparison of files, archives, and directories │ │ │ + diffoscope will try to get to the bottom of what makes files or directories │ │ │ + different. It will recursively unpack archives of many kinds and transform │ │ │ + various binary formats into more human readable form to compare them. It can │ │ │ + compare two tarballs, ISO images, or PDF just as easily. The differences can │ │ │ + be shown in a text or HTML report. │ │ │ + . │ │ │ + diffoscope is developed as part of the “reproducible builds” Debian │ │ │ + project and was formerly known as “debbindiff”. │ │ │ --- /dev/null │ │ ├── +++ ./prerm │ │ │ @@ -0,0 +1,14 @@ │ │ │ +#!/bin/sh │ │ │ +set -e │ │ │ + │ │ │ +# Automatically added by dhpython: │ │ │ +if which pyclean >/dev/null 2>&1; then │ │ │ + pyclean -p diffoscope │ │ │ +else │ │ │ + dpkg -L diffoscope | grep \.py$ | while read file │ │ │ + do │ │ │ + rm -f "${file}"[co] >/dev/null │ │ │ + done │ │ │ +fi │ │ │ + │ │ │ +# End automatically added section │ │ │ --- /dev/null │ │ ├── +++ ./postinst │ │ │ @@ -0,0 +1,9 @@ │ │ │ +#!/bin/sh │ │ │ +set -e │ │ │ + │ │ │ +# Automatically added by dhpython: │ │ │ +if which pycompile >/dev/null 2>&1; then │ │ │ + pycompile -p diffoscope │ │ │ +fi │ │ │ + │ │ │ +# End automatically added section │ │ ╵ │ ╵ │ --- /dev/null ├── +++ data.tar.xz │ │ --- /dev/null │ ├── +++ data.tar │ │ ├── metadata │ │ │ @@ -0,0 +1,64 @@ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:40 ./ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:39 ./usr/ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:39 ./usr/share/ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:39 ./usr/share/man/ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:39 ./usr/share/man/man1/ │ │ │ +-rw-r--r-- root/root 1331 2015-09-04 08:21:39 ./usr/share/man/man1/diffoscope.1.gz │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:39 ./usr/share/doc/ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:39 ./usr/share/doc/diffoscope/ │ │ │ +-rw-r--r-- root/root 5781 2015-09-04 08:21:00 ./usr/share/doc/diffoscope/changelog.gz │ │ │ +-rw-r--r-- root/root 6123 2015-09-04 08:21:00 ./usr/share/doc/diffoscope/copyright │ │ │ +-rw-r--r-- root/root 2515 2015-09-04 08:21:00 ./usr/share/doc/diffoscope/README │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:38 ./usr/lib/ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:38 ./usr/lib/python2.7/ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:38 ./usr/lib/python2.7/dist-packages/ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:39 ./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/ │ │ │ +-rw-r--r-- root/root 65 2015-09-04 08:21:38 ./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/requires.txt │ │ │ +-rw-r--r-- root/root 3577 2015-09-04 08:21:38 ./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/PKG-INFO │ │ │ +-rw-r--r-- root/root 11 2015-09-04 08:21:38 ./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/top_level.txt │ │ │ +-rw-r--r-- root/root 1 2015-09-04 08:21:38 ./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/dependency_links.txt │ │ │ +-rw-r--r-- root/root 57 2015-09-04 08:21:38 ./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/entry_points.txt │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:39 ./usr/lib/python2.7/dist-packages/diffoscope/ │ │ │ +-rwxr-xr-x root/root 0 2015-09-04 08:21:39 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/ │ │ │ +-rw-r--r-- root/root 9938 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/binary.py │ │ │ +-rw-r--r-- root/root 2900 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/iso9660.py │ │ │ +-rw-r--r-- root/root 1321 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/rpm_fallback.py │ │ │ +-rw-r--r-- root/root 2422 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/gettext.py │ │ │ +-rw-r--r-- root/root 3420 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/zip.py │ │ │ +-rw-r--r-- root/root 1913 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/cpio.py │ │ │ +-rw-r--r-- root/root 1534 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/fonts.py │ │ │ +-rw-r--r-- root/root 4930 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/tar.py │ │ │ +-rw-r--r-- root/root 2347 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/bzip2.py │ │ │ +-rw-r--r-- root/root 6848 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/squashfs.py │ │ │ +-rw-r--r-- root/root 2257 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/text.py │ │ │ +-rw-r--r-- root/root 2315 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/xz.py │ │ │ +-rw-r--r-- root/root 5940 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/__init__.py │ │ │ +-rw-r--r-- root/root 4929 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/debian.py │ │ │ +-rw-r--r-- root/root 6343 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/deb.py │ │ │ +-rw-r--r-- root/root 1075 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/ipk.py │ │ │ +-rw-r--r-- root/root 1505 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/mono.py │ │ │ +-rw-r--r-- root/root 1392 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/sqlite.py │ │ │ +-rw-r--r-- root/root 1682 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/png.py │ │ │ +-rw-r--r-- root/root 2603 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/gzip.py │ │ │ +-rw-r--r-- root/root 9478 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/utils.py │ │ │ +-rw-r--r-- root/root 3319 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/rpm.py │ │ │ +-rw-r--r-- root/root 3348 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/haskell.py │ │ │ +-rw-r--r-- root/root 1894 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/java.py │ │ │ +-rw-r--r-- root/root 1983 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/device.py │ │ │ +-rw-r--r-- root/root 1839 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/symlink.py │ │ │ +-rw-r--r-- root/root 6036 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/directory.py │ │ │ +-rw-r--r-- root/root 5121 2015-09-04 08:21:00 ./usr/lib/python2.7/dist-packages/diffoscope/comparators/libarchive.py │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/text.py │ │ │ @@ -0,0 +1,55 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import codecs │ │ │ +import re │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class TextFile(File): │ │ │ + RE_FILE_TYPE = re.compile(r'\btext\b') │ │ │ + │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return TextFile.RE_FILE_TYPE.search(file.magic_file_type) │ │ │ + │ │ │ + @property │ │ │ + def encoding(self): │ │ │ + if not hasattr(self, '_encoding'): │ │ │ + with self.get_content(): │ │ │ + self._encoding = File.guess_encoding(self.path) │ │ │ + return self._encoding │ │ │ + │ │ │ + @needs_content │ │ │ + def compare(self, other, source=None): │ │ │ + my_encoding = self.encoding or 'utf-8' │ │ │ + other_encoding = other.encoding or 'utf-8' │ │ │ + try: │ │ │ + with codecs.open(self.path, 'r', encoding=my_encoding) as my_content, \ │ │ │ + codecs.open(other.path, 'r', encoding=other_encoding) as other_content: │ │ │ + difference = Difference.from_file(my_content, other_content, self.name, other.name, source) │ │ │ + if my_encoding != other_encoding: │ │ │ + if difference is None: │ │ │ + difference = Difference(None, self.path, other.path, source) │ │ │ --- /dev/null │ │ ├── +++ ./usr/share/man/man1/diffoscope.1.gz │ │ │ │ --- /dev/null │ │ │ ├── +++ diffoscope.1 │ │ │ │ @@ -0,0 +1,111 @@ │ │ │ │ +.\" Man page generated from reStructuredText. │ │ │ │ +. │ │ │ │ +.TH DIFFOSCOPE 1 "" "" "Debian" │ │ │ │ +.SH NAME │ │ │ │ +diffoscope \- in-depth comparison of files, archives, and directories │ │ │ │ +. │ │ │ │ +.nr rst2man-indent-level 0 │ │ │ │ +. │ │ │ │ +.de1 rstReportMargin │ │ │ │ +\\$1 \\n[an-margin] │ │ │ │ +level \\n[rst2man-indent-level] │ │ │ │ +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] │ │ │ │ +- │ │ │ │ +\\n[rst2man-indent0] │ │ │ │ +\\n[rst2man-indent1] │ │ │ │ +\\n[rst2man-indent2] │ │ │ │ +.. │ │ │ │ +.de1 INDENT │ │ │ │ +.\" .rstReportMargin pre: │ │ │ │ +. RS \\$1 │ │ │ │ +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] │ │ │ │ +. nr rst2man-indent-level +1 │ │ │ │ +.\" .rstReportMargin post: │ │ │ │ +.. │ │ │ │ +.de UNINDENT │ │ │ │ +. RE │ │ │ │ +.\" indent \\n[an-margin] │ │ │ │ +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] │ │ │ │ +.nr rst2man-indent-level -1 │ │ │ │ +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] │ │ │ │ +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u │ │ │ │ +.. │ │ │ │ +.SH SYNOPSIS │ │ │ │ +.INDENT 0.0 │ │ │ │ +.INDENT 3.5 │ │ │ │ +diffoscope [\-h] [\-\-version] [\-\-debug] [\-\-html output] [\-\-text output] [\-\-max\-report\-size bytes] [\-\-css url] file1 file2 │ │ │ │ +.UNINDENT │ │ │ │ +.UNINDENT │ │ │ │ +.SH DESCRIPTION │ │ │ │ +.sp │ │ │ │ +diffoscope will try to get to the bottom of what makes files or │ │ │ │ +directories different. It will recursively unpack archives of many kinds │ │ │ │ +and transform various binary formats into more human readable form to │ │ │ │ +compare them. It can compare two tarballs, ISO images, or PDF just as │ │ │ │ +easily. │ │ │ │ +.sp │ │ │ │ +It can be scripted through error codes, and a report can be produced │ │ │ │ +with the detected differences. The report can be text or HTML. │ │ │ │ +When no type of report has been selected, diffoscope defaults │ │ │ │ +to write a text report on the standard output. │ │ │ ╵ │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/changes.py │ │ │ @@ -0,0 +1,346 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# changes.py — .changes file handling class │ │ │ +# │ │ │ +# This file was originally part of debexpo │ │ │ +# https://alioth.debian.org/projects/debexpo/ │ │ │ +# │ │ │ +# Copyright © 2008 Jonny Lamb │ │ │ +# Copyright © 2010 Jan Dittberner │ │ │ +# Copyright © 2012 Arno Töll │ │ │ +# Copyright © 2012 Paul Tagliamonte │ │ │ +# Copyright © 2014 Jérémy Bobbio │ │ │ +# │ │ │ +# Permission is hereby granted, free of charge, to any person │ │ │ +# obtaining a copy of this software and associated documentation │ │ │ +# files (the "Software"), to deal in the Software without │ │ │ +# restriction, including without limitation the rights to use, │ │ │ +# copy, modify, merge, publish, distribute, sublicense, and/or sell │ │ │ +# copies of the Software, and to permit persons to whom the │ │ │ +# Software is furnished to do so, subject to the following │ │ │ +# conditions: │ │ │ +# │ │ │ +# The above copyright notice and this permission notice shall be │ │ │ +# included in all copies or substantial portions of the Software. │ │ │ +# │ │ │ +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, │ │ │ +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES │ │ │ +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND │ │ │ +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT │ │ │ +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, │ │ │ +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ │ │ +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR │ │ │ +# OTHER DEALINGS IN THE SOFTWARE. │ │ │ +""" │ │ │ +This code deals with the reading and processing of Debian .changes files. This │ │ │ +code is copyright (c) Jonny Lamb, and is used by dput, rather then created as │ │ │ +a result of it. Thank you Jonny. │ │ │ +""" │ │ │ + │ │ │ +__author__ = 'Jonny Lamb' │ │ │ +__copyright__ = 'Copyright © 2008 Jonny Lamb, Copyright © 2010 Jan Dittberner' │ │ │ +__license__ = 'MIT' │ │ │ + │ │ │ +import sys │ │ │ +import os.path │ │ │ +import hashlib │ │ │ +import subprocess │ │ │ +from debian import deb822 │ │ │ +from diffoscope import logger, tool_required │ │ │ + │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/requires.txt │ │ │ @@ -0,0 +1,5 @@ │ │ │ +python-debian │ │ │ +Magic-file-extensions │ │ │ +rpm-python │ │ │ +libarchive-c │ │ │ +tlsh │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/__main__.py │ │ │ @@ -0,0 +1,153 @@ │ │ │ +#!/usr/bin/env python │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from __future__ import print_function │ │ │ + │ │ │ +import argparse │ │ │ +from contextlib import contextmanager │ │ │ +import logging │ │ │ +import codecs │ │ │ +import os │ │ │ +import signal │ │ │ +import sys │ │ │ +import traceback │ │ │ +from diffoscope import logger, VERSION, set_locale │ │ │ +import diffoscope.comparators │ │ │ +from diffoscope.config import Config │ │ │ +from diffoscope.presenters.html import output_html │ │ │ +from diffoscope.presenters.text import output_text │ │ │ + │ │ │ + │ │ │ +def create_parser(): │ │ │ + parser = argparse.ArgumentParser( │ │ │ + description='Highlight differences between two builds ' │ │ │ + 'of Debian packages') │ │ │ + parser.add_argument('--version', action='version', │ │ │ + version='diffoscope %s' % VERSION) │ │ │ + parser.add_argument('--list-tools', nargs=0, action=ListToolsAction, │ │ │ + help='show external tools required and exit') │ │ │ + parser.add_argument('--debug', dest='debug', action='store_true', │ │ │ + default=False, help='display debug messages') │ │ │ + parser.add_argument('--debugger', action='store_true', │ │ │ + help='Open the python debugger in case of crashes.') │ │ │ + parser.add_argument('--html', metavar='output', dest='html_output', │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/__init__.py │ │ │ @@ -0,0 +1,113 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from functools import wraps │ │ │ +import logging │ │ │ +from distutils.spawn import find_executable │ │ │ +import os │ │ │ + │ │ │ +VERSION = "33" │ │ │ + │ │ │ +logger = logging.getLogger("diffoscope") │ │ │ +logger.setLevel(logging.WARNING) │ │ │ +ch = logging.StreamHandler() │ │ │ +ch.setLevel(logging.DEBUG) │ │ │ +logger.addHandler(ch) │ │ │ +formatter = logging.Formatter('%(levelname)8s %(message)s') │ │ │ +ch.setFormatter(formatter) │ │ │ + │ │ │ + │ │ │ +class RequiredToolNotFound(Exception): │ │ │ + PROVIDERS = { 'ar': { 'debian': 'binutils-multiarch' } │ │ │ + , 'bzip2': { 'debian': 'bzip2' } │ │ │ + , 'cmp': { 'debian': 'diffutils' } │ │ │ + , 'cpio': { 'debian': 'cpio' } │ │ │ + , 'diff': { 'debian': 'diffutils' } │ │ │ + , 'file': { 'debian': 'file' } │ │ │ + , 'find': { 'debian': 'findutils' } │ │ │ + , 'getfacl': { 'debian': 'acl' } │ │ │ + , 'ghc': { 'debian': 'ghc' } │ │ │ + , 'gpg': { 'debian': 'gnupg' } │ │ │ + , 'gzip': { 'debian': 'gzip' } │ │ │ + , 'isoinfo': { 'debian': 'genisoimage' } │ │ │ + , 'javap': { 'debian': 'default-jdk | java-sdk' } │ │ │ + , 'ls': { 'debian': 'coreutils' } │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/cpio.py │ │ │ @@ -0,0 +1,50 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Reiner Herrmann │ │ │ +# 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.libarchive import LibarchiveContainer │ │ │ +from diffoscope.comparators.utils import Command │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class CpioContent(Command): │ │ │ + @tool_required('cpio') │ │ │ + def cmdline(self): │ │ │ + return ['cpio', '--quiet', '--numeric-uid-gid', '--force-local', '-tvF', self.path] │ │ │ + │ │ │ + │ │ │ +class CpioFile(File): │ │ │ + RE_FILE_TYPE = re.compile(r'\bcpio archive\b') │ │ │ + │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return CpioFile.RE_FILE_TYPE.search(file.magic_file_type) │ │ │ + │ │ │ + @needs_content │ │ │ + def compare_details(self, other, source=None): │ │ │ + differences = [] │ │ │ + differences.append(Difference.from_command( │ │ │ + CpioContent, self.path, other.path, source="file list")) │ │ │ + with LibarchiveContainer(self).open() as my_container, \ │ │ │ + LibarchiveContainer(other).open() as other_container: │ │ │ + differences.extend(my_container.compare(other_container)) │ │ │ + return differences │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/zip.py │ │ │ @@ -0,0 +1,105 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +import zipfile │ │ │ +from diffoscope.difference import Difference │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.directory import Directory │ │ │ +from diffoscope.comparators.utils import Archive, ArchiveMember, Command │ │ │ + │ │ │ + │ │ │ +class Zipinfo(Command): │ │ │ + @tool_required('zipinfo') │ │ │ + def cmdline(self): │ │ │ + return ['zipinfo', self.path] │ │ │ + │ │ │ + def filter(self, line): │ │ │ + # we don't care about the archive file path │ │ │ + if re.match('^Archive:.*', line): │ │ │ + return '' │ │ │ + return line │ │ │ + │ │ │ + │ │ │ +class ZipinfoVerbose(Zipinfo): │ │ │ + @tool_required('zipinfo') │ │ │ + def cmdline(self): │ │ │ + return ['zipinfo', '-v', self.path] │ │ │ + │ │ │ + │ │ │ +class ZipDirectory(Directory, ArchiveMember): │ │ │ + def __init__(self, archive, member_name): │ │ │ + ArchiveMember.__init__(self, archive, member_name) │ │ │ + │ │ │ --- /dev/null │ │ ├── +++ /tmp/tmpHPIZAjdiffoscope/./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/dependency_links.txt │ │ │ @@ -0,0 +1 @@ │ │ │ +00000000: 0a . │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/libarchive.py │ │ │ @@ -0,0 +1,148 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from __future__ import absolute_import │ │ │ + │ │ │ +from contextlib import contextmanager │ │ │ +import ctypes │ │ │ +import os.path │ │ │ +import libarchive │ │ │ +from diffoscope import logger │ │ │ +from diffoscope.comparators.device import Device │ │ │ +from diffoscope.comparators.directory import Directory │ │ │ +from diffoscope.comparators.symlink import Symlink │ │ │ +from diffoscope.comparators.utils import Archive, ArchiveMember │ │ │ + │ │ │ + │ │ │ +# Monkeypatch libarchive-c (<< 2.2) │ │ │ +if not hasattr(libarchive.ffi, 'entry_rdevmajor'): │ │ │ + libarchive.ffi.ffi('entry_rdevmajor', [libarchive.ffi.c_archive_entry_p], ctypes.c_uint) │ │ │ + libarchive.ArchiveEntry.rdevmajor = property(lambda self: libarchive.ffi.entry_rdevmajor(self._entry_p)) │ │ │ +if not hasattr(libarchive.ffi, 'entry_rdevminor'): │ │ │ + libarchive.ffi.ffi('entry_rdevminor', [libarchive.ffi.c_archive_entry_p], ctypes.c_uint) │ │ │ + libarchive.ArchiveEntry.rdevminor = property(lambda self: libarchive.ffi.entry_rdevminor(self._entry_p)) │ │ │ + │ │ │ + │ │ │ +class LibarchiveMember(ArchiveMember): │ │ │ + def __init__(self, archive, entry): │ │ │ + super(LibarchiveMember, self).__init__(archive, entry.pathname) │ │ │ + │ │ │ + def is_directory(self): │ │ │ + return False │ │ │ + │ │ │ + def is_symlink(self): │ │ │ + return False │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/haskell.py │ │ │ @@ -0,0 +1,88 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +import platform │ │ │ +import struct │ │ │ +import subprocess │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import Command │ │ │ +from diffoscope.difference import Difference │ │ │ +from diffoscope import logger │ │ │ + │ │ │ + │ │ │ +class ShowIface(Command): │ │ │ + @tool_required('ghc') │ │ │ + def cmdline(self): │ │ │ + return ['ghc', '--show-iface', self.path] │ │ │ + │ │ │ + │ │ │ +HI_MAGIC_32 = struct.pack('!I', 0x1face) │ │ │ +HI_MAGIC_64 = struct.pack('!I', 0x1face64) │ │ │ +if platform.architecture()[0] == '32bit': │ │ │ + HI_MAGIC = HI_MAGIC_32 │ │ │ +else: │ │ │ + HI_MAGIC = HI_MAGIC_64 │ │ │ + │ │ │ +class HiFile(File): │ │ │ + RE_FILE_EXTENSION = re.compile(r'\.(p_|dyn_)?hi$') │ │ │ + │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + if not HiFile.RE_FILE_EXTENSION.search(file.name): │ │ │ + return False │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/gzip.py │ │ │ @@ -0,0 +1,74 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +import subprocess │ │ │ +import os.path │ │ │ +import diffoscope.comparators │ │ │ +from diffoscope import logger, tool_required │ │ │ +from diffoscope.comparators.binary import needs_content │ │ │ +from diffoscope.comparators.utils import Archive, get_compressed_content_name, NO_COMMENT │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class GzipContainer(Archive): │ │ │ + @property │ │ │ + def path(self): │ │ │ + return self._path │ │ │ + │ │ │ + def open_archive(self, path): │ │ │ + self._path = path │ │ │ + return self │ │ │ + │ │ │ + def close_archive(self): │ │ │ + self._path = None │ │ │ + │ │ │ + def get_members(self): │ │ │ + return {'gzip-content': self.get_member(self.get_member_names()[0])} │ │ │ + │ │ │ + def get_member_names(self): │ │ │ + return [get_compressed_content_name(self.path, '.gz')] │ │ │ + │ │ │ + @tool_required('gzip') │ │ │ + def extract(self, member_name, dest_dir): │ │ │ + dest_path = os.path.join(dest_dir, member_name) │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/difference.py │ │ │ @@ -0,0 +1,422 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from contextlib import contextmanager │ │ │ +import os │ │ │ +import os.path │ │ │ +import re │ │ │ +import subprocess │ │ │ +import sys │ │ │ +import traceback │ │ │ +from StringIO import StringIO │ │ │ +from threading import Thread │ │ │ +from multiprocessing import Queue │ │ │ +from diffoscope.config import Config │ │ │ +from diffoscope import logger, tool_required, RequiredToolNotFound │ │ │ + │ │ │ + │ │ │ +class DiffParser(object): │ │ │ + RANGE_RE = re.compile(r'^@@\s+-(?P\d+)(,(?P\d+))?\s+\+(?P\d+)(,(?P\d+))?\s+@@$') │ │ │ + │ │ │ + def __init__(self, output, end_nl_q1, end_nl_q2): │ │ │ + self._output = output │ │ │ + self._end_nl_q1 = end_nl_q1 │ │ │ + self._end_nl_q2 = end_nl_q2 │ │ │ + self._action = self.read_headers │ │ │ + self._diff = StringIO() │ │ │ + self._success = False │ │ │ + self._remaining_hunk_lines = None │ │ │ + self._block_len = None │ │ │ + self._direction = None │ │ │ + self._end_nl = None │ │ │ + │ │ │ + @property │ │ │ + def diff(self): │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/squashfs.py │ │ │ @@ -0,0 +1,205 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Reiner Herrmann │ │ │ +# 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from contextlib import contextmanager │ │ │ +import re │ │ │ +import subprocess │ │ │ +import stat │ │ │ +from diffoscope import logger, tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.device import Device │ │ │ +from diffoscope.comparators.directory import Directory │ │ │ +from diffoscope.comparators.symlink import Symlink │ │ │ +from diffoscope.comparators.utils import Archive, ArchiveMember, Command │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class SquashfsSuperblock(Command): │ │ │ + @tool_required('unsquashfs') │ │ │ + def cmdline(self): │ │ │ + return ['unsquashfs', '-s', self.path] │ │ │ + │ │ │ + def filter(self, line): │ │ │ + # strip filename │ │ │ + return re.sub(r'^(Found a valid .*) on .*', '\\1', line) │ │ │ + │ │ │ + │ │ │ +class SquashfsListing(Command): │ │ │ + @tool_required('unsquashfs') │ │ │ + def cmdline(self): │ │ │ + return ['unsquashfs', '-d', '', '-lls', self.path] │ │ │ + │ │ │ + │ │ │ +class SquashfsMember(ArchiveMember): │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/entry_points.txt │ │ │ @@ -0,0 +1,3 @@ │ │ │ +[console_scripts] │ │ │ +diffoscope = diffoscope.__main__:main │ │ │ + │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/rpm_fallback.py │ │ │ @@ -0,0 +1,36 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +from diffoscope.comparators.binary import File │ │ │ + │ │ │ +class AbstractRpmFile(File): │ │ │ + RE_FILE_TYPE = re.compile('^RPM\s') │ │ │ + │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return AbstractRpmFile.RE_FILE_TYPE.search(file.magic_file_type) │ │ │ + │ │ │ +class RpmFile(File): │ │ │ + def compare(self, other, source=None): │ │ │ + difference = self.compare_bytes(other) │ │ │ + if not difference: │ │ │ + return None │ │ │ + difference.add_comment('Unable to find Python rpm module. Falling back to binary comparison.') │ │ │ + return difference │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/debian.py │ │ │ @@ -0,0 +1,145 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from contextlib import contextmanager │ │ │ +import os.path │ │ │ +import re │ │ │ +from diffoscope.changes import Changes │ │ │ +import diffoscope.comparators │ │ │ +from diffoscope.comparators.binary import File, NonExistingFile, needs_content │ │ │ +from diffoscope.comparators.utils import Container, NO_COMMENT │ │ │ +from diffoscope.config import Config │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +DOT_CHANGES_FIELDS = [ │ │ │ + "Format", "Source", "Binary", "Architecture", │ │ │ + "Version", "Distribution", "Urgency", │ │ │ + "Maintainer", "Changed-By", "Description", │ │ │ + "Changes", │ │ │ + ] │ │ │ + │ │ │ + │ │ │ +class DotChangesMember(File): │ │ │ + def __init__(self, container, member_name): │ │ │ + self._container = container │ │ │ + self._name = member_name │ │ │ + self._path = None │ │ │ + │ │ │ + @property │ │ │ + def container(self): │ │ │ + return self._container │ │ │ + │ │ │ + @property │ │ │ + def name(self): │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/fonts.py │ │ │ @@ -0,0 +1,45 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import Command │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class Showttf(Command): │ │ │ + @tool_required('showttf') │ │ │ + def cmdline(self): │ │ │ + return ['showttf', self.path] │ │ │ + │ │ │ + def filter(self, line): │ │ │ + return line.decode('latin-1').encode('utf-8') │ │ │ + │ │ │ + │ │ │ +class TtfFile(File): │ │ │ + RE_FILE_TYPE = re.compile(r'^(TrueType|OpenType) font data$') │ │ │ + │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return TtfFile.RE_FILE_TYPE.match(file.magic_file_type) │ │ │ + │ │ │ + @needs_content │ │ │ + def compare_details(self, other, source=None): │ │ │ + return [Difference.from_command(Showttf, self.path, other.path)] │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/utils.py │ │ │ @@ -0,0 +1,325 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from abc import ABCMeta, abstractmethod │ │ │ +from contextlib import contextmanager │ │ │ +# The following would be shutil.which in Python 3.3 │ │ │ +import os │ │ │ +import shutil │ │ │ +from stat import S_ISCHR, S_ISBLK │ │ │ +from StringIO import StringIO │ │ │ +import subprocess │ │ │ +import tempfile │ │ │ +from threading import Thread │ │ │ +import diffoscope.comparators │ │ │ +from diffoscope.comparators.binary import File, NonExistingFile │ │ │ +from diffoscope.config import Config │ │ │ +from diffoscope.difference import Difference │ │ │ +from diffoscope import logger, tool_required │ │ │ + │ │ │ + │ │ │ +@contextmanager │ │ │ +def make_temp_directory(): │ │ │ + temp_dir = tempfile.mkdtemp(suffix='diffoscope') │ │ │ + try: │ │ │ + yield temp_dir │ │ │ + finally: │ │ │ + shutil.rmtree(temp_dir) │ │ │ + │ │ │ + │ │ │ +@tool_required('ar') │ │ │ +def get_ar_content(path): │ │ │ + if path == '/dev/null': │ │ │ + return '' │ │ │ + return subprocess.check_output( │ │ │ --- /dev/null │ │ ├── +++ ./usr/bin/diffoscope │ │ │ @@ -0,0 +1,10 @@ │ │ │ +#!/usr/bin/python │ │ │ +# EASY-INSTALL-ENTRY-SCRIPT: 'diffoscope==33','console_scripts','diffoscope' │ │ │ +__requires__ = 'diffoscope==33' │ │ │ +import sys │ │ │ +from pkg_resources import load_entry_point │ │ │ + │ │ │ +if __name__ == '__main__': │ │ │ + sys.exit( │ │ │ + load_entry_point('diffoscope==33', 'console_scripts', 'diffoscope')() │ │ │ + ) │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/presenters/icon.py │ │ │ @@ -0,0 +1,20 @@ │ │ │ +# Generated from favicon.png │ │ │ +FAVICON_BASE64 = """ │ │ │ +iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz │ │ │ +AAADdgAAA3YBfdWCzAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAM8SURB │ │ │ +VFiF7dZdaJZlGAfw3/O+e9XNjy3XllZzWImBxIpifqTgsmUHBSIeBNFBdRh0UHQ0xGdb66CFBIER │ │ │ +ZEFRQRRUw0HkQkLMYQSBdpCFWdgSP6jpNn2393k68Nn2bj3vXtb6UOh/cl/39b/u67mem/913zf/ │ │ │ +4z9GUDw5xNI8n5WIzW9m/QF2YluJmI8203mALzEvLWAerRs4Pz6vSIkZSlsYk0/GkaB0zEgyDgWM │ │ │ +lijyWsJuS2fkQwu8pqpMTA0ypeiShG71Bp0rU8Q+A47MWEDgvHZbZl/Av4SpIgxVYB3IqwZDmoUu │ │ │ +YkzosDaNchpAoFqkSmgjGPWzLieF1k3JHVsjvCJQHBYaSy9g1E3m+QImQiK9SbNGqJDzuiDZ0lgg │ │ │ +QJCsyelDq8BB47sbCWTsnvhG3kqcLL833eqF4hk1EOoTOjZjnnaRdq2l6KtMAyl4/g23bKJrun+M │ │ │ +t9uOq7nvqOVbebWYy9Adc1fM/R/3CO780dONbIeFPHsPw+ULeM5ZHTbXHfUtXpxOZzgT5z02UGsZ │ │ │ +ThRzF/ilht9G+fr4zfbfccIxXIa7uVTup69i7LJXaEEqF3on1d9ludBLpVLOVoT1lky9QYtwQ6p3 │ │ │ +WAVqSyUslWwSbRrkPJrMtgn0iBVk7BPJ4sGE24EPQKW3jLgdzWKLBTbg0yTuZeGkDsp2gcUGDesH │ │ │ +GQ/gK5G8nLMigWiCe3jCrnTRiJ9EYjm1ImsUEq5l8hScPXbpsVtlKhfan+pv0yD0ZqmU5XegGBmn │ │ │ +DIo/pylj8oYLOP3Eab/v5Zmi6EtZ3vv1FU/2r7LioYSL2dNybbdiMdptFU57++20Vqh+iq/Tap1W │ │ │ +paWY611wmyWyUzxZDbIWTvEV1Ildn5agfBumoUMTtoisFvhebAAHZWxX0ChwTuScvHdVeVykToys │ │ │ +MyJ7ittwbmj31J+6IrRDp5XTfBt1WJ+WYnZdMB0F3xlUgD5uDVj2Sa+a4SrzH+FeyHHmw17V112w │ │ │ +aBOZgP4WczkLZsILar1fpIliMU4XZoK/poE0XHl+N8v6QcGAUbVymnDIZVnzrTXqGzmnhKLxZX/n │ │ │ +iyiDjIJgwg4Se1GR/Y8idGPysh6fr0i1i/AHMWHgrwUJgNAAAAAASUVORK5CYII= │ │ │ +""".replace("\n", "") │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/png.py │ │ │ @@ -0,0 +1,51 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from functools import partial │ │ │ +import re │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import Command │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class Sng(Command): │ │ │ + @tool_required('sng') │ │ │ + def cmdline(self): │ │ │ + return ['sng'] │ │ │ + │ │ │ + def feed_stdin(self, stdin): │ │ │ + try: │ │ │ + with open(self.path) as f: │ │ │ + for buf in iter(partial(f.read, 32768), b''): │ │ │ + stdin.write(buf) │ │ │ + finally: │ │ │ + stdin.close() │ │ │ + │ │ │ + │ │ │ +class PngFile(File): │ │ │ + RE_FILE_TYPE = re.compile(r'^PNG image data\b') │ │ │ + │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return PngFile.RE_FILE_TYPE.match(file.magic_file_type) │ │ │ + │ │ │ + @needs_content │ │ │ + def compare_details(self, other, source=None): │ │ │ --- /dev/null │ │ ├── +++ ./usr/share/doc/diffoscope/copyright │ │ │ @@ -0,0 +1,141 @@ │ │ │ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ │ │ │ +Upstream-Name: diffoscope │ │ │ +Source: https://anonscm.debian.org/cgit/reproducible/diffoscope.git/ │ │ │ + │ │ │ +Files: * │ │ │ +Copyright: 2014-2015 Jérémy Bobbio │ │ │ +License: GPL-3+ │ │ │ + │ │ │ +Files: diffoscope/changes.py │ │ │ +Copyright: 2008 Jonny Lamb │ │ │ + 2010 Jan Dittberner │ │ │ + 2012 Arno Töll │ │ │ + 2012 Paul Tagliamonte │ │ │ + 2014-2015 Jérémy Bobbio │ │ │ +License: Expat │ │ │ + │ │ │ +Files: diffoscope/comparators/__init__.py │ │ │ +Copyright: 2014-2015 Jérémy Bobbio │ │ │ + 2015 Helmut Grohne │ │ │ + 2015 Chris Lamb │ │ │ + 2015 Reiner Herrmann │ │ │ +License: GPL-3+ │ │ │ + │ │ │ +Files: diffoscope/comparators/bzip2.py │ │ │ + diffoscope/comparators/elf.py │ │ │ + diffoscope/comparators/fonts.py │ │ │ + diffoscope/comparators/gettext.py │ │ │ + diffoscope/comparators/gzip.py │ │ │ + diffoscope/comparators/haskell.py │ │ │ + diffoscope/comparators/pdf.py │ │ │ + diffoscope/comparators/png.py │ │ │ + diffoscope/comparators/utils.py │ │ │ + diffoscope/comparators/xz.py │ │ │ + diffoscope/comparators/zip.py │ │ │ +Copyright: 2014-2015 Jérémy Bobbio │ │ │ + 2015 Reiner Herrmann │ │ │ +License: GPL-3+ │ │ │ + │ │ │ +Files: diffoscope/comparators/cpio.py │ │ │ + diffoscope/comparators/directory.py │ │ │ + diffoscope/comparators/rpm.py │ │ │ + diffoscope/comparators/squashfs.py │ │ │ +Copyright: 2015 Reiner Herrmann │ │ │ + 2015 Jérémy Bobbio │ │ │ +License: GPL-3+ │ │ │ + │ │ │ +Files: diffoscope/presenters/html.py │ │ │ +Copyright: 2014-2015 Jérémy Bobbio │ │ │ + 2015 Reiner Herrmann │ │ │ + 2012-2013 Olivier Matz │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/ipk.py │ │ │ @@ -0,0 +1,30 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Reiner Herrmann │ │ │ +# 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +from diffoscope.comparators.gzip import GzipFile │ │ │ + │ │ │ + │ │ │ +class IpkFile(GzipFile): │ │ │ + RE_FILE_EXTENSION = re.compile('\.ipk$') │ │ │ + │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return IpkFile.RE_FILE_EXTENSION.search(file.name) │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/symlink.py │ │ │ @@ -0,0 +1,52 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from contextlib import contextmanager │ │ │ +import os │ │ │ +import tempfile │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import format_symlink │ │ │ +from diffoscope.difference import Difference │ │ │ +from diffoscope import logger │ │ │ + │ │ │ + │ │ │ +class Symlink(File): │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return file.is_symlink() │ │ │ + │ │ │ + @property │ │ │ + def symlink_destination(self): │ │ │ + return os.readlink(self.name) │ │ │ + │ │ │ + @contextmanager │ │ │ + def get_content(self): │ │ │ + with tempfile.NamedTemporaryFile(suffix='diffoscope') as f: │ │ │ + f.write(format_symlink(self.symlink_destination)) │ │ │ + f.flush() │ │ │ + self._path = f.name │ │ │ + yield │ │ │ + self._path = None │ │ │ + │ │ │ + @needs_content │ │ │ + def compare(self, other, source=None): │ │ │ + logger.debug('my_content %s', self.path) │ │ │ + with open(self.path) as my_content, \ │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/iso9660.py │ │ │ @@ -0,0 +1,80 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +import subprocess │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.libarchive import LibarchiveContainer │ │ │ +from diffoscope.comparators.utils import Command │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +@tool_required('isoinfo') │ │ │ +def get_iso9660_names(path): │ │ │ + # We always use RockRidge for names. Let's see if this proves │ │ │ + # problematic later │ │ │ + cmd = ['isoinfo', '-R', '-f', '-i', path] │ │ │ + return subprocess.check_output(cmd, shell=False).strip().split('\n') │ │ │ + │ │ │ + │ │ │ +class ISO9660PVD(Command): │ │ │ + @tool_required('isoinfo') │ │ │ + def cmdline(self): │ │ │ + return ['isoinfo', '-d', '-i', self.path] │ │ │ + │ │ │ + │ │ │ +class ISO9660Listing(Command): │ │ │ + def __init__(self, path, extension=None, *args, **kwargs): │ │ │ + self._extension = extension │ │ │ + super(ISO9660Listing, self).__init__(path, *args, **kwargs) │ │ │ + │ │ │ + @tool_required('isoinfo') │ │ │ + def cmdline(self): │ │ │ + cmd = ['isoinfo', '-l', '-i', self.path] │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/presenters/text.py │ │ │ @@ -0,0 +1,58 @@ │ │ │ +#!/usr/bin/env python │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Jérémy Bobbio │ │ │ +# 2015 Helmut Grohne │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import sys │ │ │ +from diffoscope import logger │ │ │ + │ │ │ + │ │ │ +def print_difference(difference, print_func): │ │ │ + if difference.comments: │ │ │ + for comment in difference.comments: │ │ │ + for line in comment.split('\n'): │ │ │ + print_func(u"│┄ %s" % line) │ │ │ + if difference.unified_diff: │ │ │ + for line in difference.unified_diff.splitlines(): │ │ │ + print_func(u"│ %s" % line) │ │ │ + │ │ │ +def print_details(difference, print_func): │ │ │ + if not difference.details: │ │ │ + return │ │ │ + for detail in difference.details: │ │ │ + if detail.source1 == detail.source2: │ │ │ + print_func(u"├── %s" % detail.source1) │ │ │ + else: │ │ │ + print_func(u"│ --- %s" % (detail.source1)) │ │ │ + print_func(u"├── +++ %s" % (detail.source2)) │ │ │ + print_difference(detail, print_func) │ │ │ + def new_print_func(*args, **kwargs): │ │ │ + print_func(u'│ ', *args, **kwargs) │ │ │ + print_details(detail, new_print_func) │ │ │ + print_func(u'╵') │ │ │ + │ │ │ +def output_text(difference, print_func): │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/presenters/html.py │ │ │ @@ -0,0 +1,522 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# © 2015 Reiner Herrmann │ │ │ +# © 2012-2013 Olivier Matz │ │ │ +# © 2012 Alan De Smet │ │ │ +# © 2012 Sergey Satskiy │ │ │ +# © 2012 scito │ │ │ +# │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ +# │ │ │ +# │ │ │ +# Most of the code is borrowed from diff2html.py available at: │ │ │ +# http://git.droids-corp.org/?p=diff2html.git │ │ │ +# │ │ │ +# Part of the code is inspired by diff2html.rb from │ │ │ +# Dave Burt (mainly for html theme) │ │ │ +# │ │ │ + │ │ │ +from __future__ import print_function │ │ │ +import cgi │ │ │ +import re │ │ │ +import sys │ │ │ +from xml.sax.saxutils import escape │ │ │ +from diffoscope import logger, VERSION │ │ │ +from diffoscope.config import Config │ │ │ +from diffoscope.presenters.icon import FAVICON_BASE64 │ │ │ + │ │ │ +# minimum line size, we add a zero-sized breakable space every │ │ │ +# LINESIZE characters │ │ │ +LINESIZE = 20 │ │ │ +MAX_LINE_SIZE = 1024 │ │ │ +TABSIZE = 8 │ │ │ + │ │ │ +# Characters we're willing to word wrap on │ │ │ +WORDBREAK = " \t;.,/):-" │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/PKG-INFO │ │ │ @@ -0,0 +1,87 @@ │ │ │ +Metadata-Version: 1.1 │ │ │ +Name: diffoscope │ │ │ +Version: 33 │ │ │ +Summary: display differences between files │ │ │ +Home-page: https://wiki.debian.org/ReproducibleBuilds │ │ │ +Author: Lunar │ │ │ +Author-email: lunar@debian.org │ │ │ +License: UNKNOWN │ │ │ +Description: diffoscope │ │ │ + ========== │ │ │ + │ │ │ + diffoscope will try to get to the bottom of what makes files or │ │ │ + directories different. It will recursively unpack archives of many kinds │ │ │ + and transform various binary formats into more human readable form to │ │ │ + compare them. It can compare two tarballs, ISO images, or PDF just as │ │ │ + easily. │ │ │ + │ │ │ + It can be scripted through error codes, and a report can be produced │ │ │ + with the detected differences. The report can be text or HTML. │ │ │ + When no type of report has been selected, diffoscope defaults │ │ │ + to write a text report on the standard output. │ │ │ + │ │ │ + diffoscope will exit with 0 if there's no differences and 1 if there │ │ │ + are. An HTML report can be produced with the detected differences. │ │ │ + │ │ │ + diffoscope is developed as part of the “reproducible builds” Debian │ │ │ + project: │ │ │ + It is meant to be able to quickly understand why two builds of the same │ │ │ + package produce different outputs. diffoscope was previously named │ │ │ + debbindiff. │ │ │ + │ │ │ + Example │ │ │ + ------- │ │ │ + │ │ │ + $ bin/diffoscope.py --html output.html build1.changes build2.changes │ │ │ + │ │ │ + This will compare `build1.changes` and `build2.changes` and create │ │ │ + `output.html` if there are differences between the two files. │ │ │ + │ │ │ + External dependencies │ │ │ + --------------------- │ │ │ + │ │ │ + Required Python modules: │ │ │ + │ │ │ + * Available on PyPI: libarchive-c, python-debian. │ │ │ + * Magic-file-extension is built from file: │ │ │ + http://www.darwinsys.com/file/ │ │ │ + Available on Debian and Fedora as python-magic. │ │ │ + * rpm-python is built from rpm: │ │ │ + http://rpm.org/ │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/rpm.py │ │ │ @@ -0,0 +1,96 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Reiner Herrmann │ │ │ +# 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from __future__ import absolute_import │ │ │ +import os.path │ │ │ +import subprocess │ │ │ +import rpm │ │ │ +from diffoscope import logger, tool_required │ │ │ +from diffoscope.comparators.rpm_fallback import AbstractRpmFile │ │ │ +from diffoscope.comparators.binary import needs_content │ │ │ +from diffoscope.comparators.utils import Archive, make_temp_directory │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ +def get_rpm_header(path, ts): │ │ │ + header = '' │ │ │ + with open(path, 'r') as f: │ │ │ + try: │ │ │ + hdr = ts.hdrFromFdno(f) │ │ │ + except rpm.error, e: │ │ │ + logger.error("reading rpm header failed: %s", str(e)) │ │ │ + return str(e) │ │ │ + for rpmtag in sorted(rpm.tagnames): │ │ │ + if rpmtag not in hdr: │ │ │ + continue │ │ │ + # header fields can contain binary data │ │ │ + try: │ │ │ + value = str(hdr[rpmtag]).decode('utf-8') │ │ │ + except UnicodeDecodeError: │ │ │ + value = str(hdr[rpmtag]).encode('hex_codec') │ │ │ + header += "%s: %s\n" % (rpm.tagnames[rpmtag], value) │ │ │ + │ │ │ + return header │ │ │ + │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/deb.py │ │ │ @@ -0,0 +1,161 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from __future__ import absolute_import │ │ │ + │ │ │ +import re │ │ │ +import os.path │ │ │ +from debian.arfile import ArFile │ │ │ +from diffoscope import logger │ │ │ +from diffoscope.difference import Difference │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import \ │ │ │ + Archive, ArchiveMember, get_ar_content │ │ │ +from diffoscope.comparators.tar import TarContainer, get_tar_listing │ │ │ + │ │ │ +AR_EXTRACTION_BUFFER_SIZE = 32768 │ │ │ + │ │ │ +class ArContainer(Archive): │ │ │ + def open_archive(self, path): │ │ │ + return ArFile(filename=path) │ │ │ + │ │ │ + def close_archive(self): │ │ │ + # ArFile don't have to be closed │ │ │ + pass │ │ │ + │ │ │ + def get_member_names(self): │ │ │ + return self.archive.getnames() │ │ │ + │ │ │ + def extract(self, member_name, dest_dir): │ │ │ + logger.debug('ar extracting %s to %s', member_name, dest_dir) │ │ │ + member = self.archive.getmember(member_name) │ │ │ + dest_path = os.path.join(dest_dir, os.path.basename(member_name)) │ │ │ + member.seek(0) │ │ │ + with open(dest_path, 'w') as fp: │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/binary.py │ │ │ @@ -0,0 +1,300 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from abc import ABCMeta, abstractmethod │ │ │ +from binascii import hexlify │ │ │ +from contextlib import contextmanager │ │ │ +from functools import wraps │ │ │ +import os │ │ │ +import os.path │ │ │ +import re │ │ │ +from stat import S_ISCHR, S_ISBLK │ │ │ +import subprocess │ │ │ +import tempfile │ │ │ +import tlsh │ │ │ +import magic │ │ │ +from diffoscope.config import Config │ │ │ +from diffoscope.difference import Difference │ │ │ +from diffoscope import tool_required, RequiredToolNotFound, logger │ │ │ + │ │ │ + │ │ │ +@contextmanager │ │ │ +@tool_required('xxd') │ │ │ +def xxd(path): │ │ │ + p = subprocess.Popen(['xxd', path], shell=False, stdout=subprocess.PIPE, │ │ │ + stderr=subprocess.PIPE, close_fds=True) │ │ │ + yield p.stdout │ │ │ + p.stdout.close() │ │ │ + p.stderr.close() │ │ │ + if p.poll() is None: │ │ │ + p.terminate() │ │ │ + p.wait() │ │ │ + │ │ │ + │ │ │ +def hexdump_fallback(path): │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/__init__.py │ │ │ @@ -0,0 +1,168 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# © 2015 Helmut Grohne │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import magic │ │ │ +import operator │ │ │ +import os.path │ │ │ +import re │ │ │ +import sys │ │ │ +import tlsh │ │ │ +from diffoscope import logger, tool_required │ │ │ +from diffoscope.config import Config │ │ │ +from diffoscope.difference import Difference │ │ │ +from diffoscope.comparators.binary import \ │ │ │ + File, FilesystemFile, NonExistingFile, compare_binary_files │ │ │ +from diffoscope.comparators.bzip2 import Bzip2File │ │ │ +from diffoscope.comparators.java import ClassFile │ │ │ +from diffoscope.comparators.cpio import CpioFile │ │ │ +from diffoscope.comparators.deb import DebFile, Md5sumsFile, DebDataTarFile │ │ │ +from diffoscope.comparators.debian import DotChangesFile │ │ │ +from diffoscope.comparators.device import Device │ │ │ +from diffoscope.comparators.directory import Directory, compare_directories │ │ │ +from diffoscope.comparators.elf import ElfFile, StaticLibFile │ │ │ +from diffoscope.comparators.fonts import TtfFile │ │ │ +from diffoscope.comparators.gettext import MoFile │ │ │ +from diffoscope.comparators.gzip import GzipFile │ │ │ +from diffoscope.comparators.haskell import HiFile │ │ │ +from diffoscope.comparators.ipk import IpkFile │ │ │ +from diffoscope.comparators.iso9660 import Iso9660File │ │ │ +from diffoscope.comparators.mono import MonoExeFile │ │ │ +from diffoscope.comparators.pdf import PdfFile │ │ │ +from diffoscope.comparators.png import PngFile │ │ │ +try: │ │ │ + from diffoscope.comparators.rpm import RpmFile │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/pdf.py │ │ │ @@ -0,0 +1,54 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import Command │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class Pdftotext(Command): │ │ │ + @tool_required('pdftotext') │ │ │ + def cmdline(self): │ │ │ + return ['pdftotext', self.path, '-'] │ │ │ + │ │ │ + │ │ │ +class Pdftk(Command): │ │ │ + @tool_required('pdftk') │ │ │ + def cmdline(self): │ │ │ + return ['pdftk', self.path, 'output', '-', 'uncompress'] │ │ │ + │ │ │ + def filter(self, line): │ │ │ + return line.decode('latin-1').encode('utf-8') │ │ │ + │ │ │ + │ │ │ +class PdfFile(File): │ │ │ + RE_FILE_TYPE = re.compile(r'^PDF document\b') │ │ │ + │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return PdfFile.RE_FILE_TYPE.match(file.magic_file_type) │ │ │ + │ │ │ + @needs_content │ │ │ + def compare_details(self, other, source=None): │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/tar.py │ │ │ @@ -0,0 +1,156 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from contextlib import contextmanager │ │ │ +import os.path │ │ │ +import re │ │ │ +import stat │ │ │ +from StringIO import StringIO │ │ │ +import sys │ │ │ +import tarfile │ │ │ +from diffoscope import logger │ │ │ +from diffoscope.difference import Difference │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.device import Device │ │ │ +from diffoscope.comparators.directory import Directory │ │ │ +from diffoscope.comparators.symlink import Symlink │ │ │ +from diffoscope.comparators.utils import Archive, ArchiveMember │ │ │ + │ │ │ +class TarMember(ArchiveMember): │ │ │ + def is_directory(self): │ │ │ + return False │ │ │ + │ │ │ + def is_symlink(self): │ │ │ + return False │ │ │ + │ │ │ + def is_device(self): │ │ │ + return False │ │ │ + │ │ │ + │ │ │ +class TarDirectory(Directory, TarMember): │ │ │ + def __init__(self, archive, member_name): │ │ │ + ArchiveMember.__init__(self, archive, member_name) │ │ │ + │ │ │ + def compare(self, other, source=None): │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/directory.py │ │ │ @@ -0,0 +1,177 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from contextlib import contextmanager │ │ │ +import os.path │ │ │ +import re │ │ │ +import subprocess │ │ │ +from diffoscope import logger, tool_required, RequiredToolNotFound │ │ │ +from diffoscope.difference import Difference │ │ │ +import diffoscope.comparators │ │ │ +from diffoscope.comparators.binary import FilesystemFile │ │ │ +from diffoscope.comparators.utils import Container, Command │ │ │ + │ │ │ + │ │ │ +class FindAll(Command): │ │ │ + @tool_required('find') │ │ │ + def cmdline(self): │ │ │ + return ['find', self.path, '-printf', '%P\n'] │ │ │ + │ │ │ + │ │ │ +class Stat(Command): │ │ │ + @tool_required('stat') │ │ │ + def cmdline(self): │ │ │ + return ['stat', self.path] │ │ │ + │ │ │ + FILE_RE = re.compile(r'^\s*File:.*$') │ │ │ + DEVICE_RE = re.compile(r'Device: [0-9a-f]+h/[0-9]+d') │ │ │ + INODE_RE = re.compile(r'Inode: [0-9]+') │ │ │ + ACCESS_TIME_RE = re.compile(r'^Access: [0-9]{4}-[0-9]{2}-[0-9]{2}.*$') │ │ │ + │ │ │ + def filter(self, line): │ │ │ + line = Stat.FILE_RE.sub('', line) │ │ │ + line = Stat.DEVICE_RE.sub('', line) │ │ │ + line = Stat.INODE_RE.sub('', line) │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/xz.py │ │ │ @@ -0,0 +1,69 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import os.path │ │ │ +import re │ │ │ +import subprocess │ │ │ +import diffoscope.comparators │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import Archive, get_compressed_content_name, NO_COMMENT │ │ │ +from diffoscope import logger, tool_required │ │ │ + │ │ │ + │ │ │ +class XzContainer(Archive): │ │ │ + @property │ │ │ + def path(self): │ │ │ + return self._path │ │ │ + │ │ │ + def open_archive(self, path): │ │ │ + self._path = path │ │ │ + return self │ │ │ + │ │ │ + def close_archive(self): │ │ │ + self._path = None │ │ │ + │ │ │ + def get_members(self): │ │ │ + return {'xz-content': self.get_member(self.get_member_names()[0])} │ │ │ + │ │ │ + def get_member_names(self): │ │ │ + return [get_compressed_content_name(self.path, '.xz')] │ │ │ + │ │ │ + @tool_required('xz') │ │ │ + def extract(self, member_name, dest_dir): │ │ │ + dest_path = os.path.join(dest_dir, member_name) │ │ │ + logger.debug('xz extracting to %s', dest_path) │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/elf.py │ │ │ @@ -0,0 +1,106 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import os.path │ │ │ +import re │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import get_ar_content, Command │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class Readelf(Command): │ │ │ + def __init__(self, *args, **kwargs): │ │ │ + super(Readelf, self).__init__(*args, **kwargs) │ │ │ + # we don't care about the name of the archive │ │ │ + self._archive_re = re.compile(r'^File: %s\(' % re.escape(self.path)) │ │ │ + │ │ │ + @tool_required('readelf') │ │ │ + def cmdline(self): │ │ │ + return ['readelf'] + self.readelf_options() + [self.path] │ │ │ + │ │ │ + def readelf_options(self): │ │ │ + return [] │ │ │ + │ │ │ + def filter(self, line): │ │ │ + # we don't care about the name of the archive │ │ │ + line = self._archive_re.sub('File: lib.a(', line) │ │ │ + # the full path can appear in the output, we need to remove it │ │ │ + return line.replace(self.path, os.path.basename(self.path)) │ │ │ + │ │ │ +class ReadelfAll(Readelf): │ │ │ + def readelf_options(self): │ │ │ + return ['-all'] │ │ │ + │ │ │ --- /dev/null │ │ ├── +++ ./usr/share/doc/diffoscope/README │ │ │ @@ -0,0 +1,72 @@ │ │ │ +diffoscope │ │ │ +========== │ │ │ + │ │ │ +diffoscope will try to get to the bottom of what makes files or │ │ │ +directories different. It will recursively unpack archives of many kinds │ │ │ +and transform various binary formats into more human readable form to │ │ │ +compare them. It can compare two tarballs, ISO images, or PDF just as │ │ │ +easily. │ │ │ + │ │ │ +It can be scripted through error codes, and a report can be produced │ │ │ +with the detected differences. The report can be text or HTML. │ │ │ +When no type of report has been selected, diffoscope defaults │ │ │ +to write a text report on the standard output. │ │ │ + │ │ │ +diffoscope will exit with 0 if there's no differences and 1 if there │ │ │ +are. An HTML report can be produced with the detected differences. │ │ │ + │ │ │ +diffoscope is developed as part of the “reproducible builds” Debian │ │ │ +project: │ │ │ +It is meant to be able to quickly understand why two builds of the same │ │ │ +package produce different outputs. diffoscope was previously named │ │ │ +debbindiff. │ │ │ + │ │ │ +Example │ │ │ +------- │ │ │ + │ │ │ + $ bin/diffoscope.py --html output.html build1.changes build2.changes │ │ │ + │ │ │ +This will compare `build1.changes` and `build2.changes` and create │ │ │ +`output.html` if there are differences between the two files. │ │ │ + │ │ │ +External dependencies │ │ │ +--------------------- │ │ │ + │ │ │ +Required Python modules: │ │ │ + │ │ │ + * Available on PyPI: libarchive-c, python-debian. │ │ │ + * Magic-file-extension is built from file: │ │ │ + http://www.darwinsys.com/file/ │ │ │ + Available on Debian and Fedora as python-magic. │ │ │ + * rpm-python is built from rpm: │ │ │ + http://rpm.org/ │ │ │ + Available on Debian and Fedora as python-rpm. │ │ │ + * tlsh is available at: │ │ │ + https://github.com/trendmicro/tlsh │ │ │ + │ │ │ +The various comparators rely on external commands being available. To │ │ │ +get a list of them, please run: │ │ │ + │ │ │ + $ bin/diffoscope.py --list-tools │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/device.py │ │ │ @@ -0,0 +1,55 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from contextlib import contextmanager │ │ │ +import os │ │ │ +import tempfile │ │ │ +from diffoscope.comparators.binary import File, FilesystemFile, needs_content │ │ │ +from diffoscope.comparators.utils import format_device │ │ │ +from diffoscope.difference import Difference │ │ │ +from diffoscope import logger │ │ │ + │ │ │ + │ │ │ +class Device(File): │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return file.is_device() │ │ │ + │ │ │ + def get_device(self): │ │ │ + assert isinstance(self, FilesystemFile) │ │ │ + st = os.lstat(self.name) │ │ │ + return st.st_mode, os.major(st.st_rdev), os.minor(st.st_rdev) │ │ │ + │ │ │ + def has_same_content_as(self, other): │ │ │ + return self.get_device() == other.get_device() │ │ │ + │ │ │ + @contextmanager │ │ │ + def get_content(self): │ │ │ + with tempfile.NamedTemporaryFile(suffix='diffoscope') as f: │ │ │ + f.write(format_device(*self.get_device())) │ │ │ + f.flush() │ │ │ + self._path = f.name │ │ │ + yield │ │ │ + self._path = None │ │ │ + │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/sqlite.py │ │ │ @@ -0,0 +1,40 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import Command │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class Sqlite3Dump(Command): │ │ │ + @tool_required('sqlite3') │ │ │ + def cmdline(self): │ │ │ + return ['sqlite3', self.path, '.dump'] │ │ │ + │ │ │ + │ │ │ +class Sqlite3Database(File): │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return file.magic_file_type == 'SQLite 3.x database' │ │ │ + │ │ │ + @needs_content │ │ │ + def compare_details(self, other, source=None): │ │ │ + return [Difference.from_command(Sqlite3Dump, self.path, other.path)] │ │ │ + │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope-33.egg-info/top_level.txt │ │ │ @@ -0,0 +1 @@ │ │ │ +diffoscope │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/config.py │ │ │ @@ -0,0 +1,80 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Reiner Herrmann │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ + │ │ │ +# From http://stackoverflow.com/a/7864317 │ │ │ +# Credits to kylealanhale │ │ │ +class classproperty(property): │ │ │ + def __get__(self, cls, owner): │ │ │ + return classmethod(self.fget).__get__(None, owner)() │ │ │ + │ │ │ + │ │ │ +class Config(object): │ │ │ + def __init__(self): │ │ │ + self._max_diff_block_lines = 50 │ │ │ + self._max_diff_input_lines = 100000 # GNU diff cannot process arbitrary large files :( │ │ │ + self._max_report_size = 2000 * 2 ** 10 # 2000 kB │ │ │ + self._fuzzy_threshold = 60 │ │ │ + self._new_file = False │ │ │ + │ │ │ + @classproperty │ │ │ + def general(cls): │ │ │ + if not hasattr(cls, '_general_config'): │ │ │ + cls._general_config = Config() │ │ │ + return cls._general_config │ │ │ + │ │ │ + @property │ │ │ + def max_diff_block_lines(self): │ │ │ + return self._max_diff_block_lines │ │ │ + │ │ │ + @max_diff_block_lines.setter │ │ │ + def max_diff_block_lines(self, value): │ │ │ + self._max_diff_block_lines = value │ │ │ + │ │ │ + @property │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/java.py │ │ │ @@ -0,0 +1,53 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Reiner Herrmann │ │ │ +# 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import os.path │ │ │ +import re │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import Command │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class Javap(Command): │ │ │ + def __init__(self, path, *args, **kwargs): │ │ │ + super(Javap, self).__init__(path, *args, **kwargs) │ │ │ + self.real_path = os.path.realpath(path) │ │ │ + │ │ │ + @tool_required('javap') │ │ │ + def cmdline(self): │ │ │ + return ['javap', '-verbose', '-constants', '-s', '-l', '-private', self.path] │ │ │ + │ │ │ + def filter(self, line): │ │ │ + if re.match(r'^(Classfile %s$| Last modified | MD5 checksum )' % re.escape(self.real_path), line): │ │ │ + return '' │ │ │ + return line │ │ │ + │ │ │ + │ │ │ +class ClassFile(File): │ │ │ + RE_FILE_TYPE = re.compile(r'^compiled Java class data\b') │ │ │ + │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return ClassFile.RE_FILE_TYPE.match(file.magic_file_type) │ │ │ + │ │ │ --- /dev/null │ │ ├── +++ ./usr/share/doc/diffoscope/changelog.gz │ │ │ │ --- /dev/null │ │ │ ├── +++ changelog │ │ │ │ @@ -0,0 +1,463 @@ │ │ │ │ +diffoscope (33) unstable; urgency=medium │ │ │ │ + │ │ │ │ + * Fix path to diffoscope used to generate Recommends. (Closes: #797978) │ │ │ │ + │ │ │ │ + -- Jérémy Bobbio Fri, 04 Sep 2015 10:20:45 +0200 │ │ │ │ + │ │ │ │ +diffoscope (32) unstable; urgency=medium │ │ │ │ + │ │ │ │ + [ Jérémy Bobbio ] │ │ │ │ + * Add `--fuzzy-threshold` option. This allows one to specify the TLSH score │ │ │ │ + used as cut-off for fuzzy matching. Specifying 0 will disable │ │ │ │ + fuzzy-matching entirely. Thanks Jakub Wilk for prompting me to implement │ │ │ │ + this. (Closes: #797557) │ │ │ │ + * Add `--new-file` to treat absent files as empty. Thanks Jakub Wilk for the │ │ │ │ + suggestion. (Closes: #797560) │ │ │ │ + * Enable comparisons of symlinks and devices given on the command line. │ │ │ │ + (Closes: #796262) │ │ │ │ + * Fix comparisons of device files. (Closes: #796288) │ │ │ │ + * Perform comparisons of file metadata in directories using original path. │ │ │ │ + (Closes: #796202) │ │ │ │ + * Display default values in `--help`. │ │ │ │ + * Stop unpacking Tar directories. Thanks h01ger for the report. │ │ │ │ + (Closes: #797164) │ │ │ │ + * Make directory comparison work again. │ │ │ │ + * Fix and document required Python modules. Thanks plntyk for the feedback on │ │ │ │ + IRC. │ │ │ │ + * Pass `--force-local`. to cpio. We don't want filenames with ':' to trigger │ │ │ │ + remote copies. │ │ │ │ + * Mark that get_ar_content() as requiring the 'ar' executable. │ │ │ │ + * Rework how diffoscope main() is run. Thanks Yaroslav Halchenko for the │ │ │ │ + report and suggestions. (Closes: #796196) │ │ │ │ + * Assume UTF-8 output if stdin does not advertise any encoding. │ │ │ │ + * Give proper error message when run on non-existing files. │ │ │ │ + * Output differences in directory test to ease debugging. │ │ │ │ + * Update manpage. Thanks Jakub Wilk for reporting the issue. │ │ │ │ + (Closes: #797561) │ │ │ │ + * Properly handle SIGTERM and do cleanup temp files. Thanks Mattia Rizzolo │ │ │ │ + for reporting this and how he did work around this deficiency. │ │ │ │ + (Closes: #788568) │ │ │ │ + * Fix handling of GHC .hi file (Closes: #796039) │ │ │ │ + * Add a test on how Ctrl+C is handled. │ │ │ │ + * Minor code improvements. │ │ │ │ + │ │ │ │ + [ Chris Lamb ] │ │ │ │ + * Correct "comment" on Device comparator. │ │ │ │ + │ │ │ │ + -- Jérémy Bobbio Thu, 03 Sep 2015 20:00:45 +0200 │ │ │ │ + │ │ │ │ +diffoscope (31) unstable; urgency=low │ │ │ │ + │ │ │ ╵ │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/bzip2.py │ │ │ @@ -0,0 +1,69 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import os.path │ │ │ +import re │ │ │ +import subprocess │ │ │ +import diffoscope.comparators │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import Archive, get_compressed_content_name, NO_COMMENT │ │ │ +from diffoscope import logger, tool_required │ │ │ + │ │ │ + │ │ │ +class Bzip2Container(Archive): │ │ │ + @property │ │ │ + def path(self): │ │ │ + return self._path │ │ │ + │ │ │ + def open_archive(self, path): │ │ │ + self._path = path │ │ │ + return self │ │ │ + │ │ │ + def close_archive(self): │ │ │ + self._path = None │ │ │ + │ │ │ + def get_members(self): │ │ │ + return {'bzip2-content': self.get_member(self.get_member_names()[0])} │ │ │ + │ │ │ + def get_member_names(self): │ │ │ + return [get_compressed_content_name(self.path, '.bz2')] │ │ │ + │ │ │ + @tool_required('bzip2') │ │ │ + def extract(self, member_name, dest_dir): │ │ │ + dest_path = os.path.join(dest_dir, member_name) │ │ │ + logger.debug('bzip2 extracting to %s', dest_path) │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/gettext.py │ │ │ @@ -0,0 +1,68 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2014-2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +from StringIO import StringIO │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import Command │ │ │ +from diffoscope.difference import Difference │ │ │ +from diffoscope import logger │ │ │ + │ │ │ + │ │ │ +class Msgunfmt(Command): │ │ │ + CHARSET_RE = re.compile(r'^"Content-Type: [^;]+; charset=([^\\]+)\\n"$') │ │ │ + │ │ │ + def __init__(self, *args, **kwargs): │ │ │ + super(Msgunfmt, self).__init__(*args, **kwargs) │ │ │ + self._header = StringIO() │ │ │ + self._encoding = None │ │ │ + │ │ │ + @tool_required('msgunfmt') │ │ │ + def cmdline(self): │ │ │ + return ['msgunfmt', self.path] │ │ │ + │ │ │ + def filter(self, line): │ │ │ + if not self._encoding: │ │ │ + self._header.write(line) │ │ │ + if line == '\n': │ │ │ + logger.debug("unable to determine PO encoding, let's hope it's utf-8") │ │ │ + self._encoding = 'utf-8' │ │ │ + return self._header.getvalue() │ │ │ + found = Msgunfmt.CHARSET_RE.match(line) │ │ │ + if found: │ │ │ + self._encoding = found.group(1).lower() │ │ │ --- /dev/null │ │ ├── +++ ./usr/lib/python2.7/dist-packages/diffoscope/comparators/mono.py │ │ │ @@ -0,0 +1,43 @@ │ │ │ +# -*- coding: utf-8 -*- │ │ │ +# │ │ │ +# diffoscope: in-depth comparison of files, archives, and directories │ │ │ +# │ │ │ +# Copyright © 2015 Daniel Kahn Gillmor │ │ │ +# 2015 Jérémy Bobbio │ │ │ +# │ │ │ +# diffoscope is free software: you can redistribute it and/or modify │ │ │ +# it under the terms of the GNU General Public License as published by │ │ │ +# the Free Software Foundation, either version 3 of the License, or │ │ │ +# (at your option) any later version. │ │ │ +# │ │ │ +# diffoscope 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 diffoscope. If not, see . │ │ │ + │ │ │ +import re │ │ │ +from diffoscope import tool_required │ │ │ +from diffoscope.comparators.binary import File, needs_content │ │ │ +from diffoscope.comparators.utils import Command │ │ │ +from diffoscope.difference import Difference │ │ │ + │ │ │ + │ │ │ +class Pedump(Command): │ │ │ + @tool_required('pedump') │ │ │ + def cmdline(self): │ │ │ + return ['pedump', self.path] │ │ │ + │ │ │ + │ │ │ +class MonoExeFile(File): │ │ │ + RE_FILE_TYPE = re.compile(r'\bPE[0-9]+\b.*\bMono\b') │ │ │ + │ │ │ + @staticmethod │ │ │ + def recognizes(file): │ │ │ + return MonoExeFile.RE_FILE_TYPE.search(file.magic_file_type) │ │ │ + │ │ │ + @needs_content │ │ │ + def compare_details(self, other, source=None): │ │ │ + return [Difference.from_command(Pedump, self.path, other.path)] │ │ ╵ │ ╵ │ --- /dev/null ├── +++ debian-binary │ @@ -0,0 +1 @@ │ +2.0 ╵