0001
0002
0003
0004
0005 u"""
0006 kernel-include
0007 ~~~~~~~~~~~~~~
0008
0009 Implementation of the ``kernel-include`` reST-directive.
0010
0011 :copyright: Copyright (C) 2016 Markus Heiser
0012 :license: GPL Version 2, June 1991 see linux/COPYING for details.
0013
0014 The ``kernel-include`` reST-directive is a replacement for the ``include``
0015 directive. The ``kernel-include`` directive expand environment variables in
0016 the path name and allows to include files from arbitrary locations.
0017
0018 .. hint::
0019
0020 Including files from arbitrary locations (e.g. from ``/etc``) is a
0021 security risk for builders. This is why the ``include`` directive from
0022 docutils *prohibit* pathnames pointing to locations *above* the filesystem
0023 tree where the reST document with the include directive is placed.
0024
0025 Substrings of the form $name or ${name} are replaced by the value of
0026 environment variable name. Malformed variable names and references to
0027 non-existing variables are left unchanged.
0028 """
0029
0030
0031
0032
0033
0034 import os.path
0035
0036 from docutils import io, nodes, statemachine
0037 from docutils.utils.error_reporting import SafeString, ErrorString
0038 from docutils.parsers.rst import directives
0039 from docutils.parsers.rst.directives.body import CodeBlock, NumberLines
0040 from docutils.parsers.rst.directives.misc import Include
0041
0042 __version__ = '1.0'
0043
0044
0045 def setup(app):
0046
0047
0048 app.add_directive("kernel-include", KernelInclude)
0049 return dict(
0050 version = __version__,
0051 parallel_read_safe = True,
0052 parallel_write_safe = True
0053 )
0054
0055
0056 class KernelInclude(Include):
0057
0058
0059 u"""KernelInclude (``kernel-include``) directive"""
0060
0061 def run(self):
0062 env = self.state.document.settings.env
0063 path = os.path.realpath(
0064 os.path.expandvars(self.arguments[0]))
0065
0066
0067 if path.startswith(os.sep + "etc"):
0068 raise self.severe(
0069 'Problems with "%s" directive, prohibited path: %s'
0070 % (self.name, path))
0071
0072 self.arguments[0] = path
0073
0074 env.note_dependency(os.path.abspath(path))
0075
0076
0077 return self._run()
0078
0079 def _run(self):
0080 """Include a file as part of the content of this reST file."""
0081
0082
0083
0084
0085
0086
0087 if not self.state.document.settings.file_insertion_enabled:
0088 raise self.warning('"%s" directive disabled.' % self.name)
0089 source = self.state_machine.input_lines.source(
0090 self.lineno - self.state_machine.input_offset - 1)
0091 source_dir = os.path.dirname(os.path.abspath(source))
0092 path = directives.path(self.arguments[0])
0093 if path.startswith('<') and path.endswith('>'):
0094 path = os.path.join(self.standard_include_path, path[1:-1])
0095 path = os.path.normpath(os.path.join(source_dir, path))
0096
0097
0098
0099
0100 path = nodes.reprunicode(path)
0101 encoding = self.options.get(
0102 'encoding', self.state.document.settings.input_encoding)
0103 e_handler=self.state.document.settings.input_encoding_error_handler
0104 tab_width = self.options.get(
0105 'tab-width', self.state.document.settings.tab_width)
0106 try:
0107 self.state.document.settings.record_dependencies.add(path)
0108 include_file = io.FileInput(source_path=path,
0109 encoding=encoding,
0110 error_handler=e_handler)
0111 except UnicodeEncodeError as error:
0112 raise self.severe('Problems with "%s" directive path:\n'
0113 'Cannot encode input file path "%s" '
0114 '(wrong locale?).' %
0115 (self.name, SafeString(path)))
0116 except IOError as error:
0117 raise self.severe('Problems with "%s" directive path:\n%s.' %
0118 (self.name, ErrorString(error)))
0119 startline = self.options.get('start-line', None)
0120 endline = self.options.get('end-line', None)
0121 try:
0122 if startline or (endline is not None):
0123 lines = include_file.readlines()
0124 rawtext = ''.join(lines[startline:endline])
0125 else:
0126 rawtext = include_file.read()
0127 except UnicodeError as error:
0128 raise self.severe('Problem with "%s" directive:\n%s' %
0129 (self.name, ErrorString(error)))
0130
0131
0132 after_text = self.options.get('start-after', None)
0133 if after_text:
0134
0135 after_index = rawtext.find(after_text)
0136 if after_index < 0:
0137 raise self.severe('Problem with "start-after" option of "%s" '
0138 'directive:\nText not found.' % self.name)
0139 rawtext = rawtext[after_index + len(after_text):]
0140 before_text = self.options.get('end-before', None)
0141 if before_text:
0142
0143 before_index = rawtext.find(before_text)
0144 if before_index < 0:
0145 raise self.severe('Problem with "end-before" option of "%s" '
0146 'directive:\nText not found.' % self.name)
0147 rawtext = rawtext[:before_index]
0148
0149 include_lines = statemachine.string2lines(rawtext, tab_width,
0150 convert_whitespace=True)
0151 if 'literal' in self.options:
0152
0153 if tab_width >= 0:
0154 text = rawtext.expandtabs(tab_width)
0155 else:
0156 text = rawtext
0157 literal_block = nodes.literal_block(rawtext, source=path,
0158 classes=self.options.get('class', []))
0159 literal_block.line = 1
0160 self.add_name(literal_block)
0161 if 'number-lines' in self.options:
0162 try:
0163 startline = int(self.options['number-lines'] or 1)
0164 except ValueError:
0165 raise self.error(':number-lines: with non-integer '
0166 'start value')
0167 endline = startline + len(include_lines)
0168 if text.endswith('\n'):
0169 text = text[:-1]
0170 tokens = NumberLines([([], text)], startline, endline)
0171 for classes, value in tokens:
0172 if classes:
0173 literal_block += nodes.inline(value, value,
0174 classes=classes)
0175 else:
0176 literal_block += nodes.Text(value, value)
0177 else:
0178 literal_block += nodes.Text(text, text)
0179 return [literal_block]
0180 if 'code' in self.options:
0181 self.options['source'] = path
0182 codeblock = CodeBlock(self.name,
0183 [self.options.pop('code')],
0184 self.options,
0185 include_lines,
0186 self.lineno,
0187 self.content_offset,
0188 self.block_text,
0189 self.state,
0190 self.state_machine)
0191 return codeblock.run()
0192 self.state_machine.insert_input(include_lines, path)
0193 return []