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 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.kdoc_parser import KernelDoc, type_param
23from kdoc.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 overridden 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 to 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 for log_msg in args.warnings:
128 self.config.warning(log_msg)
129
130 def check_doc(self, name, args):
131 """Check if DOC should be output"""
132
133 if self.no_doc_sections:
134 return False
135
136 if name in self.nosymbol:
137 return False
138
139 if self.out_mode == self.OUTPUT_ALL:
140 self.out_warnings(args)
141 return True
142
143 if self.out_mode == self.OUTPUT_INCLUDE:
144 if name in self.function_table:
145 self.out_warnings(args)
146 return True
147
148 return False
149
150 def check_declaration(self, dtype, name, args):
151 """
152 Checks if a declaration should be output or not based on the
153 filtering criteria.
154 """
155
156 if name in self.nosymbol:
157 return False
158
159 if self.out_mode == self.OUTPUT_ALL:
160 self.out_warnings(args)
161 return True
162
163 if self.out_mode in [self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED]:
164 if name in self.function_table:
165 return True
166
167 if self.out_mode == self.OUTPUT_INTERNAL:
168 if dtype != "function":
169 self.out_warnings(args)
170 return True
171
172 if name not in self.function_table:
173 self.out_warnings(args)
174 return True
175
176 return False
177
178 def msg(self, fname, name, args):
179 """
180 Handles a single entry from kernel-doc parser
181 """
182
183 self.data = ""
184
185 dtype = args.type
186
187 if dtype == "doc":
188 self.out_doc(fname, name, args)
189 return self.data
190
191 if not self.check_declaration(dtype, name, args):
192 return self.data
193
194 if dtype == "function":
195 self.out_function(fname, name, args)
196 return self.data
197
198 if dtype == "enum":
199 self.out_enum(fname, name, args)
200 return self.data
201
202 if dtype == "typedef":
203 self.out_typedef(fname, name, args)
204 return self.data
205
206 if dtype in ["struct", "union"]:
207 self.out_struct(fname, name, args)
208 return self.data
209
210 # Warn if some type requires an output logic
211 self.config.log.warning("doesn't know how to output '%s' block",
212 dtype)
213
214 return None
215
216 # Virtual methods to be overridden by inherited classes
217 # At the base class, those do nothing.
218 def set_symbols(self, symbols):
219 """Get a list of all symbols from kernel_doc"""
220
221 def out_doc(self, fname, name, args):
222 """Outputs a DOC block"""
223
224 def out_function(self, fname, name, args):
225 """Outputs a function"""
226
227 def out_enum(self, fname, name, args):
228 """Outputs an enum"""
229
230 def out_typedef(self, fname, name, args):
231 """Outputs a typedef"""
232
233 def out_struct(self, fname, name, args):
234 """Outputs a struct"""
235
236
237class RestFormat(OutputFormat):
238 """Consts and functions used by ReST output"""
239
240 highlights = [
241 (type_constant, r"``\1``"),
242 (type_constant2, r"``\1``"),
243
244 # Note: need to escape () to avoid func matching later
245 (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"),
246 (type_member, r":c:type:`\1\2\3 <\1>`"),
247 (type_fp_param, r"**\1\\(\\)**"),
248 (type_fp_param2, r"**\1\\(\\)**"),
249 (type_func, r"\1()"),
250 (type_enum, r":c:type:`\1 <\2>`"),
251 (type_struct, r":c:type:`\1 <\2>`"),
252 (type_typedef, r":c:type:`\1 <\2>`"),
253 (type_union, r":c:type:`\1 <\2>`"),
254
255 # in rst this can refer to any type
256 (type_fallback, r":c:type:`\1`"),
257 (type_param_ref, r"**\1\2**")
258 ]
259 blankline = "\n"
260
261 sphinx_literal = KernRe(r'^[^.].*::$', cache=False)
262 sphinx_cblock = KernRe(r'^\.\.\ +code-block::', cache=False)
263
264 def __init__(self):
265 """
266 Creates class variables.
267
268 Not really mandatory, but it is a good coding style and makes
269 pylint happy.
270 """
271
272 super().__init__()
273 self.lineprefix = ""
274
275 def print_lineno(self, ln):
276 """Outputs a line number"""
277
278 if self.enable_lineno and ln is not None:
279 ln += 1
280 self.data += f".. LINENO {ln}\n"
281
282 def output_highlight(self, args):
283 """
284 Outputs a C symbol that may require being converted to ReST using
285 the self.highlights variable
286 """
287
288 input_text = args
289 output = ""
290 in_literal = False
291 litprefix = ""
292 block = ""
293
294 for line in input_text.strip("\n").split("\n"):
295
296 # If we're in a literal block, see if we should drop out of it.
297 # Otherwise, pass the line straight through unmunged.
298 if in_literal:
299 if line.strip(): # If the line is not blank
300 # If this is the first non-blank line in a literal block,
301 # figure out the proper indent.
302 if not litprefix:
303 r = KernRe(r'^(\s*)')
304 if r.match(line):
305 litprefix = '^' + r.group(1)
306 else:
307 litprefix = ""
308
309 output += line + "\n"
310 elif not KernRe(litprefix).match(line):
311 in_literal = False
312 else:
313 output += line + "\n"
314 else:
315 output += line + "\n"
316
317 # Not in a literal block (or just dropped out)
318 if not in_literal:
319 block += line + "\n"
320 if self.sphinx_literal.match(line) or self.sphinx_cblock.match(line):
321 in_literal = True
322 litprefix = ""
323 output += self.highlight_block(block)
324 block = ""
325
326 # Handle any remaining block
327 if block:
328 output += self.highlight_block(block)
329
330 # Print the output with the line prefix
331 for line in output.strip("\n").split("\n"):
332 self.data += self.lineprefix + line + "\n"
333
334 def out_section(self, args, out_docblock=False):
335 """
336 Outputs a block section.
337
338 This could use some work; it's used to output the DOC: sections, and
339 starts by putting out the name of the doc section itself, but that
340 tends to duplicate a header already in the template file.
341 """
342 for section, text in args.sections.items():
343 # Skip sections that are in the nosymbol_table
344 if section in self.nosymbol:
345 continue
346
347 if out_docblock:
348 if not self.out_mode == self.OUTPUT_INCLUDE:
349 self.data += f".. _{section}:\n\n"
350 self.data += f'{self.lineprefix}**{section}**\n\n'
351 else:
352 self.data += f'{self.lineprefix}**{section}**\n\n'
353
354 self.print_lineno(args.section_start_lines.get(section, 0))
355 self.output_highlight(text)
356 self.data += "\n"
357 self.data += "\n"
358
359 def out_doc(self, fname, name, args):
360 if not self.check_doc(name, args):
361 return
362 self.out_section(args, out_docblock=True)
363
364 def out_function(self, fname, name, args):
365
366 oldprefix = self.lineprefix
367 signature = ""
368
369 func_macro = args.get('func_macro', False)
370 if func_macro:
371 signature = name
372 else:
373 if args.get('functiontype'):
374 signature = args['functiontype'] + " "
375 signature += name + " ("
376
377 ln = args.declaration_start_line
378 count = 0
379 for parameter in args.parameterlist:
380 if count != 0:
381 signature += ", "
382 count += 1
383 dtype = args.parametertypes.get(parameter, "")
384
385 if function_pointer.search(dtype):
386 signature += function_pointer.group(1) + parameter + function_pointer.group(3)
387 else:
388 signature += dtype
389
390 if not func_macro:
391 signature += ")"
392
393 self.print_lineno(ln)
394 if args.get('typedef') or not args.get('functiontype'):
395 self.data += f".. c:macro:: {name}\n\n"
396
397 if args.get('typedef'):
398 self.data += " **Typedef**: "
399 self.lineprefix = ""
400 self.output_highlight(args.get('purpose', ""))
401 self.data += "\n\n**Syntax**\n\n"
402 self.data += f" ``{signature}``\n\n"
403 else:
404 self.data += f"``{signature}``\n\n"
405 else:
406 self.data += f".. c:function:: {signature}\n\n"
407
408 if not args.get('typedef'):
409 self.print_lineno(ln)
410 self.lineprefix = " "
411 self.output_highlight(args.get('purpose', ""))
412 self.data += "\n"
413
414 # Put descriptive text into a container (HTML <div>) to help set
415 # function prototypes apart
416 self.lineprefix = " "
417
418 if args.parameterlist:
419 self.data += ".. container:: kernelindent\n\n"
420 self.data += f"{self.lineprefix}**Parameters**\n\n"
421
422 for parameter in args.parameterlist:
423 parameter_name = KernRe(r'\[.*').sub('', parameter)
424 dtype = args.parametertypes.get(parameter, "")
425
426 if dtype:
427 self.data += f"{self.lineprefix}``{dtype}``\n"
428 else:
429 self.data += f"{self.lineprefix}``{parameter}``\n"
430
431 self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0))
432
433 self.lineprefix = " "
434 if parameter_name in args.parameterdescs and \
435 args.parameterdescs[parameter_name] != KernelDoc.undescribed:
436
437 self.output_highlight(args.parameterdescs[parameter_name])
438 self.data += "\n"
439 else:
440 self.data += f"{self.lineprefix}*undescribed*\n\n"
441 self.lineprefix = " "
442
443 self.out_section(args)
444 self.lineprefix = oldprefix
445
446 def out_enum(self, fname, name, args):
447
448 oldprefix = self.lineprefix
449 ln = args.declaration_start_line
450
451 self.data += f"\n\n.. c:enum:: {name}\n\n"
452
453 self.print_lineno(ln)
454 self.lineprefix = " "
455 self.output_highlight(args.get('purpose', ''))
456 self.data += "\n"
457
458 self.data += ".. container:: kernelindent\n\n"
459 outer = self.lineprefix + " "
460 self.lineprefix = outer + " "
461 self.data += f"{outer}**Constants**\n\n"
462
463 for parameter in args.parameterlist:
464 self.data += f"{outer}``{parameter}``\n"
465
466 if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed:
467 self.output_highlight(args.parameterdescs[parameter])
468 else:
469 self.data += f"{self.lineprefix}*undescribed*\n\n"
470 self.data += "\n"
471
472 self.lineprefix = oldprefix
473 self.out_section(args)
474
475 def out_typedef(self, fname, name, args):
476
477 oldprefix = self.lineprefix
478 ln = args.declaration_start_line
479
480 self.data += f"\n\n.. c:type:: {name}\n\n"
481
482 self.print_lineno(ln)
483 self.lineprefix = " "
484
485 self.output_highlight(args.get('purpose', ''))
486
487 self.data += "\n"
488
489 self.lineprefix = oldprefix
490 self.out_section(args)
491
492 def out_struct(self, fname, name, args):
493
494 purpose = args.get('purpose', "")
495 declaration = args.get('definition', "")
496 dtype = args.type
497 ln = args.declaration_start_line
498
499 self.data += f"\n\n.. c:{dtype}:: {name}\n\n"
500
501 self.print_lineno(ln)
502
503 oldprefix = self.lineprefix
504 self.lineprefix += " "
505
506 self.output_highlight(purpose)
507 self.data += "\n"
508
509 self.data += ".. container:: kernelindent\n\n"
510 self.data += f"{self.lineprefix}**Definition**::\n\n"
511
512 self.lineprefix = self.lineprefix + " "
513
514 declaration = declaration.replace("\t", self.lineprefix)
515
516 self.data += f"{self.lineprefix}{dtype} {name}" + ' {' + "\n"
517 self.data += f"{declaration}{self.lineprefix}" + "};\n\n"
518
519 self.lineprefix = " "
520 self.data += f"{self.lineprefix}**Members**\n\n"
521 for parameter in args.parameterlist:
522 if not parameter or parameter.startswith("#"):
523 continue
524
525 parameter_name = parameter.split("[", maxsplit=1)[0]
526
527 if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed:
528 continue
529
530 self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0))
531
532 self.data += f"{self.lineprefix}``{parameter}``\n"
533
534 self.lineprefix = " "
535 self.output_highlight(args.parameterdescs[parameter_name])
536 self.lineprefix = " "
537
538 self.data += "\n"
539
540 self.data += "\n"
541
542 self.lineprefix = oldprefix
543 self.out_section(args)
544
545
546class ManFormat(OutputFormat):
547 """Consts and functions used by man pages output"""
548
549 highlights = (
550 (type_constant, r"\1"),
551 (type_constant2, r"\1"),
552 (type_func, r"\\fB\1\\fP"),
553 (type_enum, r"\\fI\1\\fP"),
554 (type_struct, r"\\fI\1\\fP"),
555 (type_typedef, r"\\fI\1\\fP"),
556 (type_union, r"\\fI\1\\fP"),
557 (type_param, r"\\fI\1\\fP"),
558 (type_param_ref, r"\\fI\1\2\\fP"),
559 (type_member, r"\\fI\1\2\3\\fP"),
560 (type_fallback, r"\\fI\1\\fP")
561 )
562 blankline = ""
563
564 date_formats = [
565 "%a %b %d %H:%M:%S %Z %Y",
566 "%a %b %d %H:%M:%S %Y",
567 "%Y-%m-%d",
568 "%b %d %Y",
569 "%B %d %Y",
570 "%m %d %Y",
571 ]
572
573 def __init__(self, modulename):
574 """
575 Creates class variables.
576
577 Not really mandatory, but it is a good coding style and makes
578 pylint happy.
579 """
580
581 super().__init__()
582 self.modulename = modulename
583 self.symbols = []
584
585 dt = None
586 tstamp = os.environ.get("KBUILD_BUILD_TIMESTAMP")
587 if tstamp:
588 for fmt in self.date_formats:
589 try:
590 dt = datetime.strptime(tstamp, fmt)
591 break
592 except ValueError:
593 pass
594
595 if not dt:
596 dt = datetime.now()
597
598 self.man_date = dt.strftime("%B %Y")
599
600 def arg_name(self, args, name):
601 """
602 Return the name that will be used for the man page.
603
604 As we may have the same name on different namespaces,
605 prepend the data type for all types except functions and typedefs.
606
607 The doc section is special: it uses the modulename.
608 """
609
610 dtype = args.type
611
612 if dtype == "doc":
613 return self.modulename
614
615 if dtype in ["function", "typedef"]:
616 return name
617
618 return f"{dtype} {name}"
619
620 def set_symbols(self, symbols):
621 """
622 Get a list of all symbols from kernel_doc.
623
624 Man pages will uses it to add a SEE ALSO section with other
625 symbols at the same file.
626 """
627 self.symbols = symbols
628
629 def out_tail(self, fname, name, args):
630 """Adds a tail for all man pages"""
631
632 # SEE ALSO section
633 self.data += f'.SH "SEE ALSO"' + "\n.PP\n"
634 self.data += (f"Kernel file \\fB{args.fname}\\fR\n")
635 if len(self.symbols) >= 2:
636 cur_name = self.arg_name(args, name)
637
638 related = []
639 for arg in self.symbols:
640 out_name = self.arg_name(arg, arg.name)
641
642 if cur_name == out_name:
643 continue
644
645 related.append(f"\\fB{out_name}\\fR(9)")
646
647 self.data += ",\n".join(related) + "\n"
648
649 # TODO: does it make sense to add other sections? Maybe
650 # REPORTING ISSUES? LICENSE?
651
652 def msg(self, fname, name, args):
653 """
654 Handles a single entry from kernel-doc parser.
655
656 Add a tail at the end of man pages output.
657 """
658 super().msg(fname, name, args)
659 self.out_tail(fname, name, args)
660
661 return self.data
662
663 def output_highlight(self, block):
664 """
665 Outputs a C symbol that may require being highlighted with
666 self.highlights variable using troff syntax
667 """
668
669 contents = self.highlight_block(block)
670
671 if isinstance(contents, list):
672 contents = "\n".join(contents)
673
674 for line in contents.strip("\n").split("\n"):
675 line = KernRe(r"^\s*").sub("", line)
676 if not line:
677 continue
678
679 if line[0] == ".":
680 self.data += "\\&" + line + "\n"
681 else:
682 self.data += line + "\n"
683
684 def out_doc(self, fname, name, args):
685 if not self.check_doc(name, args):
686 return
687
688 out_name = self.arg_name(args, name)
689
690 self.data += f'.TH "{self.modulename}" 9 "{out_name}" "{self.man_date}" "API Manual" LINUX' + "\n"
691
692 for section, text in args.sections.items():
693 self.data += f'.SH "{section}"' + "\n"
694 self.output_highlight(text)
695
696 def out_function(self, fname, name, args):
697 """output function in man"""
698
699 out_name = self.arg_name(args, name)
700
701 self.data += f'.TH "{name}" 9 "{out_name}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n"
702
703 self.data += ".SH NAME\n"
704 self.data += f"{name} \\- {args['purpose']}\n"
705
706 self.data += ".SH SYNOPSIS\n"
707 if args.get('functiontype', ''):
708 self.data += f'.B "{args["functiontype"]}" {name}' + "\n"
709 else:
710 self.data += f'.B "{name}' + "\n"
711
712 count = 0
713 parenth = "("
714 post = ","
715
716 for parameter in args.parameterlist:
717 if count == len(args.parameterlist) - 1:
718 post = ");"
719
720 dtype = args.parametertypes.get(parameter, "")
721 if function_pointer.match(dtype):
722 # Pointer-to-function
723 self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n"
724 else:
725 dtype = KernRe(r'([^\*])$').sub(r'\1 ', dtype)
726
727 self.data += f'.BI "{parenth}{dtype}" "{post}"' + "\n"
728 count += 1
729 parenth = ""
730
731 if args.parameterlist:
732 self.data += ".SH ARGUMENTS\n"
733
734 for parameter in args.parameterlist:
735 parameter_name = re.sub(r'\[.*', '', parameter)
736
737 self.data += f'.IP "{parameter}" 12' + "\n"
738 self.output_highlight(args.parameterdescs.get(parameter_name, ""))
739
740 for section, text in args.sections.items():
741 self.data += f'.SH "{section.upper()}"' + "\n"
742 self.output_highlight(text)
743
744 def out_enum(self, fname, name, args):
745 out_name = self.arg_name(args, name)
746
747 self.data += f'.TH "{self.modulename}" 9 "{out_name}" "{self.man_date}" "API Manual" LINUX' + "\n"
748
749 self.data += ".SH NAME\n"
750 self.data += f"enum {name} \\- {args['purpose']}\n"
751
752 self.data += ".SH SYNOPSIS\n"
753 self.data += f"enum {name}" + " {\n"
754
755 count = 0
756 for parameter in args.parameterlist:
757 self.data += f'.br\n.BI " {parameter}"' + "\n"
758 if count == len(args.parameterlist) - 1:
759 self.data += "\n};\n"
760 else:
761 self.data += ", \n.br\n"
762
763 count += 1
764
765 self.data += ".SH Constants\n"
766
767 for parameter in args.parameterlist:
768 parameter_name = KernRe(r'\[.*').sub('', parameter)
769 self.data += f'.IP "{parameter}" 12' + "\n"
770 self.output_highlight(args.parameterdescs.get(parameter_name, ""))
771
772 for section, text in args.sections.items():
773 self.data += f'.SH "{section}"' + "\n"
774 self.output_highlight(text)
775
776 def out_typedef(self, fname, name, args):
777 module = self.modulename
778 purpose = args.get('purpose')
779 out_name = self.arg_name(args, name)
780
781 self.data += f'.TH "{module}" 9 "{out_name}" "{self.man_date}" "API Manual" LINUX' + "\n"
782
783 self.data += ".SH NAME\n"
784 self.data += f"typedef {name} \\- {purpose}\n"
785
786 for section, text in args.sections.items():
787 self.data += f'.SH "{section}"' + "\n"
788 self.output_highlight(text)
789
790 def out_struct(self, fname, name, args):
791 module = self.modulename
792 purpose = args.get('purpose')
793 definition = args.get('definition')
794 out_name = self.arg_name(args, name)
795
796 self.data += f'.TH "{module}" 9 "{out_name}" "{self.man_date}" "API Manual" LINUX' + "\n"
797
798 self.data += ".SH NAME\n"
799 self.data += f"{args.type} {name} \\- {purpose}\n"
800
801 # Replace tabs with two spaces and handle newlines
802 declaration = definition.replace("\t", " ")
803 declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration)
804
805 self.data += ".SH SYNOPSIS\n"
806 self.data += f"{args.type} {name} " + "{" + "\n.br\n"
807 self.data += f'.BI "{declaration}\n' + "};\n.br\n\n"
808
809 self.data += ".SH Members\n"
810 for parameter in args.parameterlist:
811 if parameter.startswith("#"):
812 continue
813
814 parameter_name = re.sub(r"\[.*", "", parameter)
815
816 if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed:
817 continue
818
819 self.data += f'.IP "{parameter}" 12' + "\n"
820 self.output_highlight(args.parameterdescs.get(parameter_name))
821
822 for section, text in args.sections.items():
823 self.data += f'.SH "{section}"' + "\n"
824 self.output_highlight(text)