Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

scripts/kernel-doc.py: move output classes to a separate file

In preparation for letting kerneldoc Sphinx extension to import
Python libraries, move kernel-doc output logic to a separate file.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Signed-off-by: Jonathan Corbet <corbet@lwn.net>
Link: https://lore.kernel.org/r/81087eff25d11c265019a8631f7fc8d3904795d0.1744106242.git.mchehab+huawei@kernel.org

authored by

Mauro Carvalho Chehab and committed by
Jonathan Corbet
1d6fea64 ee13b3f3

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