kdoc_output.py 27 KB

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