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