]> git.ipfire.org Git - thirdparty/systemd.git/blame - tools/make-directive-index.py
Merge pull request #32336 from teknoraver/foreach_element
[thirdparty/systemd.git] / tools / make-directive-index.py
CommitLineData
3e67e5c9 1#!/usr/bin/env python3
db9ecf05 2# SPDX-License-Identifier: LGPL-2.1-or-later
e7098b69 3
d9cfd694 4import collections
ccc9a4f9 5import re
94772768 6import sys
b8f1045f 7from copy import deepcopy
d9cfd694 8
94772768
FS
9from xml_helper import tree, xml_parse, xml_print
10
0acfdd61
ZJS
11COLOPHON = '''\
12This index contains {count} entries in {sections} sections,
13referring to {pages} individual manual pages.
14'''
15
b8f1045f 16def _extract_directives(directive_groups, formatting, page):
1a13e31d 17 t = xml_parse(page)
d9cfd694
ZJS
18 section = t.find('./refmeta/manvolnum').text
19 pagename = t.find('./refmeta/refentrytitle').text
a4e0b94d
ZJS
20
21 storopt = directive_groups['options']
d9cfd694 22 for variablelist in t.iterfind('.//variablelist'):
ccc9a4f9 23 klass = variablelist.attrib.get('class')
acbfdec3 24 searchpath = variablelist.attrib.get('xpath','./varlistentry/term/varname')
ccc9a4f9 25 storvar = directive_groups[klass or 'miscellaneous']
ccc9a4f9 26 # <option>s go in OPTIONS, unless class is specified
acbfdec3 27 for xpath, stor in ((searchpath, storvar),
ccc9a4f9
ZJS
28 ('./varlistentry/term/option',
29 storvar if klass else storopt)):
30 for name in variablelist.iterfind(xpath):
31 text = re.sub(r'([= ]).*', r'\1', name.text).rstrip()
8c6c56c3
ZJS
32 if text.startswith('-'):
33 # for options, merge options with and without mandatory arg
34 text = text.partition('=')[0]
b8f1045f 35 stor[text].append((pagename, section))
d970bd6d
ZJS
36 if text not in formatting:
37 # use element as formatted display
f8b68539 38 if name.text[-1] in "= '":
699ad6c0
ZJS
39 name.clear()
40 else:
41 name.tail = ''
d970bd6d
ZJS
42 name.text = text
43 formatting[text] = name
8906e262
JR
44 extra = variablelist.attrib.get('extra-ref')
45 if extra:
b8f1045f 46 stor[extra].append((pagename, section))
8906e262
JR
47 if extra not in formatting:
48 elt = tree.Element("varname")
49 elt.text= extra
50 formatting[extra] = elt
d9cfd694 51
a4e0b94d 52 storfile = directive_groups['filenames']
845c5324
ZJS
53 for xpath, absolute_only in (('.//refsynopsisdiv//filename', False),
54 ('.//refsynopsisdiv//command', False),
55 ('.//filename', True)):
a4e0b94d 56 for name in t.iterfind(xpath):
845c5324
ZJS
57 if absolute_only and not (name.text and name.text.startswith('/')):
58 continue
b0343f8c 59 if name.attrib.get('index') == 'false':
845c5324 60 continue
a4e0b94d
ZJS
61 name.tail = ''
62 if name.text:
845c5324
ZJS
63 if name.text.endswith('*'):
64 name.text = name.text[:-1]
a4e0b94d
ZJS
65 if not name.text.startswith('.'):
66 text = name.text.partition(' ')[0]
67 if text != name.text:
68 name.clear()
69 name.text = text
845c5324
ZJS
70 if text.endswith('/'):
71 text = text[:-1]
b8f1045f 72 storfile[text].append((pagename, section))
a4e0b94d
ZJS
73 if text not in formatting:
74 # use element as formatted display
75 formatting[text] = name
76 else:
77 text = ' '.join(name.itertext())
b8f1045f 78 storfile[text].append((pagename, section))
a4e0b94d
ZJS
79 formatting[text] = name
80
785a51eb 81 for name in t.iterfind('.//constant'):
b0343f8c 82 if name.attrib.get('index') == 'false':
785a51eb
ZJS
83 continue
84 name.tail = ''
85 if name.text.startswith('('): # a cast, strip it
86 name.text = name.text.partition(' ')[2]
d080734d
ZJS
87 klass = name.attrib.get('class') or 'constants'
88 storfile = directive_groups[klass]
b8f1045f 89 storfile[name.text].append((pagename, section))
785a51eb
ZJS
90 formatting[name.text] = name
91
0d525a3e
ZJS
92 storfile = directive_groups['specifiers']
93 for name in t.iterfind(".//table[@class='specifiers']//entry/literal"):
94 if name.text[0] != '%' or name.getparent().text is not None:
95 continue
96 if name.attrib.get('index') == 'false':
97 continue
b8f1045f 98 storfile[name.text].append((pagename, section))
0d525a3e 99 formatting[name.text] = name
6dbf4025 100 for name in t.iterfind(".//literal[@class='specifiers']"):
b8f1045f 101 storfile[name.text].append((pagename, section))
6dbf4025 102 formatting[name.text] = name
0d525a3e 103
d970bd6d 104def _make_section(template, name, directives, formatting):
94772768 105 varlist = template.find(f".//*[@id='{name}']")
d9cfd694
ZJS
106 for varname, manpages in sorted(directives.items()):
107 entry = tree.SubElement(varlist, 'varlistentry')
d970bd6d 108 term = tree.SubElement(entry, 'term')
b8f1045f 109 display = deepcopy(formatting[varname])
827f70eb 110 term.append(display)
d970bd6d 111
d9cfd694
ZJS
112 para = tree.SubElement(tree.SubElement(entry, 'listitem'), 'para')
113
114 b = None
ccc9a4f9 115 for manpage, manvolume in sorted(set(manpages)):
827f70eb
ZJS
116 if b is not None:
117 b.tail = ', '
118 b = tree.SubElement(para, 'citerefentry')
119 c = tree.SubElement(b, 'refentrytitle')
120 c.text = manpage
958caa58 121 c.attrib['target'] = varname
827f70eb
ZJS
122 d = tree.SubElement(b, 'manvolnum')
123 d.text = manvolume
d9cfd694
ZJS
124 entry.tail = '\n\n'
125
0acfdd61
ZJS
126def _make_colophon(template, groups):
127 count = 0
128 pages = set()
129 for group in groups:
130 count += len(group)
131 for pagelist in group.values():
132 pages |= set(pagelist)
133
134 para = template.find(".//para[@id='colophon']")
135 para.text = COLOPHON.format(count=count,
136 sections=len(groups),
137 pages=len(pages))
138
d970bd6d 139def _make_page(template, directive_groups, formatting):
d9cfd694
ZJS
140 """Create an XML tree from directive_groups.
141
142 directive_groups = {
143 'class': {'variable': [('manpage', 'manvolume'), ...],
144 'variable2': ...},
145 ...
146 }
147 """
d9cfd694 148 for name, directives in directive_groups.items():
827f70eb 149 _make_section(template, name, directives, formatting)
d9cfd694 150
0acfdd61
ZJS
151 _make_colophon(template, directive_groups.values())
152
eeb019b5 153 return template
d9cfd694 154
28223088 155def make_page(template_path, xml_files):
d9cfd694 156 "Extract directives from xml_files and return XML index tree."
28223088 157 template = xml_parse(template_path)
eeb019b5 158 names = [vl.get('id') for vl in template.iterfind('.//variablelist')]
b8f1045f
ZJS
159 directive_groups = {name:collections.defaultdict(list)
160 for name in names}
d970bd6d 161 formatting = {}
b8f1045f
ZJS
162 for page in xml_files:
163 try:
164 _extract_directives(directive_groups, formatting, page)
94772768
FS
165 except Exception as e:
166 raise ValueError("failed to process " + page) from e
d9cfd694 167
d970bd6d 168 return _make_page(template, directive_groups, formatting)
d9cfd694 169
b8f1045f
ZJS
170if __name__ == '__main__':
171 with open(sys.argv[1], 'wb') as f:
94772768
FS
172 _template_path = sys.argv[2]
173 _xml_files = sys.argv[3:]
174 _xml = make_page(_template_path, _xml_files)
175 f.write(xml_print(_xml))