diff options
Diffstat (limited to 'Documentation/sphinx')
-rw-r--r-- | Documentation/sphinx/convert_template.sed | 18 | ||||
-rw-r--r-- | Documentation/sphinx/kernel-doc.py | 141 | ||||
-rwxr-xr-x | Documentation/sphinx/kernel_include.py | 183 | ||||
-rwxr-xr-x | Documentation/sphinx/parse-headers.pl | 321 | ||||
-rw-r--r-- | Documentation/sphinx/post_convert.sed | 23 | ||||
-rw-r--r-- | Documentation/sphinx/rstFlatTable.py | 365 | ||||
-rwxr-xr-x | Documentation/sphinx/tmplcvt | 19 |
7 files changed, 1070 insertions, 0 deletions
diff --git a/Documentation/sphinx/convert_template.sed b/Documentation/sphinx/convert_template.sed new file mode 100644 index 000000000..c1503fcca --- /dev/null +++ b/Documentation/sphinx/convert_template.sed @@ -0,0 +1,18 @@ +# +# Pandoc doesn't grok <function> or <structname>, so convert them +# ahead of time. +# +# Use the following escapes to pass through pandoc: +# $bq = "`" +# $lt = "<" +# $gt = ">" +# +s%<function>\([^<(]\+\)()</function>%:c:func:$bq\1()$bq%g +s%<function>\([^<(]\+\)</function>%:c:func:$bq\1()$bq%g +s%<structname>struct *\([^<]\+\)</structname>%:c:type:$bqstruct \1 $lt\1$gt$bq%g +s%struct <structname>\([^<]\+\)</structname>%:c:type:$bqstruct \1 $lt\1$gt$bq%g +s%<structname>\([^<]\+\)</structname>%:c:type:$bqstruct \1 $lt\1$gt$bq%g +# +# Wrap docproc directives in para and code blocks. +# +s%^\(!.*\)$%<para><code>DOCPROC: \1</code></para>% diff --git a/Documentation/sphinx/kernel-doc.py b/Documentation/sphinx/kernel-doc.py new file mode 100644 index 000000000..f6920c0af --- /dev/null +++ b/Documentation/sphinx/kernel-doc.py @@ -0,0 +1,141 @@ +# coding=utf-8 +# +# Copyright © 2016 Intel Corporation +# +# 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 (including the next +# paragraph) 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. +# +# Authors: +# Jani Nikula <jani.nikula@intel.com> +# +# Please make sure this works on both python2 and python3. +# + +import os +import subprocess +import sys +import re +import glob + +from docutils import nodes, statemachine +from docutils.statemachine import ViewList +from docutils.parsers.rst import directives +from sphinx.util.compat import Directive +from sphinx.ext.autodoc import AutodocReporter + +class KernelDocDirective(Directive): + """Extract kernel-doc comments from the specified file""" + required_argument = 1 + optional_arguments = 4 + option_spec = { + 'doc': directives.unchanged_required, + 'functions': directives.unchanged_required, + 'export': directives.unchanged, + 'internal': directives.unchanged, + } + has_content = False + + def run(self): + env = self.state.document.settings.env + cmd = [env.config.kerneldoc_bin, '-rst', '-enable-lineno'] + + filename = env.config.kerneldoc_srctree + '/' + self.arguments[0] + export_file_patterns = [] + + # Tell sphinx of the dependency + env.note_dependency(os.path.abspath(filename)) + + tab_width = self.options.get('tab-width', self.state.document.settings.tab_width) + + # FIXME: make this nicer and more robust against errors + if 'export' in self.options: + cmd += ['-export'] + export_file_patterns = str(self.options.get('export')).split() + elif 'internal' in self.options: + cmd += ['-internal'] + export_file_patterns = str(self.options.get('internal')).split() + elif 'doc' in self.options: + cmd += ['-function', str(self.options.get('doc'))] + elif 'functions' in self.options: + for f in str(self.options.get('functions')).split(): + cmd += ['-function', f] + + for pattern in export_file_patterns: + for f in glob.glob(env.config.kerneldoc_srctree + '/' + pattern): + env.note_dependency(os.path.abspath(f)) + cmd += ['-export-file', f] + + cmd += [filename] + + try: + env.app.verbose('calling kernel-doc \'%s\'' % (" ".join(cmd))) + + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + out, err = p.communicate() + + # python2 needs conversion to unicode. + # python3 with universal_newlines=True returns strings. + if sys.version_info.major < 3: + out, err = unicode(out, 'utf-8'), unicode(err, 'utf-8') + + if p.returncode != 0: + sys.stderr.write(err) + + env.app.warn('kernel-doc \'%s\' failed with return code %d' % (" ".join(cmd), p.returncode)) + return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] + elif env.config.kerneldoc_verbosity > 0: + sys.stderr.write(err) + + lines = statemachine.string2lines(out, tab_width, convert_whitespace=True) + result = ViewList() + + lineoffset = 0; + line_regex = re.compile("^#define LINENO ([0-9]+)$") + for line in lines: + match = line_regex.search(line) + if match: + # sphinx counts lines from 0 + lineoffset = int(match.group(1)) - 1 + # we must eat our comments since the upset the markup + else: + result.append(line, filename, lineoffset) + lineoffset += 1 + + node = nodes.section() + buf = self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter + self.state.memo.reporter = AutodocReporter(result, self.state.memo.reporter) + self.state.memo.title_styles, self.state.memo.section_level = [], 0 + try: + self.state.nested_parse(result, 0, node, match_titles=1) + finally: + self.state.memo.title_styles, self.state.memo.section_level, self.state.memo.reporter = buf + + return node.children + + except Exception as e: # pylint: disable=W0703 + env.app.warn('kernel-doc \'%s\' processing failed with: %s' % + (" ".join(cmd), str(e))) + return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] + +def setup(app): + app.add_config_value('kerneldoc_bin', None, 'env') + app.add_config_value('kerneldoc_srctree', None, 'env') + app.add_config_value('kerneldoc_verbosity', 1, 'env') + + app.add_directive('kernel-doc', KernelDocDirective) diff --git a/Documentation/sphinx/kernel_include.py b/Documentation/sphinx/kernel_include.py new file mode 100755 index 000000000..db5738238 --- /dev/null +++ b/Documentation/sphinx/kernel_include.py @@ -0,0 +1,183 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8; mode: python -*- +# pylint: disable=R0903, C0330, R0914, R0912, E0401 + +u""" + kernel-include + ~~~~~~~~~~~~~~ + + Implementation of the ``kernel-include`` reST-directive. + + :copyright: Copyright (C) 2016 Markus Heiser + :license: GPL Version 2, June 1991 see linux/COPYING for details. + + The ``kernel-include`` reST-directive is a replacement for the ``include`` + directive. The ``kernel-include`` directive expand environment variables in + the path name and allows to include files from arbitrary locations. + + .. hint:: + + Including files from arbitrary locations (e.g. from ``/etc``) is a + security risk for builders. This is why the ``include`` directive from + docutils *prohibit* pathnames pointing to locations *above* the filesystem + tree where the reST document with the include directive is placed. + + Substrings of the form $name or ${name} are replaced by the value of + environment variable name. Malformed variable names and references to + non-existing variables are left unchanged. +""" + +# ============================================================================== +# imports +# ============================================================================== + +import os.path + +from docutils import io, nodes, statemachine +from docutils.utils.error_reporting import SafeString, ErrorString +from docutils.parsers.rst import directives +from docutils.parsers.rst.directives.body import CodeBlock, NumberLines +from docutils.parsers.rst.directives.misc import Include + +# ============================================================================== +def setup(app): +# ============================================================================== + + app.add_directive("kernel-include", KernelInclude) + +# ============================================================================== +class KernelInclude(Include): +# ============================================================================== + + u"""KernelInclude (``kernel-include``) directive""" + + def run(self): + path = os.path.realpath( + os.path.expandvars(self.arguments[0])) + + # to get a bit security back, prohibit /etc: + if path.startswith(os.sep + "etc"): + raise self.severe( + 'Problems with "%s" directive, prohibited path: %s' + % (self.name, path)) + + self.arguments[0] = path + + #return super(KernelInclude, self).run() # won't work, see HINTs in _run() + return self._run() + + def _run(self): + """Include a file as part of the content of this reST file.""" + + # HINT: I had to copy&paste the whole Include.run method. I'am not happy + # with this, but due to security reasons, the Include.run method does + # not allow absolute or relative pathnames pointing to locations *above* + # the filesystem tree where the reST document is placed. + + if not self.state.document.settings.file_insertion_enabled: + raise self.warning('"%s" directive disabled.' % self.name) + source = self.state_machine.input_lines.source( + self.lineno - self.state_machine.input_offset - 1) + source_dir = os.path.dirname(os.path.abspath(source)) + path = directives.path(self.arguments[0]) + if path.startswith('<') and path.endswith('>'): + path = os.path.join(self.standard_include_path, path[1:-1]) + path = os.path.normpath(os.path.join(source_dir, path)) + + # HINT: this is the only line I had to change / commented out: + #path = utils.relative_path(None, path) + + path = nodes.reprunicode(path) + encoding = self.options.get( + 'encoding', self.state.document.settings.input_encoding) + e_handler=self.state.document.settings.input_encoding_error_handler + tab_width = self.options.get( + 'tab-width', self.state.document.settings.tab_width) + try: + self.state.document.settings.record_dependencies.add(path) + include_file = io.FileInput(source_path=path, + encoding=encoding, + error_handler=e_handler) + except UnicodeEncodeError as error: + raise self.severe('Problems with "%s" directive path:\n' + 'Cannot encode input file path "%s" ' + '(wrong locale?).' % + (self.name, SafeString(path))) + except IOError as error: + raise self.severe('Problems with "%s" directive path:\n%s.' % + (self.name, ErrorString(error))) + startline = self.options.get('start-line', None) + endline = self.options.get('end-line', None) + try: + if startline or (endline is not None): + lines = include_file.readlines() + rawtext = ''.join(lines[startline:endline]) + else: + rawtext = include_file.read() + except UnicodeError as error: + raise self.severe('Problem with "%s" directive:\n%s' % + (self.name, ErrorString(error))) + # start-after/end-before: no restrictions on newlines in match-text, + # and no restrictions on matching inside lines vs. line boundaries + after_text = self.options.get('start-after', None) + if after_text: + # skip content in rawtext before *and incl.* a matching text + after_index = rawtext.find(after_text) + if after_index < 0: + raise self.severe('Problem with "start-after" option of "%s" ' + 'directive:\nText not found.' % self.name) + rawtext = rawtext[after_index + len(after_text):] + before_text = self.options.get('end-before', None) + if before_text: + # skip content in rawtext after *and incl.* a matching text + before_index = rawtext.find(before_text) + if before_index < 0: + raise self.severe('Problem with "end-before" option of "%s" ' + 'directive:\nText not found.' % self.name) + rawtext = rawtext[:before_index] + + include_lines = statemachine.string2lines(rawtext, tab_width, + convert_whitespace=True) + if 'literal' in self.options: + # Convert tabs to spaces, if `tab_width` is positive. + if tab_width >= 0: + text = rawtext.expandtabs(tab_width) + else: + text = rawtext + literal_block = nodes.literal_block(rawtext, source=path, + classes=self.options.get('class', [])) + literal_block.line = 1 + self.add_name(literal_block) + if 'number-lines' in self.options: + try: + startline = int(self.options['number-lines'] or 1) + except ValueError: + raise self.error(':number-lines: with non-integer ' + 'start value') + endline = startline + len(include_lines) + if text.endswith('\n'): + text = text[:-1] + tokens = NumberLines([([], text)], startline, endline) + for classes, value in tokens: + if classes: + literal_block += nodes.inline(value, value, + classes=classes) + else: + literal_block += nodes.Text(value, value) + else: + literal_block += nodes.Text(text, text) + return [literal_block] + if 'code' in self.options: + self.options['source'] = path + codeblock = CodeBlock(self.name, + [self.options.pop('code')], # arguments + self.options, + include_lines, # content + self.lineno, + self.content_offset, + self.block_text, + self.state, + self.state_machine) + return codeblock.run() + self.state_machine.insert_input(include_lines, path) + return [] diff --git a/Documentation/sphinx/parse-headers.pl b/Documentation/sphinx/parse-headers.pl new file mode 100755 index 000000000..34bd9e263 --- /dev/null +++ b/Documentation/sphinx/parse-headers.pl @@ -0,0 +1,321 @@ +#!/usr/bin/perl +use strict; +use Text::Tabs; + +# Uncomment if debug is needed +#use Data::Dumper; + +# change to 1 to generate some debug prints +my $debug = 0; + +if (scalar @ARGV < 2 || scalar @ARGV > 3) { + die "Usage:\n\t$0 <file in> <file out> [<exceptions file>]\n"; +} + +my ($file_in, $file_out, $file_exceptions) = @ARGV; + +my $data; +my %ioctls; +my %defines; +my %typedefs; +my %enums; +my %enum_symbols; +my %structs; + +# +# read the file and get identifiers +# + +my $is_enum = 0; +my $is_comment = 0; +open IN, $file_in or die "Can't open $file_in"; +while (<IN>) { + $data .= $_; + + my $ln = $_; + if (!$is_comment) { + $ln =~ s,/\*.*(\*/),,g; + + $is_comment = 1 if ($ln =~ s,/\*.*,,); + } else { + if ($ln =~ s,^(.*\*/),,) { + $is_comment = 0; + } else { + next; + } + } + + if ($is_enum && $ln =~ m/^\s*([_\w][\w\d_]+)\s*[\,=]?/) { + my $s = $1; + my $n = $1; + $n =~ tr/A-Z/a-z/; + $n =~ tr/_/-/; + + $enum_symbols{$s} = $n; + + $is_enum = 0 if ($is_enum && m/\}/); + next; + } + $is_enum = 0 if ($is_enum && m/\}/); + + if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+_IO/) { + my $s = $1; + my $n = $1; + $n =~ tr/A-Z/a-z/; + + $ioctls{$s} = $n; + next; + } + + if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+/) { + my $s = $1; + my $n = $1; + $n =~ tr/A-Z/a-z/; + $n =~ tr/_/-/; + + $defines{$s} = $n; + next; + } + + if ($ln =~ m/^\s*typedef\s+.*\s+([_\w][\w\d_]+);/) { + my $s = $1; + my $n = $1; + $n =~ tr/A-Z/a-z/; + $n =~ tr/_/-/; + + $typedefs{$s} = $n; + next; + } + if ($ln =~ m/^\s*enum\s+([_\w][\w\d_]+)\s+\{/ + || $ln =~ m/^\s*enum\s+([_\w][\w\d_]+)$/ + || $ln =~ m/^\s*typedef\s*enum\s+([_\w][\w\d_]+)\s+\{/ + || $ln =~ m/^\s*typedef\s*enum\s+([_\w][\w\d_]+)$/) { + my $s = $1; + my $n = $1; + $n =~ tr/A-Z/a-z/; + $n =~ tr/_/-/; + + $enums{$s} = $n; + + $is_enum = $1; + next; + } + if ($ln =~ m/^\s*struct\s+([_\w][\w\d_]+)\s+\{/ + || $ln =~ m/^\s*struct\s+([[_\w][\w\d_]+)$/ + || $ln =~ m/^\s*typedef\s*struct\s+([_\w][\w\d_]+)\s+\{/ + || $ln =~ m/^\s*typedef\s*struct\s+([[_\w][\w\d_]+)$/ + ) { + my $s = $1; + my $n = $1; + $n =~ tr/A-Z/a-z/; + $n =~ tr/_/-/; + + $structs{$s} = $n; + next; + } +} +close IN; + +# +# Handle multi-line typedefs +# + +my @matches = ($data =~ m/typedef\s+struct\s+\S+?\s*\{[^\}]+\}\s*(\S+)\s*\;/g, + $data =~ m/typedef\s+enum\s+\S+?\s*\{[^\}]+\}\s*(\S+)\s*\;/g,); +foreach my $m (@matches) { + my $s = $m; + my $n = $m; + $n =~ tr/A-Z/a-z/; + $n =~ tr/_/-/; + + $typedefs{$s} = $n; + next; +} + +# +# Handle exceptions, if any +# + +if ($file_exceptions) { + open IN, $file_exceptions or die "Can't read $file_exceptions"; + while (<IN>) { + next if (m/^\s*$/ || m/^\s*#/); + + # Parsers to ignore a symbol + + if (m/^ignore\s+ioctl\s+(\S+)/) { + delete $ioctls{$1} if (exists($ioctls{$1})); + next; + } + if (m/^ignore\s+define\s+(\S+)/) { + delete $defines{$1} if (exists($defines{$1})); + next; + } + if (m/^ignore\s+typedef\s+(\S+)/) { + delete $typedefs{$1} if (exists($typedefs{$1})); + next; + } + if (m/^ignore\s+enum\s+(\S+)/) { + delete $enums{$1} if (exists($enums{$1})); + next; + } + if (m/^ignore\s+struct\s+(\S+)/) { + delete $structs{$1} if (exists($structs{$1})); + next; + } + if (m/^ignore\s+symbol\s+(\S+)/) { + delete $enum_symbols{$1} if (exists($enum_symbols{$1})); + next; + } + + # Parsers to replace a symbol + + if (m/^replace\s+ioctl\s+(\S+)\s+(\S+)/) { + $ioctls{$1} = $2 if (exists($ioctls{$1})); + next; + } + if (m/^replace\s+define\s+(\S+)\s+(\S+)/) { + $defines{$1} = $2 if (exists($defines{$1})); + next; + } + if (m/^replace\s+typedef\s+(\S+)\s+(\S+)/) { + $typedefs{$1} = $2 if (exists($typedefs{$1})); + next; + } + if (m/^replace\s+enum\s+(\S+)\s+(\S+)/) { + $enums{$1} = $2 if (exists($enums{$1})); + next; + } + if (m/^replace\s+symbol\s+(\S+)\s+(\S+)/) { + $enum_symbols{$1} = $2 if (exists($enum_symbols{$1})); + next; + } + if (m/^replace\s+struct\s+(\S+)\s+(\S+)/) { + $structs{$1} = $2 if (exists($structs{$1})); + next; + } + + die "Can't parse $file_exceptions: $_"; + } +} + +if ($debug) { + print Data::Dumper->Dump([\%ioctls], [qw(*ioctls)]) if (%ioctls); + print Data::Dumper->Dump([\%typedefs], [qw(*typedefs)]) if (%typedefs); + print Data::Dumper->Dump([\%enums], [qw(*enums)]) if (%enums); + print Data::Dumper->Dump([\%structs], [qw(*structs)]) if (%structs); + print Data::Dumper->Dump([\%defines], [qw(*defines)]) if (%defines); + print Data::Dumper->Dump([\%enum_symbols], [qw(*enum_symbols)]) if (%enum_symbols); +} + +# +# Align block +# +$data = expand($data); +$data = " " . $data; +$data =~ s/\n/\n /g; +$data =~ s/\n\s+$/\n/g; +$data =~ s/\n\s+\n/\n\n/g; + +# +# Add escape codes for special characters +# +$data =~ s,([\_\`\*\<\>\&\\\\:\/\|]),\\$1,g; + +$data =~ s,DEPRECATED,**DEPRECATED**,g; + +# +# Add references +# + +my $start_delim = "[ \n\t\(\=\*\@]"; +my $end_delim = "(\\s|,|\\\\=|\\\\:|\\;|\\\)|\\}|\\{)"; + +foreach my $r (keys %ioctls) { + my $n = $ioctls{$r}; + + my $s = "\\ :ref:`$r <$n>`\\ "; + + $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; + + print "$r -> $s\n" if ($debug); + + $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; +} + +foreach my $r (keys %defines) { + my $n = $defines{$r}; + + my $s = "\\ :ref:`$r <$n>`\\ "; + + $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; + + print "$r -> $s\n" if ($debug); + + $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; +} + +foreach my $r (keys %enum_symbols) { + my $n = $enum_symbols{$r}; + + my $s = "\\ :ref:`$r <$n>`\\ "; + + $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; + + print "$r -> $s\n" if ($debug); + + $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; +} + +foreach my $r (keys %enums) { + my $n = $enums{$r}; + + my $s = "\\ :ref:`enum $r <$n>`\\ "; + + $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; + + print "$r -> $s\n" if ($debug); + + $data =~ s/enum\s+($r)$end_delim/$s$2/g; +} + +foreach my $r (keys %structs) { + my $n = $structs{$r}; + + my $s = "\\ :ref:`struct $r <$n>`\\ "; + + $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; + + print "$r -> $s\n" if ($debug); + + $data =~ s/struct\s+($r)$end_delim/$s$2/g; +} + +foreach my $r (keys %typedefs) { + my $n = $typedefs{$r}; + + my $s = "\\ :ref:`$r <$n>`\\ "; + + $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; + + print "$r -> $s\n" if ($debug); + + $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; +} + +$data =~ s/\\ \n/\n/g; + +# +# Generate output file +# + +my $title = $file_in; +$title =~ s,.*/,,; + +open OUT, "> $file_out" or die "Can't open $file_out"; +print OUT ".. -*- coding: utf-8; mode: rst -*-\n\n"; +print OUT "$title\n"; +print OUT "=" x length($title); +print OUT "\n\n.. parsed-literal::\n\n"; +print OUT $data; +close OUT; diff --git a/Documentation/sphinx/post_convert.sed b/Documentation/sphinx/post_convert.sed new file mode 100644 index 000000000..392770bac --- /dev/null +++ b/Documentation/sphinx/post_convert.sed @@ -0,0 +1,23 @@ +# +# Unescape. +# +s/$bq/`/g +s/$lt/</g +s/$gt/>/g +# +# pandoc thinks that both "_" needs to be escaped. Remove the extra +# backslashes. +# +s/\\_/_/g +# +# Unwrap docproc directives. +# +s/^``DOCPROC: !E\(.*\)``$/.. kernel-doc:: \1\n :export:/ +s/^``DOCPROC: !I\(.*\)``$/.. kernel-doc:: \1\n :internal:/ +s/^``DOCPROC: !F\([^ ]*\) \(.*\)``$/.. kernel-doc:: \1\n :functions: \2/ +s/^``DOCPROC: !P\([^ ]*\) \(.*\)``$/.. kernel-doc:: \1\n :doc: \2/ +s/^``DOCPROC: \(!.*\)``$/.. WARNING: DOCPROC directive not supported: \1/ +# +# Trim trailing whitespace. +# +s/[[:space:]]*$// diff --git a/Documentation/sphinx/rstFlatTable.py b/Documentation/sphinx/rstFlatTable.py new file mode 100644 index 000000000..26db852e3 --- /dev/null +++ b/Documentation/sphinx/rstFlatTable.py @@ -0,0 +1,365 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8; mode: python -*- +# pylint: disable=C0330, R0903, R0912 + +u""" + flat-table + ~~~~~~~~~~ + + Implementation of the ``flat-table`` reST-directive. + + :copyright: Copyright (C) 2016 Markus Heiser + :license: GPL Version 2, June 1991 see linux/COPYING for details. + + The ``flat-table`` (:py:class:`FlatTable`) is a double-stage list similar to + the ``list-table`` with some additional features: + + * *column-span*: with the role ``cspan`` a cell can be extended through + additional columns + + * *row-span*: with the role ``rspan`` a cell can be extended through + additional rows + + * *auto span* rightmost cell of a table row over the missing cells on the + right side of that table-row. With Option ``:fill-cells:`` this behavior + can changed from *auto span* to *auto fill*, which automaticly inserts + (empty) cells instead of spanning the last cell. + + Options: + + * header-rows: [int] count of header rows + * stub-columns: [int] count of stub columns + * widths: [[int] [int] ... ] widths of columns + * fill-cells: instead of autospann missing cells, insert missing cells + + roles: + + * cspan: [int] additionale columns (*morecols*) + * rspan: [int] additionale rows (*morerows*) +""" + +# ============================================================================== +# imports +# ============================================================================== + +import sys + +from docutils import nodes +from docutils.parsers.rst import directives, roles +from docutils.parsers.rst.directives.tables import Table +from docutils.utils import SystemMessagePropagation + +# ============================================================================== +# common globals +# ============================================================================== + +# The version numbering follows numbering of the specification +# (Documentation/books/kernel-doc-HOWTO). +__version__ = '1.0' + +PY3 = sys.version_info[0] == 3 +PY2 = sys.version_info[0] == 2 + +if PY3: + # pylint: disable=C0103, W0622 + unicode = str + basestring = str + +# ============================================================================== +def setup(app): +# ============================================================================== + + app.add_directive("flat-table", FlatTable) + roles.register_local_role('cspan', c_span) + roles.register_local_role('rspan', r_span) + +# ============================================================================== +def c_span(name, rawtext, text, lineno, inliner, options=None, content=None): +# ============================================================================== + # pylint: disable=W0613 + + options = options if options is not None else {} + content = content if content is not None else [] + nodelist = [colSpan(span=int(text))] + msglist = [] + return nodelist, msglist + +# ============================================================================== +def r_span(name, rawtext, text, lineno, inliner, options=None, content=None): +# ============================================================================== + # pylint: disable=W0613 + + options = options if options is not None else {} + content = content if content is not None else [] + nodelist = [rowSpan(span=int(text))] + msglist = [] + return nodelist, msglist + + +# ============================================================================== +class rowSpan(nodes.General, nodes.Element): pass # pylint: disable=C0103,C0321 +class colSpan(nodes.General, nodes.Element): pass # pylint: disable=C0103,C0321 +# ============================================================================== + +# ============================================================================== +class FlatTable(Table): +# ============================================================================== + + u"""FlatTable (``flat-table``) directive""" + + option_spec = { + 'name': directives.unchanged + , 'class': directives.class_option + , 'header-rows': directives.nonnegative_int + , 'stub-columns': directives.nonnegative_int + , 'widths': directives.positive_int_list + , 'fill-cells' : directives.flag } + + def run(self): + + if not self.content: + error = self.state_machine.reporter.error( + 'The "%s" directive is empty; content required.' % self.name, + nodes.literal_block(self.block_text, self.block_text), + line=self.lineno) + return [error] + + title, messages = self.make_title() + node = nodes.Element() # anonymous container for parsing + self.state.nested_parse(self.content, self.content_offset, node) + + tableBuilder = ListTableBuilder(self) + tableBuilder.parseFlatTableNode(node) + tableNode = tableBuilder.buildTableNode() + # SDK.CONSOLE() # print --> tableNode.asdom().toprettyxml() + if title: + tableNode.insert(0, title) + return [tableNode] + messages + + +# ============================================================================== +class ListTableBuilder(object): +# ============================================================================== + + u"""Builds a table from a double-stage list""" + + def __init__(self, directive): + self.directive = directive + self.rows = [] + self.max_cols = 0 + + def buildTableNode(self): + + colwidths = self.directive.get_column_widths(self.max_cols) + stub_columns = self.directive.options.get('stub-columns', 0) + header_rows = self.directive.options.get('header-rows', 0) + + table = nodes.table() + tgroup = nodes.tgroup(cols=len(colwidths)) + table += tgroup + + + for colwidth in colwidths: + colspec = nodes.colspec(colwidth=colwidth) + # FIXME: It seems, that the stub method only works well in the + # absence of rowspan (observed by the html buidler, the docutils-xml + # build seems OK). This is not extraordinary, because there exists + # no table directive (except *this* flat-table) which allows to + # define coexistent of rowspan and stubs (there was no use-case + # before flat-table). This should be reviewed (later). + if stub_columns: + colspec.attributes['stub'] = 1 + stub_columns -= 1 + tgroup += colspec + stub_columns = self.directive.options.get('stub-columns', 0) + + if header_rows: + thead = nodes.thead() + tgroup += thead + for row in self.rows[:header_rows]: + thead += self.buildTableRowNode(row) + + tbody = nodes.tbody() + tgroup += tbody + + for row in self.rows[header_rows:]: + tbody += self.buildTableRowNode(row) + return table + + def buildTableRowNode(self, row_data, classes=None): + classes = [] if classes is None else classes + row = nodes.row() + for cell in row_data: + if cell is None: + continue + cspan, rspan, cellElements = cell + + attributes = {"classes" : classes} + if rspan: + attributes['morerows'] = rspan + if cspan: + attributes['morecols'] = cspan + entry = nodes.entry(**attributes) + entry.extend(cellElements) + row += entry + return row + + def raiseError(self, msg): + error = self.directive.state_machine.reporter.error( + msg + , nodes.literal_block(self.directive.block_text + , self.directive.block_text) + , line = self.directive.lineno ) + raise SystemMessagePropagation(error) + + def parseFlatTableNode(self, node): + u"""parses the node from a :py:class:`FlatTable` directive's body""" + + if len(node) != 1 or not isinstance(node[0], nodes.bullet_list): + self.raiseError( + 'Error parsing content block for the "%s" directive: ' + 'exactly one bullet list expected.' % self.directive.name ) + + for rowNum, rowItem in enumerate(node[0]): + row = self.parseRowItem(rowItem, rowNum) + self.rows.append(row) + self.roundOffTableDefinition() + + def roundOffTableDefinition(self): + u"""Round off the table definition. + + This method rounds off the table definition in :py:member:`rows`. + + * This method inserts the needed ``None`` values for the missing cells + arising from spanning cells over rows and/or columns. + + * recount the :py:member:`max_cols` + + * Autospan or fill (option ``fill-cells``) missing cells on the right + side of the table-row + """ + + y = 0 + while y < len(self.rows): + x = 0 + + while x < len(self.rows[y]): + cell = self.rows[y][x] + if cell is None: + x += 1 + continue + cspan, rspan = cell[:2] + # handle colspan in current row + for c in range(cspan): + try: + self.rows[y].insert(x+c+1, None) + except: # pylint: disable=W0702 + # the user sets ambiguous rowspans + pass # SDK.CONSOLE() + # handle colspan in spanned rows + for r in range(rspan): + for c in range(cspan + 1): + try: + self.rows[y+r+1].insert(x+c, None) + except: # pylint: disable=W0702 + # the user sets ambiguous rowspans + pass # SDK.CONSOLE() + x += 1 + y += 1 + + # Insert the missing cells on the right side. For this, first + # re-calculate the max columns. + + for row in self.rows: + if self.max_cols < len(row): + self.max_cols = len(row) + + # fill with empty cells or cellspan? + + fill_cells = False + if 'fill-cells' in self.directive.options: + fill_cells = True + + for row in self.rows: + x = self.max_cols - len(row) + if x and not fill_cells: + if row[-1] is None: + row.append( ( x - 1, 0, []) ) + else: + cspan, rspan, content = row[-1] + row[-1] = (cspan + x, rspan, content) + elif x and fill_cells: + for i in range(x): + row.append( (0, 0, nodes.comment()) ) + + def pprint(self): + # for debugging + retVal = "[ " + for row in self.rows: + retVal += "[ " + for col in row: + if col is None: + retVal += ('%r' % col) + retVal += "\n , " + else: + content = col[2][0].astext() + if len (content) > 30: + content = content[:30] + "..." + retVal += ('(cspan=%s, rspan=%s, %r)' + % (col[0], col[1], content)) + retVal += "]\n , " + retVal = retVal[:-2] + retVal += "]\n , " + retVal = retVal[:-2] + return retVal + "]" + + def parseRowItem(self, rowItem, rowNum): + row = [] + childNo = 0 + error = False + cell = None + target = None + + for child in rowItem: + if (isinstance(child , nodes.comment) + or isinstance(child, nodes.system_message)): + pass + elif isinstance(child , nodes.target): + target = child + elif isinstance(child, nodes.bullet_list): + childNo += 1 + cell = child + else: + error = True + break + + if childNo != 1 or error: + self.raiseError( + 'Error parsing content block for the "%s" directive: ' + 'two-level bullet list expected, but row %s does not ' + 'contain a second-level bullet list.' + % (self.directive.name, rowNum + 1)) + + for cellItem in cell: + cspan, rspan, cellElements = self.parseCellItem(cellItem) + if target is not None: + cellElements.insert(0, target) + row.append( (cspan, rspan, cellElements) ) + return row + + def parseCellItem(self, cellItem): + # search and remove cspan, rspan colspec from the first element in + # this listItem (field). + cspan = rspan = 0 + if not len(cellItem): + return cspan, rspan, [] + for elem in cellItem[0]: + if isinstance(elem, colSpan): + cspan = elem.get("span") + elem.parent.remove(elem) + continue + if isinstance(elem, rowSpan): + rspan = elem.get("span") + elem.parent.remove(elem) + continue + return cspan, rspan, cellItem[:] diff --git a/Documentation/sphinx/tmplcvt b/Documentation/sphinx/tmplcvt new file mode 100755 index 000000000..909a73065 --- /dev/null +++ b/Documentation/sphinx/tmplcvt @@ -0,0 +1,19 @@ +#!/bin/bash +# +# Convert a template file into something like RST +# +# fix <function> +# feed to pandoc +# fix \_ +# title line? +# + +in=$1 +rst=$2 +tmp=$rst.tmp + +cp $in $tmp +sed --in-place -f convert_template.sed $tmp +pandoc -s -S -f docbook -t rst -o $rst $tmp +sed --in-place -f post_convert.sed $rst +rm $tmp |