[diffoscope] 01/01: Add comparator for Fontconfig cache files.

Chris Lamb chris at chris-lamb.co.uk
Fri Jun 2 19:46:13 CEST 2017


This is an automated email from the git hooks/post-receive script.

lamby pushed a commit to branch experimental
in repository diffoscope.

commit df8360b1230c0c019cc8f0faa0f50cb5b1f7c265
Author: Chris Lamb <lamby at debian.org>
Date:   Fri Jun 2 16:40:15 2017 +0100

    Add comparator for Fontconfig cache files.
    
    This was to diagnose issue #12567 in Tails [0].
    
      [0] https://labs.riseup.net/code/issues/12567
    
    Signed-off-by: Chris Lamb <lamby at debian.org>
---
 diffoscope/comparators/__init__.py   |   1 +
 diffoscope/comparators/fontconfig.py |  91 +++++++++++++++++++++++++++++++++++
 tests/comparators/test_fontconfig.py |  46 ++++++++++++++++++
 tests/data/fontconfig_expected_diff  |  18 +++++++
 tests/data/test1-le64.cache-4        | Bin 0 -> 14208 bytes
 tests/data/test2-le64.cache-4        | Bin 0 -> 15384 bytes
 6 files changed, 156 insertions(+)

diff --git a/diffoscope/comparators/__init__.py b/diffoscope/comparators/__init__.py
index 38d9e58..d22aa79 100644
--- a/diffoscope/comparators/__init__.py
+++ b/diffoscope/comparators/__init__.py
@@ -52,6 +52,7 @@ class ComparatorManager(object):
         ('llvm.LlvmBitCodeFile',),
         ('sqlite.Sqlite3Database',),
         ('fonts.TtfFile',),
+        ('fontconfig.FontconfigCacheFile',),
         ('gettext.MoFile',),
         ('ipk.IpkFile',),
         ('rust.RustObjectFile',),
