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