1# SPDX-License-Identifier: GPL-2.0 2# Copyright 2019 Jonathan Corbet <corbet@lwn.net> 3# 4# Apply kernel-specific tweaks after the initial document processing 5# has been done. 6# 7from docutils import nodes 8import sphinx 9from sphinx import addnodes 10if sphinx.version_info[0] < 2 or \ 11 sphinx.version_info[0] == 2 and sphinx.version_info[1] < 1: 12 from sphinx.environment import NoUri 13else: 14 from sphinx.errors import NoUri 15import re 16 17# 18# Regex nastiness. Of course. 19# Try to identify "function()" that's not already marked up some 20# other way. Sphinx doesn't like a lot of stuff right after a 21# :c:func: block (i.e. ":c:func:`mmap()`s" flakes out), so the last 22# bit tries to restrict matches to things that won't create trouble. 23# 24RE_function = re.compile(r'([\w_][\w\d_]+\(\))') 25 26# 27# Many places in the docs refer to common system calls. It is 28# pointless to try to cross-reference them and, as has been known 29# to happen, somebody defining a function by these names can lead 30# to the creation of incorrect and confusing cross references. So 31# just don't even try with these names. 32# 33Skipfuncs = [ 'open', 'close', 'read', 'write', 'fcntl', 'mmap', 34 'select', 'poll', 'fork', 'execve', 'clone', 'ioctl', 35 'socket' ] 36 37# 38# Find all occurrences of function() and try to replace them with 39# appropriate cross references. 40# 41def markup_funcs(docname, app, node): 42 cdom = app.env.domains['c'] 43 t = node.astext() 44 done = 0 45 repl = [ ] 46 for m in RE_function.finditer(t): 47 # 48 # Include any text prior to function() as a normal text node. 49 # 50 if m.start() > done: 51 repl.append(nodes.Text(t[done:m.start()])) 52 # 53 # Go through the dance of getting an xref out of the C domain 54 # 55 target = m.group(1)[:-2] 56 target_text = nodes.Text(target + '()') 57 xref = None 58 if target not in Skipfuncs: 59 lit_text = nodes.literal(classes=['xref', 'c', 'c-func']) 60 lit_text += target_text 61 pxref = addnodes.pending_xref('', refdomain = 'c', 62 reftype = 'function', 63 reftarget = target, modname = None, 64 classname = None) 65 # 66 # XXX The Latex builder will throw NoUri exceptions here, 67 # work around that by ignoring them. 68 # 69 try: 70 xref = cdom.resolve_xref(app.env, docname, app.builder, 71 'function', target, pxref, lit_text) 72 except NoUri: 73 xref = None 74 # 75 # Toss the xref into the list if we got it; otherwise just put 76 # the function text. 77 # 78 if xref: 79 repl.append(xref) 80 else: 81 repl.append(target_text) 82 done = m.end() 83 if done < len(t): 84 repl.append(nodes.Text(t[done:])) 85 return repl 86 87def auto_markup(app, doctree, name): 88 # 89 # This loop could eventually be improved on. Someday maybe we 90 # want a proper tree traversal with a lot of awareness of which 91 # kinds of nodes to prune. But this works well for now. 92 # 93 # The nodes.literal test catches ``literal text``, its purpose is to 94 # avoid adding cross-references to functions that have been explicitly 95 # marked with cc:func:. 96 # 97 for para in doctree.traverse(nodes.paragraph): 98 for node in para.traverse(nodes.Text): 99 if not isinstance(node.parent, nodes.literal): 100 node.parent.replace(node, markup_funcs(name, app, node)) 101 102def setup(app): 103 app.connect('doctree-resolved', auto_markup) 104 return { 105 'parallel_read_safe': True, 106 'parallel_write_safe': True, 107 } 108