Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1#!/usr/bin/env python3
2# SPDX-License-Identifier: GPL-2.0
3# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
4#
5# pylint: disable=C0301,R0902,R0911,R0912,R0913,R0914,R0915,R0917
6
7"""
8Implement output filters to print kernel-doc documentation.
9
10The implementation uses a virtual base class (OutputFormat) which
11contains a dispatches to virtual methods, and some code to filter
12out output messages.
13
14The actual implementation is done on one separate class per each type
15of output. Currently, there are output classes for ReST and man/troff.
16"""
17
18import os
19import re
20from datetime import datetime
21
22from kdoc_parser import KernelDoc, type_param
23from kdoc_re import KernRe
24
25
26function_pointer = KernRe(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=False)
27
28# match expressions used to find embedded type information
29type_constant = KernRe(r"\b``([^\`]+)``\b", cache=False)
30type_constant2 = KernRe(r"\%([-_*\w]+)", cache=False)
31type_func = KernRe(r"(\w+)\(\)", cache=False)
32type_param_ref = KernRe(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False)
33
34# Special RST handling for func ptr params
35type_fp_param = KernRe(r"\@(\w+)\(\)", cache=False)
36
37# Special RST handling for structs with func ptr params
38type_fp_param2 = KernRe(r"\@(\w+->\S+)\(\)", cache=False)
39
40type_env = KernRe(r"(\$\w+)", cache=False)
41type_enum = KernRe(r"\&(enum\s*([_\w]+))", cache=False)
42type_struct = KernRe(r"\&(struct\s*([_\w]+))", cache=False)
43type_typedef = KernRe(r"\&(typedef\s*([_\w]+))", cache=False)
44type_union = KernRe(r"\&(union\s*([_\w]+))", cache=False)
45type_member = KernRe(r"\&([_\w]+)(\.|->)([_\w]+)", cache=False)
46type_fallback = KernRe(r"\&([_\w]+)", cache=False)
47type_member_func = type_member + KernRe(r"\(\)", cache=False)
48
49
50class OutputFormat:
51 """
52 Base class for OutputFormat. If used as-is, it means that only
53 warnings will be displayed.
54 """
55
56 # output mode.
57 OUTPUT_ALL = 0 # output all symbols and doc sections
58 OUTPUT_INCLUDE = 1 # output only specified symbols
59 OUTPUT_EXPORTED = 2 # output exported symbols
60 OUTPUT_INTERNAL = 3 # output non-exported symbols
61
62 # Virtual member to be overriden at the inherited classes
63 highlights = []
64
65 def __init__(self):
66 """Declare internal vars and set mode to OUTPUT_ALL"""
67
68 self.out_mode = self.OUTPUT_ALL
69 self.enable_lineno = None
70 self.nosymbol = {}
71 self.symbol = None
72 self.function_table = None
73 self.config = None
74 self.no_doc_sections = False
75
76 self.data = ""
77
78 def set_config(self, config):
79 """
80 Setup global config variables used by both parser and output.
81 """
82
83 self.config = config
84
85 def set_filter(self, export, internal, symbol, nosymbol, function_table,
86 enable_lineno, no_doc_sections):
87 """
88 Initialize filter variables according with the requested mode.
89
90 Only one choice is valid between export, internal and symbol.
91
92 The nosymbol filter can be used on all modes.
93 """
94
95 self.enable_lineno = enable_lineno
96 self.no_doc_sections = no_doc_sections
97 self.function_table = function_table
98
99 if symbol:
100 self.out_mode = self.OUTPUT_INCLUDE
101 elif export:
102 self.out_mode = self.OUTPUT_EXPORTED
103 elif internal:
104 self.out_mode = self.OUTPUT_INTERNAL
105 else:
106 self.out_mode = self.OUTPUT_ALL
107
108 if nosymbol:
109 self.nosymbol = set(nosymbol)
110
111
112 def highlight_block(self, block):
113 """
114 Apply the RST highlights to a sub-block of text.
115 """
116
117 for r, sub in self.highlights:
118 block = r.sub(sub, block)
119
120 return block
121
122 def out_warnings(self, args):
123 """
124 Output warnings for identifiers that will be displayed.
125 """
126
127 warnings = args.get('warnings', [])
128
129 for log_msg in warnings:
130 self.config.warning(log_msg)
131
132 def check_doc(self, name, args):
133 """Check if DOC should be output"""
134
135 if self.no_doc_sections:
136 return False
137
138 if name in self.nosymbol:
139 return False
140
141 if self.out_mode == self.OUTPUT_ALL:
142 self.out_warnings(args)
143 return True
144
145 if self.out_mode == self.OUTPUT_INCLUDE:
146 if name in self.function_table:
147 self.out_warnings(args)
148 return True
149
150 return False
151
152 def check_declaration(self, dtype, name, args):
153 """
154 Checks if a declaration should be output or not based on the
155 filtering criteria.
156 """
157
158 if name in self.nosymbol:
159 return False
160
161 if self.out_mode == self.OUTPUT_ALL:
162 self.out_warnings(args)
163 return True
164
165 if self.out_mode in [self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED]:
166 if name in self.function_table:
167 return True
168
169 if self.out_mode == self.OUTPUT_INTERNAL:
170 if dtype != "function":
171 self.out_warnings(args)
172 return True
173
174 if name not in self.function_table:
175 self.out_warnings(args)
176 return True
177
178 return False
179
180 def msg(self, fname, name, args):
181 """
182 Handles a single entry from kernel-doc parser
183 """
184
185 self.data = ""
186
187 dtype = args.get('type', "")
188
189 if dtype == "doc":
190 self.out_doc(fname, name, args)
191 return self.data
192
193 if not self.check_declaration(dtype, name, args):
194 return self.data
195
196 if dtype == "function":
197 self.out_function(fname, name, args)
198 return self.data
199
200 if dtype == "enum":
201 self.out_enum(fname, name, args)
202 return self.data
203
204 if dtype == "typedef":
205 self.out_typedef(fname, name, args)
206 return self.data
207
208 if dtype in ["struct", "union"]:
209 self.out_struct(fname, name, args)
210 return self.data
211
212 # Warn if some type requires an output logic
213 self.config.log.warning("doesn't now how to output '%s' block",
214 dtype)
215
216 return None
217
218 # Virtual methods to be overridden by inherited classes
219 # At the base class, those do nothing.
220 def out_doc(self, fname, name, args):
221 """Outputs a DOC block"""
222
223 def out_function(self, fname, name, args):
224 """Outputs a function"""
225
226 def out_enum(self, fname, name, args):
227 """Outputs an enum"""
228
229 def out_typedef(self, fname, name, args):
230 """Outputs a typedef"""
231
232 def out_struct(self, fname, name, args):
233 """Outputs a struct"""
234
235
236class RestFormat(OutputFormat):
237 """Consts and functions used by ReST output"""
238
239 highlights = [
240 (type_constant, r"``\1``"),
241 (type_constant2, r"``\1``"),
242
243 # Note: need to escape () to avoid func matching later
244 (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"),
245 (type_member, r":c:type:`\1\2\3 <\1>`"),
246 (type_fp_param, r"**\1\\(\\)**"),
247 (type_fp_param2, r"**\1\\(\\)**"),
248 (type_func, r"\1()"),
249 (type_enum, r":c:type:`\1 <\2>`"),
250 (type_struct, r":c:type:`\1 <\2>`"),
251 (type_typedef, r":c:type:`\1 <\2>`"),
252 (type_union, r":c:type:`\1 <\2>`"),
253
254 # in rst this can refer to any type
255 (type_fallback, r":c:type:`\1`"),
256 (type_param_ref, r"**\1\2**")
257 ]
258 blankline = "\n"
259
260 sphinx_literal = KernRe(r'^[^.].*::$', cache=False)
261 sphinx_cblock = KernRe(r'^\.\.\ +code-block::', cache=False)
262
263 def __init__(self):
264 """
265 Creates class variables.
266
267 Not really mandatory, but it is a good coding style and makes
268 pylint happy.
269 """
270
271 super().__init__()
272 self.lineprefix = ""
273
274 def print_lineno(self, ln):
275 """Outputs a line number"""
276
277 if self.enable_lineno and ln is not None:
278 ln += 1
279 self.data += f".. LINENO {ln}\n"
280
281 def output_highlight(self, args):
282 """
283 Outputs a C symbol that may require being converted to ReST using
284 the self.highlights variable
285 """
286
287 input_text = args
288 output = ""
289 in_literal = False
290 litprefix = ""
291 block = ""
292
293 for line in input_text.strip("\n").split("\n"):
294
295 # If we're in a literal block, see if we should drop out of it.
296 # Otherwise, pass the line straight through unmunged.
297 if in_literal:
298 if line.strip(): # If the line is not blank
299 # If this is the first non-blank line in a literal block,
300 # figure out the proper indent.
301 if not litprefix:
302 r = KernRe(r'^(\s*)')
303 if r.match(line):
304 litprefix = '^' + r.group(1)
305 else:
306 litprefix = ""
307
308 output += line + "\n"
309 elif not KernRe(litprefix).match(line):
310 in_literal = False
311 else:
312 output += line + "\n"
313 else:
314 output += line + "\n"
315
316 # Not in a literal block (or just dropped out)
317 if not in_literal:
318 block += line + "\n"
319 if self.sphinx_literal.match(line) or self.sphinx_cblock.match(line):
320 in_literal = True
321 litprefix = ""
322 output += self.highlight_block(block)
323 block = ""
324
325 # Handle any remaining block
326 if block:
327 output += self.highlight_block(block)
328
329 # Print the output with the line prefix
330 for line in output.strip("\n").split("\n"):
331 self.data += self.lineprefix + line + "\n"
332
333 def out_section(self, args, out_docblock=False):
334 """
335 Outputs a block section.
336
337 This could use some work; it's used to output the DOC: sections, and
338 starts by putting out the name of the doc section itself, but that
339 tends to duplicate a header already in the template file.
340 """
341
342 sectionlist = args.get('sectionlist', [])
343 sections = args.get('sections', {})
344 section_start_lines = args.get('section_start_lines', {})
345
346 for section in sectionlist:
347 # Skip sections that are in the nosymbol_table
348 if section in self.nosymbol:
349 continue
350
351 if out_docblock:
352 if not self.out_mode == self.OUTPUT_INCLUDE:
353 self.data += f".. _{section}:\n\n"
354 self.data += f'{self.lineprefix}**{section}**\n\n'
355 else:
356 self.data += f'{self.lineprefix}**{section}**\n\n'
357
358 self.print_lineno(section_start_lines.get(section, 0))
359 self.output_highlight(sections[section])
360 self.data += "\n"
361 self.data += "\n"
362
363 def out_doc(self, fname, name, args):
364 if not self.check_doc(name, args):
365 return
366 self.out_section(args, out_docblock=True)
367
368 def out_function(self, fname, name, args):
369
370 oldprefix = self.lineprefix
371 signature = ""
372
373 func_macro = args.get('func_macro', False)
374 if func_macro:
375 signature = args['function']
376 else:
377 if args.get('functiontype'):
378 signature = args['functiontype'] + " "
379 signature += args['function'] + " ("
380
381 parameterlist = args.get('parameterlist', [])
382 parameterdescs = args.get('parameterdescs', {})
383 parameterdesc_start_lines = args.get('parameterdesc_start_lines', {})
384
385 ln = args.get('declaration_start_line', 0)
386
387 count = 0
388 for parameter in parameterlist:
389 if count != 0:
390 signature += ", "
391 count += 1
392 dtype = args['parametertypes'].get(parameter, "")
393
394 if function_pointer.search(dtype):
395 signature += function_pointer.group(1) + parameter + function_pointer.group(3)
396 else:
397 signature += dtype
398
399 if not func_macro:
400 signature += ")"
401
402 self.print_lineno(ln)
403 if args.get('typedef') or not args.get('functiontype'):
404 self.data += f".. c:macro:: {args['function']}\n\n"
405
406 if args.get('typedef'):
407 self.data += " **Typedef**: "
408 self.lineprefix = ""
409 self.output_highlight(args.get('purpose', ""))
410 self.data += "\n\n**Syntax**\n\n"
411 self.data += f" ``{signature}``\n\n"
412 else:
413 self.data += f"``{signature}``\n\n"
414 else:
415 self.data += f".. c:function:: {signature}\n\n"
416
417 if not args.get('typedef'):
418 self.print_lineno(ln)
419 self.lineprefix = " "
420 self.output_highlight(args.get('purpose', ""))
421 self.data += "\n"
422
423 # Put descriptive text into a container (HTML <div>) to help set
424 # function prototypes apart
425 self.lineprefix = " "
426
427 if parameterlist:
428 self.data += ".. container:: kernelindent\n\n"
429 self.data += f"{self.lineprefix}**Parameters**\n\n"
430
431 for parameter in parameterlist:
432 parameter_name = KernRe(r'\[.*').sub('', parameter)
433 dtype = args['parametertypes'].get(parameter, "")
434
435 if dtype:
436 self.data += f"{self.lineprefix}``{dtype}``\n"
437 else:
438 self.data += f"{self.lineprefix}``{parameter}``\n"
439
440 self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0))
441
442 self.lineprefix = " "
443 if parameter_name in parameterdescs and \
444 parameterdescs[parameter_name] != KernelDoc.undescribed:
445
446 self.output_highlight(parameterdescs[parameter_name])
447 self.data += "\n"
448 else:
449 self.data += f"{self.lineprefix}*undescribed*\n\n"
450 self.lineprefix = " "
451
452 self.out_section(args)
453 self.lineprefix = oldprefix
454
455 def out_enum(self, fname, name, args):
456
457 oldprefix = self.lineprefix
458 name = args.get('enum', '')
459 parameterlist = args.get('parameterlist', [])
460 parameterdescs = args.get('parameterdescs', {})
461 ln = args.get('declaration_start_line', 0)
462
463 self.data += f"\n\n.. c:enum:: {name}\n\n"
464
465 self.print_lineno(ln)
466 self.lineprefix = " "
467 self.output_highlight(args.get('purpose', ''))
468 self.data += "\n"
469
470 self.data += ".. container:: kernelindent\n\n"
471 outer = self.lineprefix + " "
472 self.lineprefix = outer + " "
473 self.data += f"{outer}**Constants**\n\n"
474
475 for parameter in parameterlist:
476 self.data += f"{outer}``{parameter}``\n"
477
478 if parameterdescs.get(parameter, '') != KernelDoc.undescribed:
479 self.output_highlight(parameterdescs[parameter])
480 else:
481 self.data += f"{self.lineprefix}*undescribed*\n\n"
482 self.data += "\n"
483
484 self.lineprefix = oldprefix
485 self.out_section(args)
486
487 def out_typedef(self, fname, name, args):
488
489 oldprefix = self.lineprefix
490 name = args.get('typedef', '')
491 ln = args.get('declaration_start_line', 0)
492
493 self.data += f"\n\n.. c:type:: {name}\n\n"
494
495 self.print_lineno(ln)
496 self.lineprefix = " "
497
498 self.output_highlight(args.get('purpose', ''))
499
500 self.data += "\n"
501
502 self.lineprefix = oldprefix
503 self.out_section(args)
504
505 def out_struct(self, fname, name, args):
506
507 name = args.get('struct', "")
508 purpose = args.get('purpose', "")
509 declaration = args.get('definition', "")
510 dtype = args.get('type', "struct")
511 ln = args.get('declaration_start_line', 0)
512
513 parameterlist = args.get('parameterlist', [])
514 parameterdescs = args.get('parameterdescs', {})
515 parameterdesc_start_lines = args.get('parameterdesc_start_lines', {})
516
517 self.data += f"\n\n.. c:{dtype}:: {name}\n\n"
518
519 self.print_lineno(ln)
520
521 oldprefix = self.lineprefix
522 self.lineprefix += " "
523
524 self.output_highlight(purpose)
525 self.data += "\n"
526
527 self.data += ".. container:: kernelindent\n\n"
528 self.data += f"{self.lineprefix}**Definition**::\n\n"
529
530 self.lineprefix = self.lineprefix + " "
531
532 declaration = declaration.replace("\t", self.lineprefix)
533
534 self.data += f"{self.lineprefix}{dtype} {name}" + ' {' + "\n"
535 self.data += f"{declaration}{self.lineprefix}" + "};\n\n"
536
537 self.lineprefix = " "
538 self.data += f"{self.lineprefix}**Members**\n\n"
539 for parameter in parameterlist:
540 if not parameter or parameter.startswith("#"):
541 continue
542
543 parameter_name = parameter.split("[", maxsplit=1)[0]
544
545 if parameterdescs.get(parameter_name) == KernelDoc.undescribed:
546 continue
547
548 self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0))
549
550 self.data += f"{self.lineprefix}``{parameter}``\n"
551
552 self.lineprefix = " "
553 self.output_highlight(parameterdescs[parameter_name])
554 self.lineprefix = " "
555
556 self.data += "\n"
557
558 self.data += "\n"
559
560 self.lineprefix = oldprefix
561 self.out_section(args)
562
563
564class ManFormat(OutputFormat):
565 """Consts and functions used by man pages output"""
566
567 highlights = (
568 (type_constant, r"\1"),
569 (type_constant2, r"\1"),
570 (type_func, r"\\fB\1\\fP"),
571 (type_enum, r"\\fI\1\\fP"),
572 (type_struct, r"\\fI\1\\fP"),
573 (type_typedef, r"\\fI\1\\fP"),
574 (type_union, r"\\fI\1\\fP"),
575 (type_param, r"\\fI\1\\fP"),
576 (type_param_ref, r"\\fI\1\2\\fP"),
577 (type_member, r"\\fI\1\2\3\\fP"),
578 (type_fallback, r"\\fI\1\\fP")
579 )
580 blankline = ""
581
582 date_formats = [
583 "%a %b %d %H:%M:%S %Z %Y",
584 "%a %b %d %H:%M:%S %Y",
585 "%Y-%m-%d",
586 "%b %d %Y",
587 "%B %d %Y",
588 "%m %d %Y",
589 ]
590
591 def __init__(self, modulename):
592 """
593 Creates class variables.
594
595 Not really mandatory, but it is a good coding style and makes
596 pylint happy.
597 """
598
599 super().__init__()
600 self.modulename = modulename
601
602 dt = None
603 tstamp = os.environ.get("KBUILD_BUILD_TIMESTAMP")
604 if tstamp:
605 for fmt in self.date_formats:
606 try:
607 dt = datetime.strptime(tstamp, fmt)
608 break
609 except ValueError:
610 pass
611
612 if not dt:
613 dt = datetime.now()
614
615 self.man_date = dt.strftime("%B %Y")
616
617 def output_highlight(self, block):
618 """
619 Outputs a C symbol that may require being highlighted with
620 self.highlights variable using troff syntax
621 """
622
623 contents = self.highlight_block(block)
624
625 if isinstance(contents, list):
626 contents = "\n".join(contents)
627
628 for line in contents.strip("\n").split("\n"):
629 line = KernRe(r"^\s*").sub("", line)
630 if not line:
631 continue
632
633 if line[0] == ".":
634 self.data += "\\&" + line + "\n"
635 else:
636 self.data += line + "\n"
637
638 def out_doc(self, fname, name, args):
639 sectionlist = args.get('sectionlist', [])
640 sections = args.get('sections', {})
641
642 if not self.check_doc(name, args):
643 return
644
645 self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n"
646
647 for section in sectionlist:
648 self.data += f'.SH "{section}"' + "\n"
649 self.output_highlight(sections.get(section))
650
651 def out_function(self, fname, name, args):
652 """output function in man"""
653
654 parameterlist = args.get('parameterlist', [])
655 parameterdescs = args.get('parameterdescs', {})
656 sectionlist = args.get('sectionlist', [])
657 sections = args.get('sections', {})
658
659 self.data += f'.TH "{args["function"]}" 9 "{args["function"]}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n"
660
661 self.data += ".SH NAME\n"
662 self.data += f"{args['function']} \\- {args['purpose']}\n"
663
664 self.data += ".SH SYNOPSIS\n"
665 if args.get('functiontype', ''):
666 self.data += f'.B "{args["functiontype"]}" {args["function"]}' + "\n"
667 else:
668 self.data += f'.B "{args["function"]}' + "\n"
669
670 count = 0
671 parenth = "("
672 post = ","
673
674 for parameter in parameterlist:
675 if count == len(parameterlist) - 1:
676 post = ");"
677
678 dtype = args['parametertypes'].get(parameter, "")
679 if function_pointer.match(dtype):
680 # Pointer-to-function
681 self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n"
682 else:
683 dtype = KernRe(r'([^\*])$').sub(r'\1 ', dtype)
684
685 self.data += f'.BI "{parenth}{dtype}" "{post}"' + "\n"
686 count += 1
687 parenth = ""
688
689 if parameterlist:
690 self.data += ".SH ARGUMENTS\n"
691
692 for parameter in parameterlist:
693 parameter_name = re.sub(r'\[.*', '', parameter)
694
695 self.data += f'.IP "{parameter}" 12' + "\n"
696 self.output_highlight(parameterdescs.get(parameter_name, ""))
697
698 for section in sectionlist:
699 self.data += f'.SH "{section.upper()}"' + "\n"
700 self.output_highlight(sections[section])
701
702 def out_enum(self, fname, name, args):
703
704 name = args.get('enum', '')
705 parameterlist = args.get('parameterlist', [])
706 sectionlist = args.get('sectionlist', [])
707 sections = args.get('sections', {})
708
709 self.data += f'.TH "{self.modulename}" 9 "enum {args["enum"]}" "{self.man_date}" "API Manual" LINUX' + "\n"
710
711 self.data += ".SH NAME\n"
712 self.data += f"enum {args['enum']} \\- {args['purpose']}\n"
713
714 self.data += ".SH SYNOPSIS\n"
715 self.data += f"enum {args['enum']}" + " {\n"
716
717 count = 0
718 for parameter in parameterlist:
719 self.data += f'.br\n.BI " {parameter}"' + "\n"
720 if count == len(parameterlist) - 1:
721 self.data += "\n};\n"
722 else:
723 self.data += ", \n.br\n"
724
725 count += 1
726
727 self.data += ".SH Constants\n"
728
729 for parameter in parameterlist:
730 parameter_name = KernRe(r'\[.*').sub('', parameter)
731 self.data += f'.IP "{parameter}" 12' + "\n"
732 self.output_highlight(args['parameterdescs'].get(parameter_name, ""))
733
734 for section in sectionlist:
735 self.data += f'.SH "{section}"' + "\n"
736 self.output_highlight(sections[section])
737
738 def out_typedef(self, fname, name, args):
739 module = self.modulename
740 typedef = args.get('typedef')
741 purpose = args.get('purpose')
742 sectionlist = args.get('sectionlist', [])
743 sections = args.get('sections', {})
744
745 self.data += f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual" LINUX' + "\n"
746
747 self.data += ".SH NAME\n"
748 self.data += f"typedef {typedef} \\- {purpose}\n"
749
750 for section in sectionlist:
751 self.data += f'.SH "{section}"' + "\n"
752 self.output_highlight(sections.get(section))
753
754 def out_struct(self, fname, name, args):
755 module = self.modulename
756 struct_type = args.get('type')
757 struct_name = args.get('struct')
758 purpose = args.get('purpose')
759 definition = args.get('definition')
760 sectionlist = args.get('sectionlist', [])
761 parameterlist = args.get('parameterlist', [])
762 sections = args.get('sections', {})
763 parameterdescs = args.get('parameterdescs', {})
764
765 self.data += f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_date}" "API Manual" LINUX' + "\n"
766
767 self.data += ".SH NAME\n"
768 self.data += f"{struct_type} {struct_name} \\- {purpose}\n"
769
770 # Replace tabs with two spaces and handle newlines
771 declaration = definition.replace("\t", " ")
772 declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration)
773
774 self.data += ".SH SYNOPSIS\n"
775 self.data += f"{struct_type} {struct_name} " + "{" + "\n.br\n"
776 self.data += f'.BI "{declaration}\n' + "};\n.br\n\n"
777
778 self.data += ".SH Members\n"
779 for parameter in parameterlist:
780 if parameter.startswith("#"):
781 continue
782
783 parameter_name = re.sub(r"\[.*", "", parameter)
784
785 if parameterdescs.get(parameter_name) == KernelDoc.undescribed:
786 continue
787
788 self.data += f'.IP "{parameter}" 12' + "\n"
789 self.output_highlight(parameterdescs.get(parameter_name))
790
791 for section in sectionlist:
792 self.data += f'.SH "{section}"' + "\n"
793 self.output_highlight(sections.get(section))