cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

maintainers_include.py (7441B)


      1#!/usr/bin/env python
      2# SPDX-License-Identifier: GPL-2.0
      3# -*- coding: utf-8; mode: python -*-
      4# pylint: disable=R0903, C0330, R0914, R0912, E0401
      5
      6u"""
      7    maintainers-include
      8    ~~~~~~~~~~~~~~~~~~~
      9
     10    Implementation of the ``maintainers-include`` reST-directive.
     11
     12    :copyright:  Copyright (C) 2019  Kees Cook <keescook@chromium.org>
     13    :license:    GPL Version 2, June 1991 see linux/COPYING for details.
     14
     15    The ``maintainers-include`` reST-directive performs extensive parsing
     16    specific to the Linux kernel's standard "MAINTAINERS" file, in an
     17    effort to avoid needing to heavily mark up the original plain text.
     18"""
     19
     20import sys
     21import re
     22import os.path
     23
     24from docutils import statemachine
     25from docutils.utils.error_reporting import ErrorString
     26from docutils.parsers.rst import Directive
     27from docutils.parsers.rst.directives.misc import Include
     28
     29__version__  = '1.0'
     30
     31def setup(app):
     32    app.add_directive("maintainers-include", MaintainersInclude)
     33    return dict(
     34        version = __version__,
     35        parallel_read_safe = True,
     36        parallel_write_safe = True
     37    )
     38
     39class MaintainersInclude(Include):
     40    u"""MaintainersInclude (``maintainers-include``) directive"""
     41    required_arguments = 0
     42
     43    def parse_maintainers(self, path):
     44        """Parse all the MAINTAINERS lines into ReST for human-readability"""
     45
     46        result = list()
     47        result.append(".. _maintainers:")
     48        result.append("")
     49
     50        # Poor man's state machine.
     51        descriptions = False
     52        maintainers = False
     53        subsystems = False
     54
     55        # Field letter to field name mapping.
     56        field_letter = None
     57        fields = dict()
     58
     59        prev = None
     60        field_prev = ""
     61        field_content = ""
     62
     63        for line in open(path):
     64            # Have we reached the end of the preformatted Descriptions text?
     65            if descriptions and line.startswith('Maintainers'):
     66                descriptions = False
     67                # Ensure a blank line following the last "|"-prefixed line.
     68                result.append("")
     69
     70            # Start subsystem processing? This is to skip processing the text
     71            # between the Maintainers heading and the first subsystem name.
     72            if maintainers and not subsystems:
     73                if re.search('^[A-Z0-9]', line):
     74                    subsystems = True
     75
     76            # Drop needless input whitespace.
     77            line = line.rstrip()
     78
     79            # Linkify all non-wildcard refs to ReST files in Documentation/.
     80            pat = '(Documentation/([^\s\?\*]*)\.rst)'
     81            m = re.search(pat, line)
     82            if m:
     83                # maintainers.rst is in a subdirectory, so include "../".
     84                line = re.sub(pat, ':doc:`%s <../%s>`' % (m.group(2), m.group(2)), line)
     85
     86            # Check state machine for output rendering behavior.
     87            output = None
     88            if descriptions:
     89                # Escape the escapes in preformatted text.
     90                output = "| %s" % (line.replace("\\", "\\\\"))
     91                # Look for and record field letter to field name mappings:
     92                #   R: Designated *reviewer*: FullName <address@domain>
     93                m = re.search("\s(\S):\s", line)
     94                if m:
     95                    field_letter = m.group(1)
     96                if field_letter and not field_letter in fields:
     97                    m = re.search("\*([^\*]+)\*", line)
     98                    if m:
     99                        fields[field_letter] = m.group(1)
    100            elif subsystems:
    101                # Skip empty lines: subsystem parser adds them as needed.
    102                if len(line) == 0:
    103                    continue
    104                # Subsystem fields are batched into "field_content"
    105                if line[1] != ':':
    106                    # Render a subsystem entry as:
    107                    #   SUBSYSTEM NAME
    108                    #   ~~~~~~~~~~~~~~
    109
    110                    # Flush pending field content.
    111                    output = field_content + "\n\n"
    112                    field_content = ""
    113
    114                    # Collapse whitespace in subsystem name.
    115                    heading = re.sub("\s+", " ", line)
    116                    output = output + "%s\n%s" % (heading, "~" * len(heading))
    117                    field_prev = ""
    118                else:
    119                    # Render a subsystem field as:
    120                    #   :Field: entry
    121                    #           entry...
    122                    field, details = line.split(':', 1)
    123                    details = details.strip()
    124
    125                    # Mark paths (and regexes) as literal text for improved
    126                    # readability and to escape any escapes.
    127                    if field in ['F', 'N', 'X', 'K']:
    128                        # But only if not already marked :)
    129                        if not ':doc:' in details:
    130                            details = '``%s``' % (details)
    131
    132                    # Comma separate email field continuations.
    133                    if field == field_prev and field_prev in ['M', 'R', 'L']:
    134                        field_content = field_content + ","
    135
    136                    # Do not repeat field names, so that field entries
    137                    # will be collapsed together.
    138                    if field != field_prev:
    139                        output = field_content + "\n"
    140                        field_content = ":%s:" % (fields.get(field, field))
    141                    field_content = field_content + "\n\t%s" % (details)
    142                    field_prev = field
    143            else:
    144                output = line
    145
    146            # Re-split on any added newlines in any above parsing.
    147            if output != None:
    148                for separated in output.split('\n'):
    149                    result.append(separated)
    150
    151            # Update the state machine when we find heading separators.
    152            if line.startswith('----------'):
    153                if prev.startswith('Descriptions'):
    154                    descriptions = True
    155                if prev.startswith('Maintainers'):
    156                    maintainers = True
    157
    158            # Retain previous line for state machine transitions.
    159            prev = line
    160
    161        # Flush pending field contents.
    162        if field_content != "":
    163            for separated in field_content.split('\n'):
    164                result.append(separated)
    165
    166        output = "\n".join(result)
    167        # For debugging the pre-rendered results...
    168        #print(output, file=open("/tmp/MAINTAINERS.rst", "w"))
    169
    170        self.state_machine.insert_input(
    171          statemachine.string2lines(output), path)
    172
    173    def run(self):
    174        """Include the MAINTAINERS file as part of this reST file."""
    175        if not self.state.document.settings.file_insertion_enabled:
    176            raise self.warning('"%s" directive disabled.' % self.name)
    177
    178        # Walk up source path directories to find Documentation/../
    179        path = self.state_machine.document.attributes['source']
    180        path = os.path.realpath(path)
    181        tail = path
    182        while tail != "Documentation" and tail != "":
    183            (path, tail) = os.path.split(path)
    184
    185        # Append "MAINTAINERS"
    186        path = os.path.join(path, "MAINTAINERS")
    187
    188        try:
    189            self.state.document.settings.record_dependencies.add(path)
    190            lines = self.parse_maintainers(path)
    191        except IOError as error:
    192            raise self.severe('Problems with "%s" directive path:\n%s.' %
    193                      (self.name, ErrorString(error)))
    194
    195        return []