diff --git a/diffoscope/comparators/fontconfig.py b/diffoscope/comparators/fontconfig.py
new file mode 100644
index 0000000..dc2b981
--- /dev/null
+++ b/diffoscope/comparators/fontconfig.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+#
+# diffoscope: in-depth comparison of files, archives, and directories
+#
+# Copyright © 2017 Chris Lamb <lamby at debian.org>
+#
+# 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 <https://www.gnu.org/licenses/>.
+
+import re
+import struct
+
+from diffoscope.difference import Difference
+
+from .utils.file import File
+from .utils.command import Command
+
+
+class FontconfigCacheFile(File):
+    MAGIC = struct.pack('<H', 0xFC04)
+    RE_FILE_EXTENSION = re.compile(r'\-le64\.cache-4$')
+
+    @staticmethod
+    def recognizes(file):
+        if not FontconfigCacheFile.RE_FILE_EXTENSION.search(file.name):
+            return False
+
+        with open(file.path, 'rb') as f:
+            return f.read(len(FontconfigCacheFile.MAGIC)) == \
+                FontconfigCacheFile.MAGIC
+
+    def compare_details(self, other, source=None):
+        return [Difference.from_text(
+            describe_cache_file(self.path),
+            describe_cache_file(other.path),
+            self.path,
+            other.path,
+        )]
+
+
+def describe_cache_file(filename):
+    fmt = '<IIQQQQQQQ'
+    fields = (
+        'magic', 'version', 'size', 'dir', 'dirs', 'dirs_count', 'set',
+        'checksum', 'checksum_nano',
+    )
+
+    with open(filename, 'rb') as f:
+        data = struct.unpack(fmt, f.read(struct.calcsize(fmt)))
+        kwargs = {x: y for x, y in zip(fields, data)}
+
+        kwargs['dir_name'] = read_null_terminated_string(f, kwargs['dir'])
+
+    return """
+struct FcCache {{
+    unsigned int    magic = 0x{magic:08X};  /* FC_CACHE_MAGIC_MMAP or FC_CACHE_ALLOC */
+    int             version = {version};  /* FC_CACHE_VERSION_NUMBER */
+    intptr_t        size = {size};  /* size of file */
+    intptr_t        dir = 0x{dir};  /* offset to dir name ("{dir_name}") */
+    intptr_t        dirs = 0x{dirs:08X};  /* offset to subdirs */
+    int             dirs_count = {dirs_count};  /* number of subdir strings */
+    intptr_t        set = 0x{set:08X};  /* offset to font set */
+    int             checksum = {checksum};  /* checksum of directory state */
+    int64_t         checksum_nano = {checksum_nano};  /* checksum of directory state */
+}};
+""".format(**kwargs)
+
+
+def read_null_terminated_string(fileobj, offset=None):
+    result = ''
+
+    if offset is not None:
+        fileobj.seek(offset)
+
+    while True:
+        x = fileobj.read(1).decode('ascii')
+        if x in ('', '\0'):
+            break
+        result += x
+
+    return result
diff --git a/tests/comparators/test_fontconfig.py b/tests/comparators/test_fontconfig.py
new file mode 100644
index 0000000..229e69f
--- /dev/null
+++ b/tests/comparators/test_fontconfig.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+#
+# diffoscope: in-depth comparison of files, archives, and directories
+#
+# Copyright © 2017 Chris Lamb <lamby at debian.org>
+#
+# 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 <https://www.gnu.org/licenses/>.
+
+import pytest
+
+from diffoscope.comparators.fontconfig import FontconfigCacheFile
+
+from ..utils.data import load_fixture, get_data
+
+cache1 = load_fixture('test1-le64.cache-4')
+cache2 = load_fixture('test2-le64.cache-4')
+
+
+def test_identification(cache1):
+    assert isinstance(cache1, FontconfigCacheFile)
+
+
+def test_no_differences(cache1):
+    difference = cache1.compare(cache1)
+    assert difference is None
+
+
+ at pytest.fixture
+def differences(cache1, cache2):
+    return cache1.compare(cache2).details
+
+
+def test_diff(differences):
+    expected_diff = get_data('fontconfig_expected_diff')
+    assert differences[0].unified_diff == expected_diff
diff --git a/tests/data/fontconfig_expected_diff b/tests/data/fontconfig_expected_diff
new file mode 100644
index 0000000..86001a3
--- /dev/null
+++ b/tests/data/fontconfig_expected_diff
@@ -0,0 +1,18 @@
+@@ -1,12 +1,12 @@
+ 
+ struct FcCache {
+     unsigned int    magic = 0xFC02FC04;  /* FC_CACHE_MAGIC_MMAP or FC_CACHE_ALLOC */
+     int             version = 4;  /* FC_CACHE_VERSION_NUMBER */
+-    intptr_t        size = 14208;  /* size of file */
+-    intptr_t        dir = 0x56;  /* offset to dir name ("/usr/share/fonts/type1/texlive-fonts-recommended") */
+-    intptr_t        dirs = 0x00000070;  /* offset to subdirs */
++    intptr_t        size = 15384;  /* size of file */
++    intptr_t        dir = 0x56;  /* offset to dir name ("/usr/share/fonts/truetype/dejavu") */
++    intptr_t        dirs = 0x00000060;  /* offset to subdirs */
+     int             dirs_count = 0;  /* number of subdir strings */
+-    intptr_t        set = 0x00000070;  /* offset to font set */
+-    int             checksum = 996;  /* checksum of directory state */
++    intptr_t        set = 0x00000060;  /* offset to font set */
++    int             checksum = 1496417874;  /* checksum of directory state */
+     int64_t         checksum_nano = 7018986666877744431;  /* checksum of directory state */
+ };
diff --git a/tests/data/test1-le64.cache-4 b/tests/data/test1-le64.cache-4
new file mode 100644
index 0000000..1846dc4
Binary files /dev/null and b/tests/data/test1-le64.cache-4 differ
diff --git a/tests/data/test2-le64.cache-4 b/tests/data/test2-le64.cache-4
new file mode 100644
index 0000000..4ef0f15
Binary files /dev/null and b/tests/data/test2-le64.cache-4 differ

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/diffoscope.git


More information about the diffoscope mailing list