This file is indexed.

/usr/lib/python3/dist-packages/diffoscope/comparators/haskell.py is in diffoscope 93ubuntu1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# -*- coding: utf-8 -*-
#
# diffoscope: in-depth comparison of files, archives, and directories
#
# Copyright © 2014-2015 Jérémy Bobbio <lunar@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
import logging
import platform
import subprocess

from diffoscope.tools import tool_required
from diffoscope.profiling import profile
from diffoscope.difference import Difference

from .utils.file import File
from .utils.command import Command

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

logger = logging.getLogger(__name__)


class ShowIface(Command):
    @tool_required('ghc')
    def cmdline(self):
        return ['ghc', '--show-iface', self.path]


class HiFile(File):
    """
    Here is how an example .hi file starts:

    % hexdump -C tests/data/test1.hi | head -n 1
    00000000  01 fa ce 64 00 00 00 00  00 00 00 00 04 00 00 00  |...d............|
              ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ ~~ ~~~~~~~~~~
                HI_MAGIC    zero padding (used to  ↑↑  int('7')
                             be a field here)      ||
                                                   ||
                           ·~~~~~~~~~~~~~~~~~~~~~~~||
                           | version string length ||
                           ·~~~~~~~~~~~~~~~~~~~~~~~~~

    00000010  37 00 00 00 31 00 00 00  30 00 00 00 33 00 00 00  |7...1...0...3...|
            ~~~~ ~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~
                  int('1')     int('0')    int('3')


    So the version of this file has 4 characters, and it's 7103. Note how all
    this information is stored as big endian.
    """
    DESCRIPTION = "GHC Haskell .hi files"
    RE_FILE_EXTENSION = re.compile(r'\.(p_|dyn_)?hi$')

    @classmethod
    def recognizes(cls, file):
        if not HiFile.RE_FILE_EXTENSION.search(file.name):
            return False

        if not hasattr(HiFile, 'hi_version'):
            try:
                with profile('command', 'ghc'):
                    output = subprocess.check_output(
                        ['ghc', '--numeric-version'],
                    )
            except (OSError, subprocess.CalledProcessError):
                HiFile.hi_version = None
                logger.debug("Unable to read GHC version")
            else:
                major, minor, patch = [
                    int(x) for x in output.decode('utf-8').strip().split('.')
                ]
                HiFile.hi_version = '%d%02d%d' % (major, minor, patch)
                logger.debug("Found .hi version %s", HiFile.hi_version)

        if HiFile.hi_version is None:
            return False

        with open(file.path, 'rb') as fp:
            # Read magic
            buf = fp.read(4)
            if buf != HI_MAGIC:
                logger.debug(
                    "Haskell interface magic mismatch. "
                    "Found %r instead of %r or %r",
                    buf, HI_MAGIC_32, HI_MAGIC_64,
                )
                return False

            # Skip some old descriptor thingy that has varying size
            if buf == HI_MAGIC_32:
                fp.read(4)
            elif buf == HI_MAGIC_64:
                fp.read(8)

            # Read version, which is [Char]
            buf = fp.read(1)

            # Small list optimisation - anything less than 0xff has its length
            # in a single byte; everything else is 0xff followed by the 32-bit
            # length (big-endian).
            if buf[0] == 0xff:
                buf = fp.read(4)
                length = struct.unpack('>I', buf)[0]
            else:
                length = buf[0]

            # Now read characters; each is 32-bit big-endian.
            version_found = ''.join(
                chr(struct.unpack('>I', fp.read(4))[0]) for _ in range(length)
            )

            if version_found != HiFile.hi_version:
                logger.debug(
                    "Haskell version mismatch; found %s instead of %s.",
                    version_found,
                    HiFile.hi_version,
                )
                return False

            return True

    def compare_details(self, other, source=None):
        return [Difference.from_command(ShowIface, self.path, other.path)]