xref: /linux/scripts/kernel-doc (revision e31fd36da9c41f9f664e51a35860e9f606e81ef4)
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=R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0917,R1702
6# pylint: disable=C0302,C0103,C0301
7# pylint: disable=C0116,C0115,W0511,W0613
8#
9# Converted from the kernel-doc script originally written in Perl
10# under GPLv2, copyrighted since 1998 by the following authors:
11#
12#    Aditya Srivastava <yashsri421@gmail.com>
13#    Akira Yokosawa <akiyks@gmail.com>
14#    Alexander A. Klimov <grandmaster@al2klimov.de>
15#    Alexander Lobakin <aleksander.lobakin@intel.com>
16#    André Almeida <andrealmeid@igalia.com>
17#    Andy Shevchenko <andriy.shevchenko@linux.intel.com>
18#    Anna-Maria Behnsen <anna-maria@linutronix.de>
19#    Armin Kuster <akuster@mvista.com>
20#    Bart Van Assche <bart.vanassche@sandisk.com>
21#    Ben Hutchings <ben@decadent.org.uk>
22#    Borislav Petkov <bbpetkov@yahoo.de>
23#    Chen-Yu Tsai <wenst@chromium.org>
24#    Coco Li <lixiaoyan@google.com>
25#    Conchúr Navid <conchur@web.de>
26#    Daniel Santos <daniel.santos@pobox.com>
27#    Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk>
28#    Dan Luedtke <mail@danrl.de>
29#    Donald Hunter <donald.hunter@gmail.com>
30#    Gabriel Krisman Bertazi <krisman@collabora.co.uk>
31#    Greg Kroah-Hartman <gregkh@linuxfoundation.org>
32#    Harvey Harrison <harvey.harrison@gmail.com>
33#    Horia Geanta <horia.geanta@freescale.com>
34#    Ilya Dryomov <idryomov@gmail.com>
35#    Jakub Kicinski <kuba@kernel.org>
36#    Jani Nikula <jani.nikula@intel.com>
37#    Jason Baron <jbaron@redhat.com>
38#    Jason Gunthorpe <jgg@nvidia.com>
39#    Jérémy Bobbio <lunar@debian.org>
40#    Johannes Berg <johannes.berg@intel.com>
41#    Johannes Weiner <hannes@cmpxchg.org>
42#    Jonathan Cameron <Jonathan.Cameron@huawei.com>
43#    Jonathan Corbet <corbet@lwn.net>
44#    Jonathan Neuschäfer <j.neuschaefer@gmx.net>
45#    Kamil Rytarowski <n54@gmx.com>
46#    Kees Cook <kees@kernel.org>
47#    Laurent Pinchart <laurent.pinchart@ideasonboard.com>
48#    Levin, Alexander (Sasha Levin) <alexander.levin@verizon.com>
49#    Linus Torvalds <torvalds@linux-foundation.org>
50#    Lucas De Marchi <lucas.demarchi@profusion.mobi>
51#    Mark Rutland <mark.rutland@arm.com>
52#    Markus Heiser <markus.heiser@darmarit.de>
53#    Martin Waitz <tali@admingilde.org>
54#    Masahiro Yamada <masahiroy@kernel.org>
55#    Matthew Wilcox <willy@infradead.org>
56#    Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
57#    Michal Wajdeczko <michal.wajdeczko@intel.com>
58#    Michael Zucchi
59#    Mike Rapoport <rppt@linux.ibm.com>
60#    Niklas Söderlund <niklas.soderlund@corigine.com>
61#    Nishanth Menon <nm@ti.com>
62#    Paolo Bonzini <pbonzini@redhat.com>
63#    Pavan Kumar Linga <pavan.kumar.linga@intel.com>
64#    Pavel Pisa <pisa@cmp.felk.cvut.cz>
65#    Peter Maydell <peter.maydell@linaro.org>
66#    Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
67#    Randy Dunlap <rdunlap@infradead.org>
68#    Richard Kennedy <richard@rsk.demon.co.uk>
69#    Rich Walker <rw@shadow.org.uk>
70#    Rolf Eike Beer <eike-kernel@sf-tec.de>
71#    Sakari Ailus <sakari.ailus@linux.intel.com>
72#    Silvio Fricke <silvio.fricke@gmail.com>
73#    Simon Huggins
74#    Tim Waugh <twaugh@redhat.com>
75#    Tomasz Warniełło <tomasz.warniello@gmail.com>
76#    Utkarsh Tripathi <utripathi2002@gmail.com>
77#    valdis.kletnieks@vt.edu <valdis.kletnieks@vt.edu>
78#    Vegard Nossum <vegard.nossum@oracle.com>
79#    Will Deacon <will.deacon@arm.com>
80#    Yacine Belkadi <yacine.belkadi.1@gmail.com>
81#    Yujie Liu <yujie.liu@intel.com>
82
83# TODO: implement warning filtering
84
85"""
86kernel_doc
87==========
88
89Print formatted kernel documentation to stdout
90
91Read C language source or header FILEs, extract embedded
92documentation comments, and print formatted documentation
93to standard output.
94
95The documentation comments are identified by the "/**"
96opening comment mark.
97
98See Documentation/doc-guide/kernel-doc.rst for the
99documentation comment syntax.
100"""
101
102import argparse
103import logging
104import os
105import re
106import sys
107
108from datetime import datetime
109from pprint import pformat
110
111from dateutil import tz
112
113# Import Python modules
114
115LIB_DIR = "lib/kdoc"
116SRC_DIR = os.path.dirname(os.path.realpath(__file__))
117
118sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
119
120from kdoc_re import Re, NestedMatch
121
122
123#
124# Regular expressions used to parse kernel-doc markups at KernelDoc class.
125#
126# Let's declare them in lowercase outside any class to make easier to
127# convert from the python script.
128#
129# As those are evaluated at the beginning, no need to cache them
130#
131
132
133# Allow whitespace at end of comment start.
134doc_start = Re(r'^/\*\*\s*$', cache=False)
135
136doc_end = Re(r'\*/', cache=False)
137doc_com = Re(r'\s*\*\s*', cache=False)
138doc_com_body = Re(r'\s*\* ?', cache=False)
139doc_decl = doc_com + Re(r'(\w+)', cache=False)
140
141# @params and a strictly limited set of supported section names
142# Specifically:
143#   Match @word:
144#         @...:
145#         @{section-name}:
146# while trying to not match literal block starts like "example::"
147#
148doc_sect = doc_com + \
149            Re(r'\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$',
150                flags=re.I, cache=False)
151
152doc_content = doc_com_body + Re(r'(.*)', cache=False)
153doc_block = doc_com + Re(r'DOC:\s*(.*)?', cache=False)
154doc_inline_start = Re(r'^\s*/\*\*\s*$', cache=False)
155doc_inline_sect = Re(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False)
156doc_inline_end = Re(r'^\s*\*/\s*$', cache=False)
157doc_inline_oneline = Re(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False)
158function_pointer = Re(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=False)
159attribute = Re(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)",
160               flags=re.I | re.S, cache=False)
161
162# match expressions used to find embedded type information
163type_constant = Re(r"\b``([^\`]+)``\b", cache=False)
164type_constant2 = Re(r"\%([-_*\w]+)", cache=False)
165type_func = Re(r"(\w+)\(\)", cache=False)
166type_param = Re(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False)
167type_param_ref = Re(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False)
168
169# Special RST handling for func ptr params
170type_fp_param = Re(r"\@(\w+)\(\)", cache=False)
171
172# Special RST handling for structs with func ptr params
173type_fp_param2 = Re(r"\@(\w+->\S+)\(\)", cache=False)
174
175type_env = Re(r"(\$\w+)", cache=False)
176type_enum = Re(r"\&(enum\s*([_\w]+))", cache=False)
177type_struct = Re(r"\&(struct\s*([_\w]+))", cache=False)
178type_typedef = Re(r"\&(typedef\s*([_\w]+))", cache=False)
179type_union = Re(r"\&(union\s*([_\w]+))", cache=False)
180type_member = Re(r"\&([_\w]+)(\.|->)([_\w]+)", cache=False)
181type_fallback = Re(r"\&([_\w]+)", cache=False)
182type_member_func = type_member + Re(r"\(\)", cache=False)
183
184export_symbol = Re(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False)
185export_symbol_ns = Re(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False)
186
187class KernelDoc:
188    # Parser states
189    STATE_NORMAL        = 0        # normal code
190    STATE_NAME          = 1        # looking for function name
191    STATE_BODY_MAYBE    = 2        # body - or maybe more description
192    STATE_BODY          = 3        # the body of the comment
193    STATE_BODY_WITH_BLANK_LINE = 4 # the body which has a blank line
194    STATE_PROTO         = 5        # scanning prototype
195    STATE_DOCBLOCK      = 6        # documentation block
196    STATE_INLINE        = 7        # gathering doc outside main block
197
198    st_name = [
199        "NORMAL",
200        "NAME",
201        "BODY_MAYBE",
202        "BODY",
203        "BODY_WITH_BLANK_LINE",
204        "PROTO",
205        "DOCBLOCK",
206        "INLINE",
207    ]
208
209    # Inline documentation state
210    STATE_INLINE_NA     = 0 # not applicable ($state != STATE_INLINE)
211    STATE_INLINE_NAME   = 1 # looking for member name (@foo:)
212    STATE_INLINE_TEXT   = 2 # looking for member documentation
213    STATE_INLINE_END    = 3 # done
214    STATE_INLINE_ERROR  = 4 # error - Comment without header was found.
215                            # Spit a warning as it's not
216                            # proper kernel-doc and ignore the rest.
217
218    st_inline_name = [
219        "",
220        "_NAME",
221        "_TEXT",
222        "_END",
223        "_ERROR",
224    ]
225
226    # Section names
227
228    section_default = "Description"  # default section
229    section_intro = "Introduction"
230    section_context = "Context"
231    section_return = "Return"
232
233    undescribed = "-- undescribed --"
234
235    def __init__(self, config, fname):
236        """Initialize internal variables"""
237
238        self.fname = fname
239        self.config = config
240
241        # Initial state for the state machines
242        self.state = self.STATE_NORMAL
243        self.inline_doc_state = self.STATE_INLINE_NA
244
245        # Store entry currently being processed
246        self.entry = None
247
248        # Place all potential outputs into an array
249        self.entries = []
250
251    def show_warnings(self, dtype, declaration_name):
252        # TODO: implement it
253
254        return True
255
256    # TODO: rename to emit_message
257    def emit_warning(self, ln, msg, warning=True):
258        """Emit a message"""
259
260        if warning:
261            self.config.log.warning("%s:%d %s", self.fname, ln, msg)
262        else:
263            self.config.log.info("%s:%d %s", self.fname, ln, msg)
264
265    def dump_section(self, start_new=True):
266        """
267        Dumps section contents to arrays/hashes intended for that purpose.
268        """
269
270        name = self.entry.section
271        contents = self.entry.contents
272
273        # TODO: we can prevent dumping empty sections here with:
274        #
275        #    if self.entry.contents.strip("\n"):
276        #       if start_new:
277        #           self.entry.section = self.section_default
278        #           self.entry.contents = ""
279        #
280        #        return
281        #
282        # But, as we want to be producing the same output of the
283        # venerable kernel-doc Perl tool, let's just output everything,
284        # at least for now
285
286        if type_param.match(name):
287            name = type_param.group(1)
288
289            self.entry.parameterdescs[name] = contents
290            self.entry.parameterdesc_start_lines[name] = self.entry.new_start_line
291
292            self.entry.sectcheck += name + " "
293            self.entry.new_start_line = 0
294
295        elif name == "@...":
296            name = "..."
297            self.entry.parameterdescs[name] = contents
298            self.entry.sectcheck += name + " "
299            self.entry.parameterdesc_start_lines[name] = self.entry.new_start_line
300            self.entry.new_start_line = 0
301
302        else:
303            if name in self.entry.sections and self.entry.sections[name] != "":
304                # Only warn on user-specified duplicate section names
305                if name != self.section_default:
306                    self.emit_warning(self.entry.new_start_line,
307                                      f"duplicate section name '{name}'\n")
308                self.entry.sections[name] += contents
309            else:
310                self.entry.sections[name] = contents
311                self.entry.sectionlist.append(name)
312                self.entry.section_start_lines[name] = self.entry.new_start_line
313                self.entry.new_start_line = 0
314
315#        self.config.log.debug("Section: %s : %s", name, pformat(vars(self.entry)))
316
317        if start_new:
318            self.entry.section = self.section_default
319            self.entry.contents = ""
320
321    # TODO: rename it to store_declaration
322    def output_declaration(self, dtype, name, **args):
323        """
324        Stores the entry into an entry array.
325
326        The actual output and output filters will be handled elsewhere
327        """
328
329        # The implementation here is different than the original kernel-doc:
330        # instead of checking for output filters or actually output anything,
331        # it just stores the declaration content at self.entries, as the
332        # output will happen on a separate class.
333        #
334        # For now, we're keeping the same name of the function just to make
335        # easier to compare the source code of both scripts
336
337        if "declaration_start_line" not in args:
338            args["declaration_start_line"] = self.entry.declaration_start_line
339
340        args["type"] = dtype
341
342        # TODO: use colletions.OrderedDict
343
344        sections = args.get('sections', {})
345        sectionlist = args.get('sectionlist', [])
346
347        # Drop empty sections
348        # TODO: improve it to emit warnings
349        for section in [ "Description", "Return" ]:
350            if section in sectionlist:
351                if not sections[section].rstrip():
352                    del sections[section]
353                    sectionlist.remove(section)
354
355        self.entries.append((name, args))
356
357        self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args))
358
359    def reset_state(self, ln):
360        """
361        Ancillary routine to create a new entry. It initializes all
362        variables used by the state machine.
363        """
364
365        self.entry = argparse.Namespace
366
367        self.entry.contents = ""
368        self.entry.function = ""
369        self.entry.sectcheck = ""
370        self.entry.struct_actual = ""
371        self.entry.prototype = ""
372
373        self.entry.parameterlist = []
374        self.entry.parameterdescs = {}
375        self.entry.parametertypes = {}
376        self.entry.parameterdesc_start_lines = {}
377
378        self.entry.section_start_lines = {}
379        self.entry.sectionlist = []
380        self.entry.sections = {}
381
382        self.entry.anon_struct_union = False
383
384        self.entry.leading_space = None
385
386        # State flags
387        self.state = self.STATE_NORMAL
388        self.inline_doc_state = self.STATE_INLINE_NA
389        self.entry.brcount = 0
390
391        self.entry.in_doc_sect = False
392        self.entry.declaration_start_line = ln
393
394    def push_parameter(self, ln, decl_type, param, dtype,
395                       org_arg, declaration_name):
396        if self.entry.anon_struct_union and dtype == "" and param == "}":
397            return  # Ignore the ending }; from anonymous struct/union
398
399        self.entry.anon_struct_union = False
400
401        param = Re(r'[\[\)].*').sub('', param, count=1)
402
403        if dtype == "" and param.endswith("..."):
404            if Re(r'\w\.\.\.$').search(param):
405                # For named variable parameters of the form `x...`,
406                # remove the dots
407                param = param[:-3]
408            else:
409                # Handles unnamed variable parameters
410                param = "..."
411
412            if param not in self.entry.parameterdescs or \
413                not self.entry.parameterdescs[param]:
414
415                self.entry.parameterdescs[param] = "variable arguments"
416
417        elif dtype == "" and (not param or param == "void"):
418            param = "void"
419            self.entry.parameterdescs[param] = "no arguments"
420
421        elif dtype == "" and param in ["struct", "union"]:
422            # Handle unnamed (anonymous) union or struct
423            dtype = param
424            param = "{unnamed_" + param + "}"
425            self.entry.parameterdescs[param] = "anonymous\n"
426            self.entry.anon_struct_union = True
427
428        # Handle cache group enforcing variables: they do not need
429        # to be described in header files
430        elif "__cacheline_group" in param:
431            # Ignore __cacheline_group_begin and __cacheline_group_end
432            return
433
434        # Warn if parameter has no description
435        # (but ignore ones starting with # as these are not parameters
436        # but inline preprocessor statements)
437        if param not in self.entry.parameterdescs and not param.startswith("#"):
438            self.entry.parameterdescs[param] = self.undescribed
439
440            if self.show_warnings(dtype, declaration_name) and "." not in param:
441                if decl_type == 'function':
442                    dname = f"{decl_type} parameter"
443                else:
444                    dname = f"{decl_type} member"
445
446                self.emit_warning(ln,
447                                  f"{dname} '{param}' not described in '{declaration_name}'")
448
449        # Strip spaces from param so that it is one continuous string on
450        # parameterlist. This fixes a problem where check_sections()
451        # cannot find a parameter like "addr[6 + 2]" because it actually
452        # appears as "addr[6", "+", "2]" on the parameter list.
453        # However, it's better to maintain the param string unchanged for
454        # output, so just weaken the string compare in check_sections()
455        # to ignore "[blah" in a parameter string.
456
457        self.entry.parameterlist.append(param)
458        org_arg = Re(r'\s\s+').sub(' ', org_arg)
459        self.entry.parametertypes[param] = org_arg
460
461    def save_struct_actual(self, actual):
462        """
463        Strip all spaces from the actual param so that it looks like
464        one string item.
465        """
466
467        actual = Re(r'\s*').sub("", actual, count=1)
468
469        self.entry.struct_actual += actual + " "
470
471    def create_parameter_list(self, ln, decl_type, args, splitter, declaration_name):
472
473        # temporarily replace all commas inside function pointer definition
474        arg_expr = Re(r'(\([^\),]+),')
475        while arg_expr.search(args):
476            args = arg_expr.sub(r"\1#", args)
477
478        for arg in args.split(splitter):
479            # Strip comments
480            arg = Re(r'\/\*.*\*\/').sub('', arg)
481
482            # Ignore argument attributes
483            arg = Re(r'\sPOS0?\s').sub(' ', arg)
484
485            # Strip leading/trailing spaces
486            arg = arg.strip()
487            arg = Re(r'\s+').sub(' ', arg, count=1)
488
489            if arg.startswith('#'):
490                # Treat preprocessor directive as a typeless variable just to fill
491                # corresponding data structures "correctly". Catch it later in
492                # output_* subs.
493
494                # Treat preprocessor directive as a typeless variable
495                self.push_parameter(ln, decl_type, arg, "",
496                                    "", declaration_name)
497
498            elif Re(r'\(.+\)\s*\(').search(arg):
499                # Pointer-to-function
500
501                arg = arg.replace('#', ',')
502
503                r = Re(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)')
504                if r.match(arg):
505                    param = r.group(1)
506                else:
507                    self.emit_warning(ln, f"Invalid param: {arg}")
508                    param = arg
509
510                dtype = Re(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg)
511                self.save_struct_actual(param)
512                self.push_parameter(ln, decl_type, param, dtype,
513                                    arg, declaration_name)
514
515            elif Re(r'\(.+\)\s*\[').search(arg):
516                # Array-of-pointers
517
518                arg = arg.replace('#', ',')
519                r = Re(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)')
520                if r.match(arg):
521                    param = r.group(1)
522                else:
523                    self.emit_warning(ln, f"Invalid param: {arg}")
524                    param = arg
525
526                dtype = Re(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg)
527
528                self.save_struct_actual(param)
529                self.push_parameter(ln, decl_type, param, dtype,
530                                    arg, declaration_name)
531
532            elif arg:
533                arg = Re(r'\s*:\s*').sub(":", arg)
534                arg = Re(r'\s*\[').sub('[', arg)
535
536                args = Re(r'\s*,\s*').split(arg)
537                if args[0] and '*' in args[0]:
538                    args[0] = re.sub(r'(\*+)\s*', r' \1', args[0])
539
540                first_arg = []
541                r = Re(r'^(.*\s+)(.*?\[.*\].*)$')
542                if args[0] and r.match(args[0]):
543                    args.pop(0)
544                    first_arg.extend(r.group(1))
545                    first_arg.append(r.group(2))
546                else:
547                    first_arg = Re(r'\s+').split(args.pop(0))
548
549                args.insert(0, first_arg.pop())
550                dtype = ' '.join(first_arg)
551
552                for param in args:
553                    if Re(r'^(\*+)\s*(.*)').match(param):
554                        r = Re(r'^(\*+)\s*(.*)')
555                        if not r.match(param):
556                            self.emit_warning(ln, f"Invalid param: {param}")
557                            continue
558
559                        param = r.group(1)
560
561                        self.save_struct_actual(r.group(2))
562                        self.push_parameter(ln, decl_type, r.group(2),
563                                            f"{dtype} {r.group(1)}",
564                                            arg, declaration_name)
565
566                    elif Re(r'(.*?):(\w+)').search(param):
567                        r = Re(r'(.*?):(\w+)')
568                        if not r.match(param):
569                            self.emit_warning(ln, f"Invalid param: {param}")
570                            continue
571
572                        if dtype != "":  # Skip unnamed bit-fields
573                            self.save_struct_actual(r.group(1))
574                            self.push_parameter(ln, decl_type, r.group(1),
575                                                f"{dtype}:{r.group(2)}",
576                                                arg, declaration_name)
577                    else:
578                        self.save_struct_actual(param)
579                        self.push_parameter(ln, decl_type, param, dtype,
580                                            arg, declaration_name)
581
582    def check_sections(self, ln, decl_name, decl_type, sectcheck, prmscheck):
583        sects = sectcheck.split()
584        prms = prmscheck.split()
585        err = False
586
587        for sx in range(len(sects)):                  # pylint: disable=C0200
588            err = True
589            for px in range(len(prms)):               # pylint: disable=C0200
590                prm_clean = prms[px]
591                prm_clean = Re(r'\[.*\]').sub('', prm_clean)
592                prm_clean = attribute.sub('', prm_clean)
593
594                # ignore array size in a parameter string;
595                # however, the original param string may contain
596                # spaces, e.g.:  addr[6 + 2]
597                # and this appears in @prms as "addr[6" since the
598                # parameter list is split at spaces;
599                # hence just ignore "[..." for the sections check;
600                prm_clean = Re(r'\[.*').sub('', prm_clean)
601
602                if prm_clean == sects[sx]:
603                    err = False
604                    break
605
606            if err:
607                if decl_type == 'function':
608                    dname = f"{decl_type} parameter"
609                else:
610                    dname = f"{decl_type} member"
611
612                self.emit_warning(ln,
613                                  f"Excess {dname} '{sects[sx]}' description in '{decl_name}'")
614
615    def check_return_section(self, ln, declaration_name, return_type):
616
617        if not self.config.wreturn:
618            return
619
620        # Ignore an empty return type (It's a macro)
621        # Ignore functions with a "void" return type (but not "void *")
622        if not return_type or Re(r'void\s*\w*\s*$').search(return_type):
623            return
624
625        if not self.entry.sections.get("Return", None):
626            self.emit_warning(ln,
627                              f"No description found for return value of '{declaration_name}'")
628
629    def dump_struct(self, ln, proto):
630        """
631        Store an entry for an struct or union
632        """
633
634        type_pattern = r'(struct|union)'
635
636        qualifiers = [
637            "__attribute__",
638            "__packed",
639            "__aligned",
640            "____cacheline_aligned_in_smp",
641            "____cacheline_aligned",
642        ]
643
644        definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?"
645        struct_members = Re(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)')
646
647        # Extract struct/union definition
648        members = None
649        declaration_name = None
650        decl_type = None
651
652        r = Re(type_pattern + r'\s+(\w+)\s*' + definition_body)
653        if r.search(proto):
654            decl_type = r.group(1)
655            declaration_name = r.group(2)
656            members = r.group(3)
657        else:
658            r = Re(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;')
659
660            if r.search(proto):
661                decl_type = r.group(1)
662                declaration_name = r.group(3)
663                members = r.group(2)
664
665        if not members:
666            self.emit_warning(ln, f"{proto} error: Cannot parse struct or union!")
667            self.config.errors += 1
668            return
669
670        if self.entry.identifier != declaration_name:
671            self.emit_warning(ln,
672                              f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n")
673            return
674
675        args_pattern =r'([^,)]+)'
676
677        sub_prefixes = [
678            (Re(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I),  ''),
679            (Re(r'\/\*\s*private:.*', re.S| re.I),  ''),
680
681            # Strip comments
682            (Re(r'\/\*.*?\*\/', re.S),  ''),
683
684            # Strip attributes
685            (attribute, ' '),
686            (Re(r'\s*__aligned\s*\([^;]*\)', re.S),  ' '),
687            (Re(r'\s*__counted_by\s*\([^;]*\)', re.S),  ' '),
688            (Re(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S),  ' '),
689            (Re(r'\s*__packed\s*', re.S),  ' '),
690            (Re(r'\s*CRYPTO_MINALIGN_ATTR', re.S),  ' '),
691            (Re(r'\s*____cacheline_aligned_in_smp', re.S),  ' '),
692            (Re(r'\s*____cacheline_aligned', re.S),  ' '),
693
694            # Unwrap struct_group macros based on this definition:
695            # __struct_group(TAG, NAME, ATTRS, MEMBERS...)
696            # which has variants like: struct_group(NAME, MEMBERS...)
697            # Only MEMBERS arguments require documentation.
698            #
699            # Parsing them happens on two steps:
700            #
701            # 1. drop struct group arguments that aren't at MEMBERS,
702            #    storing them as STRUCT_GROUP(MEMBERS)
703            #
704            # 2. remove STRUCT_GROUP() ancillary macro.
705            #
706            # The original logic used to remove STRUCT_GROUP() using an
707            # advanced regex:
708            #
709            #   \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;
710            #
711            # with two patterns that are incompatible with
712            # Python re module, as it has:
713            #
714            #   - a recursive pattern: (?1)
715            #   - an atomic grouping: (?>...)
716            #
717            # I tried a simpler version: but it didn't work either:
718            #   \bSTRUCT_GROUP\(([^\)]+)\)[^;]*;
719            #
720            # As it doesn't properly match the end parenthesis on some cases.
721            #
722            # So, a better solution was crafted: there's now a NestedMatch
723            # class that ensures that delimiters after a search are properly
724            # matched. So, the implementation to drop STRUCT_GROUP() will be
725            # handled in separate.
726
727            (Re(r'\bstruct_group\s*\(([^,]*,)', re.S),  r'STRUCT_GROUP('),
728            (Re(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S),  r'STRUCT_GROUP('),
729            (Re(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S),  r'struct \1 \2; STRUCT_GROUP('),
730            (Re(r'\b__struct_group\s*\(([^,]*,){3}', re.S),  r'STRUCT_GROUP('),
731
732            # Replace macros
733            #
734            # TODO: it is better to also move those to the NestedMatch logic,
735            # to ensure that parenthesis will be properly matched.
736
737            (Re(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S),  r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'),
738            (Re(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S),  r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'),
739            (Re(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S),  r'unsigned long \1[BITS_TO_LONGS(\2)]'),
740            (Re(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S),  r'unsigned long \1[1 << ((\2) - 1)]'),
741            (Re(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S),  r'\2 *\1'),
742            (Re(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S),  r'\2 *\1'),
743            (Re(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S),  r'\1 \2[]'),
744            (Re(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S),  r'dma_addr_t \1'),
745            (Re(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S),  r'__u32 \1'),
746        ]
747
748        # Regexes here are guaranteed to have the end limiter matching
749        # the start delimiter. Yet, right now, only one replace group
750        # is allowed.
751
752        sub_nested_prefixes = [
753            (re.compile(r'\bSTRUCT_GROUP\('),  r'\1'),
754        ]
755
756        for search, sub in sub_prefixes:
757            members = search.sub(sub, members)
758
759        nested = NestedMatch()
760
761        for search, sub in sub_nested_prefixes:
762            members = nested.sub(search, sub, members)
763
764        # Keeps the original declaration as-is
765        declaration = members
766
767        # Split nested struct/union elements
768        #
769        # This loop was simpler at the original kernel-doc perl version, as
770        #   while ($members =~ m/$struct_members/) { ... }
771        # reads 'members' string on each interaction.
772        #
773        # Python behavior is different: it parses 'members' only once,
774        # creating a list of tuples from the first interaction.
775        #
776        # On other words, this won't get nested structs.
777        #
778        # So, we need to have an extra loop on Python to override such
779        # re limitation.
780
781        while True:
782            tuples = struct_members.findall(members)
783            if not tuples:
784                break
785
786            for t in tuples:
787                newmember = ""
788                maintype = t[0]
789                s_ids = t[5]
790                content = t[3]
791
792                oldmember = "".join(t)
793
794                for s_id in s_ids.split(','):
795                    s_id = s_id.strip()
796
797                    newmember += f"{maintype} {s_id}; "
798                    s_id = Re(r'[:\[].*').sub('', s_id)
799                    s_id = Re(r'^\s*\**(\S+)\s*').sub(r'\1', s_id)
800
801                    for arg in content.split(';'):
802                        arg = arg.strip()
803
804                        if not arg:
805                            continue
806
807                        r = Re(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)')
808                        if r.match(arg):
809                            # Pointer-to-function
810                            dtype = r.group(1)
811                            name = r.group(2)
812                            extra = r.group(3)
813
814                            if not name:
815                                continue
816
817                            if not s_id:
818                                # Anonymous struct/union
819                                newmember += f"{dtype}{name}{extra}; "
820                            else:
821                                newmember += f"{dtype}{s_id}.{name}{extra}; "
822
823                        else:
824                            arg = arg.strip()
825                            # Handle bitmaps
826                            arg = Re(r':\s*\d+\s*').sub('', arg)
827
828                            # Handle arrays
829                            arg = Re(r'\[.*\]').sub('', arg)
830
831                            # Handle multiple IDs
832                            arg = Re(r'\s*,\s*').sub(',', arg)
833
834
835                            r = Re(r'(.*)\s+([\S+,]+)')
836
837                            if r.search(arg):
838                                dtype = r.group(1)
839                                names = r.group(2)
840                            else:
841                                newmember += f"{arg}; "
842                                continue
843
844                            for name in names.split(','):
845                                name = Re(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip()
846
847                                if not name:
848                                    continue
849
850                                if not s_id:
851                                    # Anonymous struct/union
852                                    newmember += f"{dtype} {name}; "
853                                else:
854                                    newmember += f"{dtype} {s_id}.{name}; "
855
856                members = members.replace(oldmember, newmember)
857
858        # Ignore other nested elements, like enums
859        members = re.sub(r'(\{[^\{\}]*\})', '', members)
860
861        self.create_parameter_list(ln, decl_type, members, ';',
862                                   declaration_name)
863        self.check_sections(ln, declaration_name, decl_type,
864                            self.entry.sectcheck, self.entry.struct_actual)
865
866        # Adjust declaration for better display
867        declaration = Re(r'([\{;])').sub(r'\1\n', declaration)
868        declaration = Re(r'\}\s+;').sub('};', declaration)
869
870        # Better handle inlined enums
871        while True:
872            r = Re(r'(enum\s+\{[^\}]+),([^\n])')
873            if not r.search(declaration):
874                break
875
876            declaration = r.sub(r'\1,\n\2', declaration)
877
878        def_args = declaration.split('\n')
879        level = 1
880        declaration = ""
881        for clause in def_args:
882
883            clause = clause.strip()
884            clause = Re(r'\s+').sub(' ', clause, count=1)
885
886            if not clause:
887                continue
888
889            if '}' in clause and level > 1:
890                level -= 1
891
892            if not Re(r'^\s*#').match(clause):
893                declaration += "\t" * level
894
895            declaration += "\t" + clause + "\n"
896            if "{" in clause and "}" not in clause:
897                level += 1
898
899        self.output_declaration(decl_type, declaration_name,
900                    struct=declaration_name,
901                    module=self.entry.modulename,
902                    definition=declaration,
903                    parameterlist=self.entry.parameterlist,
904                    parameterdescs=self.entry.parameterdescs,
905                    parametertypes=self.entry.parametertypes,
906                    sectionlist=self.entry.sectionlist,
907                    sections=self.entry.sections,
908                    purpose=self.entry.declaration_purpose)
909
910    def dump_enum(self, ln, proto):
911
912        # Ignore members marked private
913        proto = Re(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto)
914        proto = Re(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto)
915
916        # Strip comments
917        proto = Re(r'\/\*.*?\*\/', flags=re.S).sub('', proto)
918
919        # Strip #define macros inside enums
920        proto = Re(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto)
921
922        members = None
923        declaration_name = None
924
925        r = Re(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;')
926        if r.search(proto):
927            declaration_name = r.group(2)
928            members = r.group(1).rstrip()
929        else:
930            r = Re(r'enum\s+(\w*)\s*\{(.*)\}')
931            if r.match(proto):
932                declaration_name = r.group(1)
933                members = r.group(2).rstrip()
934
935        if not members:
936            self.emit_warning(ln, f"{proto}: error: Cannot parse enum!")
937            self.config.errors += 1
938            return
939
940        if self.entry.identifier != declaration_name:
941            if self.entry.identifier == "":
942                self.emit_warning(ln,
943                                  f"{proto}: wrong kernel-doc identifier on prototype")
944            else:
945                self.emit_warning(ln,
946                                  f"expecting prototype for enum {self.entry.identifier}. Prototype was for enum {declaration_name} instead")
947            return
948
949        if not declaration_name:
950            declaration_name = "(anonymous)"
951
952        member_set = set()
953
954        members = Re(r'\([^;]*?[\)]').sub('', members)
955
956        for arg in members.split(','):
957            if not arg:
958                continue
959            arg = Re(r'^\s*(\w+).*').sub(r'\1', arg)
960            self.entry.parameterlist.append(arg)
961            if arg not in self.entry.parameterdescs:
962                self.entry.parameterdescs[arg] = self.undescribed
963                if self.show_warnings("enum", declaration_name):
964                    self.emit_warning(ln,
965                                      f"Enum value '{arg}' not described in enum '{declaration_name}'")
966            member_set.add(arg)
967
968        for k in self.entry.parameterdescs:
969            if k not in member_set:
970                if self.show_warnings("enum", declaration_name):
971                    self.emit_warning(ln,
972                                      f"Excess enum value '%{k}' description in '{declaration_name}'")
973
974        self.output_declaration('enum', declaration_name,
975                   enum=declaration_name,
976                   module=self.config.modulename,
977                   parameterlist=self.entry.parameterlist,
978                   parameterdescs=self.entry.parameterdescs,
979                   sectionlist=self.entry.sectionlist,
980                   sections=self.entry.sections,
981                   purpose=self.entry.declaration_purpose)
982
983    def dump_declaration(self, ln, prototype):
984        if self.entry.decl_type == "enum":
985            self.dump_enum(ln, prototype)
986            return
987
988        if self.entry.decl_type == "typedef":
989            self.dump_typedef(ln, prototype)
990            return
991
992        if self.entry.decl_type in ["union", "struct"]:
993            self.dump_struct(ln, prototype)
994            return
995
996        # TODO: handle other types
997        self.output_declaration(self.entry.decl_type, prototype,
998                   entry=self.entry)
999
1000    def dump_function(self, ln, prototype):
1001
1002        func_macro = False
1003        return_type = ''
1004        decl_type = 'function'
1005
1006        # Prefixes that would be removed
1007        sub_prefixes = [
1008            (r"^static +", "", 0),
1009            (r"^extern +", "", 0),
1010            (r"^asmlinkage +", "", 0),
1011            (r"^inline +", "", 0),
1012            (r"^__inline__ +", "", 0),
1013            (r"^__inline +", "", 0),
1014            (r"^__always_inline +", "", 0),
1015            (r"^noinline +", "", 0),
1016            (r"^__FORTIFY_INLINE +", "", 0),
1017            (r"__init +", "", 0),
1018            (r"__init_or_module +", "", 0),
1019            (r"__deprecated +", "", 0),
1020            (r"__flatten +", "", 0),
1021            (r"__meminit +", "", 0),
1022            (r"__must_check +", "", 0),
1023            (r"__weak +", "", 0),
1024            (r"__sched +", "", 0),
1025            (r"_noprof", "", 0),
1026            (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0),
1027            (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0),
1028            (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0),
1029            (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0),
1030            (r"__attribute_const__ +", "", 0),
1031
1032            # It seems that Python support for re.X is broken:
1033            # At least for me (Python 3.13), this didn't work
1034#            (r"""
1035#              __attribute__\s*\(\(
1036#                (?:
1037#                    [\w\s]+          # attribute name
1038#                    (?:\([^)]*\))?   # attribute arguments
1039#                    \s*,?            # optional comma at the end
1040#                )+
1041#              \)\)\s+
1042#             """, "", re.X),
1043
1044            # So, remove whitespaces and comments from it
1045            (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0),
1046        ]
1047
1048        for search, sub, flags in sub_prefixes:
1049            prototype = Re(search, flags).sub(sub, prototype)
1050
1051        # Macros are a special case, as they change the prototype format
1052        new_proto = Re(r"^#\s*define\s+").sub("", prototype)
1053        if new_proto != prototype:
1054            is_define_proto = True
1055            prototype = new_proto
1056        else:
1057            is_define_proto = False
1058
1059        # Yes, this truly is vile.  We are looking for:
1060        # 1. Return type (may be nothing if we're looking at a macro)
1061        # 2. Function name
1062        # 3. Function parameters.
1063        #
1064        # All the while we have to watch out for function pointer parameters
1065        # (which IIRC is what the two sections are for), C types (these
1066        # regexps don't even start to express all the possibilities), and
1067        # so on.
1068        #
1069        # If you mess with these regexps, it's a good idea to check that
1070        # the following functions' documentation still comes out right:
1071        # - parport_register_device (function pointer parameters)
1072        # - atomic_set (macro)
1073        # - pci_match_device, __copy_to_user (long return type)
1074
1075        name = r'[a-zA-Z0-9_~:]+'
1076        prototype_end1 = r'[^\(]*'
1077        prototype_end2 = r'[^\{]*'
1078        prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)'
1079
1080        # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group.
1081        # So, this needs to be mapped in Python with (?:...)? or (?:...)+
1082
1083        type1 = r'(?:[\w\s]+)?'
1084        type2 = r'(?:[\w\s]+\*+)+'
1085
1086        found = False
1087
1088        if is_define_proto:
1089            r = Re(r'^()(' + name + r')\s+')
1090
1091            if r.search(prototype):
1092                return_type = ''
1093                declaration_name = r.group(2)
1094                func_macro = True
1095
1096                found = True
1097
1098        if not found:
1099            patterns = [
1100                rf'^()({name})\s*{prototype_end}',
1101                rf'^({type1})\s+({name})\s*{prototype_end}',
1102                rf'^({type2})\s*({name})\s*{prototype_end}',
1103            ]
1104
1105            for p in patterns:
1106                r = Re(p)
1107
1108                if r.match(prototype):
1109
1110                    return_type = r.group(1)
1111                    declaration_name = r.group(2)
1112                    args = r.group(3)
1113
1114                    self.create_parameter_list(ln, decl_type, args, ',',
1115                                               declaration_name)
1116
1117                    found = True
1118                    break
1119        if not found:
1120            self.emit_warning(ln,
1121                              f"cannot understand function prototype: '{prototype}'")
1122            return
1123
1124        if self.entry.identifier != declaration_name:
1125            self.emit_warning(ln,
1126                              f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead")
1127            return
1128
1129        prms = " ".join(self.entry.parameterlist)
1130        self.check_sections(ln, declaration_name, "function",
1131                            self.entry.sectcheck, prms)
1132
1133        self.check_return_section(ln, declaration_name, return_type)
1134
1135        if 'typedef' in return_type:
1136            self.output_declaration(decl_type, declaration_name,
1137                       function=declaration_name,
1138                       typedef=True,
1139                       module=self.config.modulename,
1140                       functiontype=return_type,
1141                       parameterlist=self.entry.parameterlist,
1142                       parameterdescs=self.entry.parameterdescs,
1143                       parametertypes=self.entry.parametertypes,
1144                       sectionlist=self.entry.sectionlist,
1145                       sections=self.entry.sections,
1146                       purpose=self.entry.declaration_purpose,
1147                       func_macro=func_macro)
1148        else:
1149            self.output_declaration(decl_type, declaration_name,
1150                       function=declaration_name,
1151                       typedef=False,
1152                       module=self.config.modulename,
1153                       functiontype=return_type,
1154                       parameterlist=self.entry.parameterlist,
1155                       parameterdescs=self.entry.parameterdescs,
1156                       parametertypes=self.entry.parametertypes,
1157                       sectionlist=self.entry.sectionlist,
1158                       sections=self.entry.sections,
1159                       purpose=self.entry.declaration_purpose,
1160                       func_macro=func_macro)
1161
1162    def dump_typedef(self, ln, proto):
1163        typedef_type = r'((?:\s+[\w\*]+\b){1,8})\s*'
1164        typedef_ident = r'\*?\s*(\w\S+)\s*'
1165        typedef_args = r'\s*\((.*)\);'
1166
1167        typedef1 = Re(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args)
1168        typedef2 = Re(r'typedef' + typedef_type + typedef_ident + typedef_args)
1169
1170        # Strip comments
1171        proto = Re(r'/\*.*?\*/', flags=re.S).sub('', proto)
1172
1173        # Parse function typedef prototypes
1174        for r in [typedef1, typedef2]:
1175            if not r.match(proto):
1176                continue
1177
1178            return_type = r.group(1).strip()
1179            declaration_name = r.group(2)
1180            args = r.group(3)
1181
1182            if self.entry.identifier != declaration_name:
1183                self.emit_warning(ln,
1184                                  f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n")
1185                return
1186
1187            decl_type = 'function'
1188            self.create_parameter_list(ln, decl_type, args, ',', declaration_name)
1189
1190            self.output_declaration(decl_type, declaration_name,
1191                       function=declaration_name,
1192                       typedef=True,
1193                       module=self.entry.modulename,
1194                       functiontype=return_type,
1195                       parameterlist=self.entry.parameterlist,
1196                       parameterdescs=self.entry.parameterdescs,
1197                       parametertypes=self.entry.parametertypes,
1198                       sectionlist=self.entry.sectionlist,
1199                       sections=self.entry.sections,
1200                       purpose=self.entry.declaration_purpose)
1201            return
1202
1203        # Handle nested parentheses or brackets
1204        r = Re(r'(\(*.\)\s*|\[*.\]\s*);$')
1205        while r.search(proto):
1206            proto = r.sub('', proto)
1207
1208        # Parse simple typedefs
1209        r = Re(r'typedef.*\s+(\w+)\s*;')
1210        if r.match(proto):
1211            declaration_name = r.group(1)
1212
1213            if self.entry.identifier != declaration_name:
1214                self.emit_warning(ln, f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n")
1215                return
1216
1217            self.output_declaration('typedef', declaration_name,
1218                       typedef=declaration_name,
1219                       module=self.entry.modulename,
1220                       sectionlist=self.entry.sectionlist,
1221                       sections=self.entry.sections,
1222                       purpose=self.entry.declaration_purpose)
1223            return
1224
1225        self.emit_warning(ln, "error: Cannot parse typedef!")
1226        self.config.errors += 1
1227
1228    @staticmethod
1229    def process_export(function_table, line):
1230        """
1231        process EXPORT_SYMBOL* tags
1232
1233        This method is called both internally and externally, so, it
1234        doesn't use self.
1235        """
1236
1237        if export_symbol.search(line):
1238            symbol = export_symbol.group(2)
1239            function_table.add(symbol)
1240
1241        if export_symbol_ns.search(line):
1242            symbol = export_symbol_ns.group(2)
1243            function_table.add(symbol)
1244
1245    def process_normal(self, ln, line):
1246        """
1247        STATE_NORMAL: looking for the /** to begin everything.
1248        """
1249
1250        if not doc_start.match(line):
1251            return
1252
1253        # start a new entry
1254        self.reset_state(ln + 1)
1255        self.entry.in_doc_sect = False
1256
1257        # next line is always the function name
1258        self.state = self.STATE_NAME
1259
1260    def process_name(self, ln, line):
1261        """
1262        STATE_NAME: Looking for the "name - description" line
1263        """
1264
1265        if doc_block.search(line):
1266            self.entry.new_start_line = ln
1267
1268            if not doc_block.group(1):
1269                self.entry.section = self.section_intro
1270            else:
1271                self.entry.section = doc_block.group(1)
1272
1273            self.state = self.STATE_DOCBLOCK
1274            return
1275
1276        if doc_decl.search(line):
1277            self.entry.identifier = doc_decl.group(1)
1278            self.entry.is_kernel_comment = False
1279
1280            decl_start = str(doc_com)       # comment block asterisk
1281            fn_type = r"(?:\w+\s*\*\s*)?"  # type (for non-functions)
1282            parenthesis = r"(?:\(\w*\))?"   # optional parenthesis on function
1283            decl_end = r"(?:[-:].*)"         # end of the name part
1284
1285            # test for pointer declaration type, foo * bar() - desc
1286            r = Re(fr"^{decl_start}([\w\s]+?){parenthesis}?\s*{decl_end}?$")
1287            if r.search(line):
1288                self.entry.identifier = r.group(1)
1289
1290            # Test for data declaration
1291            r = Re(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)")
1292            if r.search(line):
1293                self.entry.decl_type = r.group(1)
1294                self.entry.identifier = r.group(2)
1295                self.entry.is_kernel_comment = True
1296            else:
1297                # Look for foo() or static void foo() - description;
1298                # or misspelt identifier
1299
1300                r1 = Re(fr"^{decl_start}{fn_type}(\w+)\s*{parenthesis}\s*{decl_end}?$")
1301                r2 = Re(fr"^{decl_start}{fn_type}(\w+[^-:]*){parenthesis}\s*{decl_end}$")
1302
1303                for r in [r1, r2]:
1304                    if r.search(line):
1305                        self.entry.identifier = r.group(1)
1306                        self.entry.decl_type = "function"
1307
1308                        r = Re(r"define\s+")
1309                        self.entry.identifier = r.sub("", self.entry.identifier)
1310                        self.entry.is_kernel_comment = True
1311                        break
1312
1313            self.entry.identifier = self.entry.identifier.strip(" ")
1314
1315            self.state = self.STATE_BODY
1316
1317            # if there's no @param blocks need to set up default section here
1318            self.entry.section = self.section_default
1319            self.entry.new_start_line = ln + 1
1320
1321            r = Re("[-:](.*)")
1322            if r.search(line):
1323                # strip leading/trailing/multiple spaces
1324                self.entry.descr = r.group(1).strip(" ")
1325
1326                r = Re(r"\s+")
1327                self.entry.descr = r.sub(" ", self.entry.descr)
1328                self.entry.declaration_purpose = self.entry.descr
1329                self.state = self.STATE_BODY_MAYBE
1330            else:
1331                self.entry.declaration_purpose = ""
1332
1333            if not self.entry.is_kernel_comment:
1334                self.emit_warning(ln,
1335                                  f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}")
1336                self.state = self.STATE_NORMAL
1337
1338            if not self.entry.declaration_purpose and self.config.wshort_desc:
1339                self.emit_warning(ln,
1340                                  f"missing initial short description on line:\n{line}")
1341
1342            if not self.entry.identifier and self.entry.decl_type != "enum":
1343                self.emit_warning(ln,
1344                                  f"wrong kernel-doc identifier on line:\n{line}")
1345                self.state = self.STATE_NORMAL
1346
1347            if self.config.verbose:
1348                self.emit_warning(ln,
1349                                  f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}",
1350                             warning=False)
1351
1352            return
1353
1354        # Failed to find an identifier. Emit a warning
1355        self.emit_warning(ln, f"Cannot find identifier on line:\n{line}")
1356
1357    def process_body(self, ln, line):
1358        """
1359        STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment.
1360        """
1361
1362        if self.state == self.STATE_BODY_WITH_BLANK_LINE:
1363            r = Re(r"\s*\*\s?\S")
1364            if r.match(line):
1365                self.dump_section()
1366                self.entry.section = self.section_default
1367                self.entry.new_start_line = line
1368                self.entry.contents = ""
1369
1370        if doc_sect.search(line):
1371            self.entry.in_doc_sect = True
1372            newsection = doc_sect.group(1)
1373
1374            if newsection.lower() in ["description", "context"]:
1375                newsection = newsection.title()
1376
1377            # Special case: @return is a section, not a param description
1378            if newsection.lower() in ["@return", "@returns",
1379                                    "return", "returns"]:
1380                newsection = "Return"
1381
1382            # Perl kernel-doc has a check here for contents before sections.
1383            # the logic there is always false, as in_doc_sect variable is
1384            # always true. So, just don't implement Wcontents_before_sections
1385
1386            # .title()
1387            newcontents = doc_sect.group(2)
1388            if not newcontents:
1389                newcontents = ""
1390
1391            if self.entry.contents.strip("\n"):
1392                self.dump_section()
1393
1394            self.entry.new_start_line = ln
1395            self.entry.section = newsection
1396            self.entry.leading_space = None
1397
1398            self.entry.contents = newcontents.lstrip()
1399            if self.entry.contents:
1400                self.entry.contents += "\n"
1401
1402            self.state = self.STATE_BODY
1403            return
1404
1405        if doc_end.search(line):
1406            self.dump_section()
1407
1408            # Look for doc_com + <text> + doc_end:
1409            r = Re(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/')
1410            if r.match(line):
1411                self.emit_warning(ln, f"suspicious ending line: {line}")
1412
1413            self.entry.prototype = ""
1414            self.entry.new_start_line = ln + 1
1415
1416            self.state = self.STATE_PROTO
1417            return
1418
1419        if doc_content.search(line):
1420            cont = doc_content.group(1)
1421
1422            if cont == "":
1423                if self.entry.section == self.section_context:
1424                    self.dump_section()
1425
1426                    self.entry.new_start_line = ln
1427                    self.state = self.STATE_BODY
1428                else:
1429                    if self.entry.section != self.section_default:
1430                        self.state = self.STATE_BODY_WITH_BLANK_LINE
1431                    else:
1432                        self.state = self.STATE_BODY
1433
1434                    self.entry.contents += "\n"
1435
1436            elif self.state == self.STATE_BODY_MAYBE:
1437
1438                # Continued declaration purpose
1439                self.entry.declaration_purpose = self.entry.declaration_purpose.rstrip()
1440                self.entry.declaration_purpose += " " + cont
1441
1442                r = Re(r"\s+")
1443                self.entry.declaration_purpose = r.sub(' ',
1444                                                       self.entry.declaration_purpose)
1445
1446            else:
1447                if self.entry.section.startswith('@') or        \
1448                   self.entry.section == self.section_context:
1449                    if self.entry.leading_space is None:
1450                        r = Re(r'^(\s+)')
1451                        if r.match(cont):
1452                            self.entry.leading_space = len(r.group(1))
1453                        else:
1454                            self.entry.leading_space = 0
1455
1456                    # Double-check if leading space are realy spaces
1457                    pos = 0
1458                    for i in range(0, self.entry.leading_space):
1459                        if cont[i] != " ":
1460                            break
1461                        pos += 1
1462
1463                    cont = cont[pos:]
1464
1465                    # NEW LOGIC:
1466                    # In case it is different, update it
1467                    if self.entry.leading_space != pos:
1468                        self.entry.leading_space = pos
1469
1470                self.entry.contents += cont + "\n"
1471            return
1472
1473        # Unknown line, ignore
1474        self.emit_warning(ln, f"bad line: {line}")
1475
1476    def process_inline(self, ln, line):
1477        """STATE_INLINE: docbook comments within a prototype."""
1478
1479        if self.inline_doc_state == self.STATE_INLINE_NAME and \
1480           doc_inline_sect.search(line):
1481            self.entry.section = doc_inline_sect.group(1)
1482            self.entry.new_start_line = ln
1483
1484            self.entry.contents = doc_inline_sect.group(2).lstrip()
1485            if self.entry.contents != "":
1486                self.entry.contents += "\n"
1487
1488            self.inline_doc_state = self.STATE_INLINE_TEXT
1489            # Documentation block end */
1490            return
1491
1492        if doc_inline_end.search(line):
1493            if self.entry.contents not in ["", "\n"]:
1494                self.dump_section()
1495
1496            self.state = self.STATE_PROTO
1497            self.inline_doc_state = self.STATE_INLINE_NA
1498            return
1499
1500        if doc_content.search(line):
1501            if self.inline_doc_state == self.STATE_INLINE_TEXT:
1502                self.entry.contents += doc_content.group(1) + "\n"
1503                if not self.entry.contents.strip(" ").rstrip("\n"):
1504                    self.entry.contents = ""
1505
1506            elif self.inline_doc_state == self.STATE_INLINE_NAME:
1507                self.emit_warning(ln,
1508                                  f"Incorrect use of kernel-doc format: {line}")
1509
1510                self.inline_doc_state = self.STATE_INLINE_ERROR
1511
1512    def syscall_munge(self, ln, proto):
1513        """
1514        Handle syscall definitions
1515        """
1516
1517        is_void = False
1518
1519        # Strip newlines/CR's
1520        proto = re.sub(r'[\r\n]+', ' ', proto)
1521
1522        # Check if it's a SYSCALL_DEFINE0
1523        if 'SYSCALL_DEFINE0' in proto:
1524            is_void = True
1525
1526        # Replace SYSCALL_DEFINE with correct return type & function name
1527        proto = Re(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto)
1528
1529        r = Re(r'long\s+(sys_.*?),')
1530        if r.search(proto):
1531            proto = proto.replace(',', '(', count=1)
1532        elif is_void:
1533            proto = proto.replace(')', '(void)', count=1)
1534
1535        # Now delete all of the odd-numbered commas in the proto
1536        # so that argument types & names don't have a comma between them
1537        count = 0
1538        length = len(proto)
1539
1540        if is_void:
1541            length = 0  # skip the loop if is_void
1542
1543        for ix in range(length):
1544            if proto[ix] == ',':
1545                count += 1
1546                if count % 2 == 1:
1547                    proto = proto[:ix] + ' ' + proto[ix+1:]
1548
1549        return proto
1550
1551    def tracepoint_munge(self, ln, proto):
1552        """
1553        Handle tracepoint definitions
1554        """
1555
1556        tracepointname = None
1557        tracepointargs = None
1558
1559        # Match tracepoint name based on different patterns
1560        r = Re(r'TRACE_EVENT\((.*?),')
1561        if r.search(proto):
1562            tracepointname = r.group(1)
1563
1564        r = Re(r'DEFINE_SINGLE_EVENT\((.*?),')
1565        if r.search(proto):
1566            tracepointname = r.group(1)
1567
1568        r = Re(r'DEFINE_EVENT\((.*?),(.*?),')
1569        if r.search(proto):
1570            tracepointname = r.group(2)
1571
1572        if tracepointname:
1573            tracepointname = tracepointname.lstrip()
1574
1575        r = Re(r'TP_PROTO\((.*?)\)')
1576        if r.search(proto):
1577            tracepointargs = r.group(1)
1578
1579        if not tracepointname or not tracepointargs:
1580            self.emit_warning(ln,
1581                              f"Unrecognized tracepoint format:\n{proto}\n")
1582        else:
1583            proto = f"static inline void trace_{tracepointname}({tracepointargs})"
1584            self.entry.identifier = f"trace_{self.entry.identifier}"
1585
1586        return proto
1587
1588    def process_proto_function(self, ln, line):
1589        """Ancillary routine to process a function prototype"""
1590
1591        # strip C99-style comments to end of line
1592        r = Re(r"\/\/.*$", re.S)
1593        line = r.sub('', line)
1594
1595        if Re(r'\s*#\s*define').match(line):
1596            self.entry.prototype = line
1597        elif line.startswith('#'):
1598            # Strip other macros like #ifdef/#ifndef/#endif/...
1599            pass
1600        else:
1601            r = Re(r'([^\{]*)')
1602            if r.match(line):
1603                self.entry.prototype += r.group(1) + " "
1604
1605        if '{' in line or ';' in line or Re(r'\s*#\s*define').match(line):
1606            # strip comments
1607            r = Re(r'/\*.*?\*/')
1608            self.entry.prototype = r.sub('', self.entry.prototype)
1609
1610            # strip newlines/cr's
1611            r = Re(r'[\r\n]+')
1612            self.entry.prototype = r.sub(' ', self.entry.prototype)
1613
1614            # strip leading spaces
1615            r = Re(r'^\s+')
1616            self.entry.prototype = r.sub('', self.entry.prototype)
1617
1618            # Handle self.entry.prototypes for function pointers like:
1619            #       int (*pcs_config)(struct foo)
1620
1621            r = Re(r'^(\S+\s+)\(\s*\*(\S+)\)')
1622            self.entry.prototype = r.sub(r'\1\2', self.entry.prototype)
1623
1624            if 'SYSCALL_DEFINE' in self.entry.prototype:
1625                self.entry.prototype = self.syscall_munge(ln,
1626                                                          self.entry.prototype)
1627
1628            r = Re(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT')
1629            if r.search(self.entry.prototype):
1630                self.entry.prototype = self.tracepoint_munge(ln,
1631                                                             self.entry.prototype)
1632
1633            self.dump_function(ln, self.entry.prototype)
1634            self.reset_state(ln)
1635
1636    def process_proto_type(self, ln, line):
1637        """Ancillary routine to process a type"""
1638
1639        # Strip newlines/cr's.
1640        line = Re(r'[\r\n]+', re.S).sub(' ', line)
1641
1642        # Strip leading spaces
1643        line = Re(r'^\s+', re.S).sub('', line)
1644
1645        # Strip trailing spaces
1646        line = Re(r'\s+$', re.S).sub('', line)
1647
1648        # Strip C99-style comments to the end of the line
1649        line = Re(r"\/\/.*$", re.S).sub('', line)
1650
1651        # To distinguish preprocessor directive from regular declaration later.
1652        if line.startswith('#'):
1653            line += ";"
1654
1655        r = Re(r'([^\{\};]*)([\{\};])(.*)')
1656        while True:
1657            if r.search(line):
1658                if self.entry.prototype:
1659                    self.entry.prototype += " "
1660                self.entry.prototype += r.group(1) + r.group(2)
1661
1662                self.entry.brcount += r.group(2).count('{')
1663                self.entry.brcount -= r.group(2).count('}')
1664
1665                self.entry.brcount = max(self.entry.brcount, 0)
1666
1667                if r.group(2) == ';' and self.entry.brcount == 0:
1668                    self.dump_declaration(ln, self.entry.prototype)
1669                    self.reset_state(ln)
1670                    break
1671
1672                line = r.group(3)
1673            else:
1674                self.entry.prototype += line
1675                break
1676
1677    def process_proto(self, ln, line):
1678        """STATE_PROTO: reading a function/whatever prototype."""
1679
1680        if doc_inline_oneline.search(line):
1681            self.entry.section = doc_inline_oneline.group(1)
1682            self.entry.contents = doc_inline_oneline.group(2)
1683
1684            if self.entry.contents != "":
1685                self.entry.contents += "\n"
1686                self.dump_section(start_new=False)
1687
1688        elif doc_inline_start.search(line):
1689            self.state = self.STATE_INLINE
1690            self.inline_doc_state = self.STATE_INLINE_NAME
1691
1692        elif self.entry.decl_type == 'function':
1693            self.process_proto_function(ln, line)
1694
1695        else:
1696            self.process_proto_type(ln, line)
1697
1698    def process_docblock(self, ln, line):
1699        """STATE_DOCBLOCK: within a DOC: block."""
1700
1701        if doc_end.search(line):
1702            self.dump_section()
1703            self.output_declaration("doc", None,
1704                       sectionlist=self.entry.sectionlist,
1705                       sections=self.entry.sections,                    module=self.config.modulename)
1706            self.reset_state(ln)
1707
1708        elif doc_content.search(line):
1709            self.entry.contents += doc_content.group(1) + "\n"
1710
1711    def run(self):
1712        """
1713        Open and process each line of a C source file.
1714        he parsing is controlled via a state machine, and the line is passed
1715        to a different process function depending on the state. The process
1716        function may update the state as needed.
1717        """
1718
1719        cont = False
1720        prev = ""
1721        prev_ln = None
1722
1723        try:
1724            with open(self.fname, "r", encoding="utf8",
1725                      errors="backslashreplace") as fp:
1726                for ln, line in enumerate(fp):
1727
1728                    line = line.expandtabs().strip("\n")
1729
1730                    # Group continuation lines on prototypes
1731                    if self.state == self.STATE_PROTO:
1732                        if line.endswith("\\"):
1733                            prev += line.removesuffix("\\")
1734                            cont = True
1735
1736                            if not prev_ln:
1737                                prev_ln = ln
1738
1739                            continue
1740
1741                        if cont:
1742                            ln = prev_ln
1743                            line = prev + line
1744                            prev = ""
1745                            cont = False
1746                            prev_ln = None
1747
1748                    self.config.log.debug("%d %s%s: %s",
1749                                          ln, self.st_name[self.state],
1750                                          self.st_inline_name[self.inline_doc_state],
1751                                          line)
1752
1753                    # TODO: not all states allow EXPORT_SYMBOL*, so this
1754                    # can be optimized later on to speedup parsing
1755                    self.process_export(self.config.function_table, line)
1756
1757                    # Hand this line to the appropriate state handler
1758                    if self.state == self.STATE_NORMAL:
1759                        self.process_normal(ln, line)
1760                    elif self.state == self.STATE_NAME:
1761                        self.process_name(ln, line)
1762                    elif self.state in [self.STATE_BODY, self.STATE_BODY_MAYBE,
1763                                        self.STATE_BODY_WITH_BLANK_LINE]:
1764                        self.process_body(ln, line)
1765                    elif self.state == self.STATE_INLINE:  # scanning for inline parameters
1766                        self.process_inline(ln, line)
1767                    elif self.state == self.STATE_PROTO:
1768                        self.process_proto(ln, line)
1769                    elif self.state == self.STATE_DOCBLOCK:
1770                        self.process_docblock(ln, line)
1771        except OSError:
1772            self.config.log.error(f"Error: Cannot open file {self.fname}")
1773            self.config.errors += 1
1774
1775
1776class GlobSourceFiles:
1777    """
1778    Parse C source code file names and directories via an Interactor.
1779
1780    """
1781
1782    def __init__(self, srctree=None, valid_extensions=None):
1783        """
1784        Initialize valid extensions with a tuple.
1785
1786        If not defined, assume default C extensions (.c and .h)
1787
1788        It would be possible to use python's glob function, but it is
1789        very slow, and it is not interactive. So, it would wait to read all
1790        directories before actually do something.
1791
1792        So, let's use our own implementation.
1793        """
1794
1795        if not valid_extensions:
1796            self.extensions = (".c", ".h")
1797        else:
1798            self.extensions = valid_extensions
1799
1800        self.srctree = srctree
1801
1802    def _parse_dir(self, dirname):
1803        """Internal function to parse files recursively"""
1804
1805        with os.scandir(dirname) as obj:
1806            for entry in obj:
1807                name = os.path.join(dirname, entry.name)
1808
1809                if entry.is_dir():
1810                    yield from self._parse_dir(name)
1811
1812                if not entry.is_file():
1813                    continue
1814
1815                basename = os.path.basename(name)
1816
1817                if not basename.endswith(self.extensions):
1818                    continue
1819
1820                yield name
1821
1822    def parse_files(self, file_list, file_not_found_cb):
1823        for fname in file_list:
1824            if self.srctree:
1825                f = os.path.join(self.srctree, fname)
1826            else:
1827                f = fname
1828
1829            if os.path.isdir(f):
1830                yield from self._parse_dir(f)
1831            elif os.path.isfile(f):
1832                yield f
1833            elif file_not_found_cb:
1834                file_not_found_cb(fname)
1835
1836
1837class KernelFiles():
1838
1839    def parse_file(self, fname):
1840
1841        doc = KernelDoc(self.config, fname)
1842        doc.run()
1843
1844        return doc
1845
1846    def process_export_file(self, fname):
1847        try:
1848            with open(fname, "r", encoding="utf8",
1849                      errors="backslashreplace") as fp:
1850                for line in fp:
1851                    KernelDoc.process_export(self.config.function_table, line)
1852
1853        except IOError:
1854            print(f"Error: Cannot open fname {fname}", fname=sys.stderr)
1855            self.config.errors += 1
1856
1857    def file_not_found_cb(self, fname):
1858        self.config.log.error("Cannot find file %s", fname)
1859        self.config.errors += 1
1860
1861    def __init__(self, files=None, verbose=False, out_style=None,
1862                 werror=False, wreturn=False, wshort_desc=False,
1863                 wcontents_before_sections=False,
1864                 logger=None, modulename=None, export_file=None):
1865        """Initialize startup variables and parse all files"""
1866
1867
1868        if not verbose:
1869            verbose = bool(os.environ.get("KBUILD_VERBOSE", 0))
1870
1871        if not modulename:
1872            modulename = "Kernel API"
1873
1874        dt = datetime.now()
1875        if os.environ.get("KBUILD_BUILD_TIMESTAMP", None):
1876            # use UTC TZ
1877            to_zone = tz.gettz('UTC')
1878            dt = dt.astimezone(to_zone)
1879
1880        if not werror:
1881            kcflags = os.environ.get("KCFLAGS", None)
1882            if kcflags:
1883                match = re.search(r"(\s|^)-Werror(\s|$)/", kcflags)
1884                if match:
1885                    werror = True
1886
1887            # reading this variable is for backwards compat just in case
1888            # someone was calling it with the variable from outside the
1889            # kernel's build system
1890            kdoc_werror = os.environ.get("KDOC_WERROR", None)
1891            if kdoc_werror:
1892                werror = kdoc_werror
1893
1894        # Set global config data used on all files
1895        self.config = argparse.Namespace
1896
1897        self.config.verbose = verbose
1898        self.config.werror = werror
1899        self.config.wreturn = wreturn
1900        self.config.wshort_desc = wshort_desc
1901        self.config.wcontents_before_sections = wcontents_before_sections
1902        self.config.modulename = modulename
1903
1904        self.config.function_table = set()
1905        self.config.source_map = {}
1906
1907        if not logger:
1908            self.config.log = logging.getLogger("kernel-doc")
1909        else:
1910            self.config.log = logger
1911
1912        self.config.kernel_version = os.environ.get("KERNELVERSION",
1913                                                    "unknown kernel version'")
1914        self.config.src_tree = os.environ.get("SRCTREE", None)
1915
1916        self.out_style = out_style
1917        self.export_file = export_file
1918
1919        # Initialize internal variables
1920
1921        self.config.errors = 0
1922        self.results = []
1923
1924        self.file_list = files
1925        self.files = set()
1926
1927    def parse(self):
1928        """
1929        Parse all files
1930        """
1931
1932        glob = GlobSourceFiles(srctree=self.config.src_tree)
1933
1934        # Let's use a set here to avoid duplicating files
1935
1936        for fname in glob.parse_files(self.file_list, self.file_not_found_cb):
1937            if fname in self.files:
1938                continue
1939
1940            self.files.add(fname)
1941
1942            res = self.parse_file(fname)
1943            self.results.append((res.fname, res.entries))
1944
1945        if not self.files:
1946            sys.exit(1)
1947
1948        # If a list of export files was provided, parse EXPORT_SYMBOL*
1949        # from the ones not already parsed
1950
1951        if self.export_file:
1952            files = self.files
1953
1954            glob = GlobSourceFiles(srctree=self.config.src_tree)
1955
1956            for fname in glob.parse_files(self.export_file,
1957                                          self.file_not_found_cb):
1958                if fname not in files:
1959                    files.add(fname)
1960
1961                    self.process_export_file(fname)
1962
1963    def out_msg(self, fname, name, arg):
1964        # TODO: filter out unwanted parts
1965
1966        return self.out_style.msg(fname, name, arg)
1967
1968    def msg(self, enable_lineno=False, export=False, internal=False,
1969            symbol=None, nosymbol=None):
1970
1971        function_table = self.config.function_table
1972
1973        if symbol:
1974            for s in symbol:
1975                function_table.add(s)
1976
1977        # Output none mode: only warnings will be shown
1978        if not self.out_style:
1979            return
1980
1981        self.out_style.set_config(self.config)
1982
1983        self.out_style.set_filter(export, internal, symbol, nosymbol,
1984                                  function_table, enable_lineno)
1985
1986        for fname, arg_tuple in self.results:
1987            for name, arg in arg_tuple:
1988                if self.out_msg(fname, name, arg):
1989                    ln = arg.get("ln", 0)
1990                    dtype = arg.get('type', "")
1991
1992                    self.config.log.warning("%s:%d Can't handle %s",
1993                                            fname, ln, dtype)
1994
1995
1996class OutputFormat:
1997    # output mode.
1998    OUTPUT_ALL          = 0 # output all symbols and doc sections
1999    OUTPUT_INCLUDE      = 1 # output only specified symbols
2000    OUTPUT_EXPORTED     = 2 # output exported symbols
2001    OUTPUT_INTERNAL     = 3 # output non-exported symbols
2002
2003    # Virtual member to be overriden at the  inherited classes
2004    highlights = []
2005
2006    def __init__(self):
2007        """Declare internal vars and set mode to OUTPUT_ALL"""
2008
2009        self.out_mode = self.OUTPUT_ALL
2010        self.enable_lineno = None
2011        self.nosymbol = {}
2012        self.symbol = None
2013        self.function_table = set()
2014        self.config = None
2015
2016    def set_config(self, config):
2017        self.config = config
2018
2019    def set_filter(self, export, internal, symbol, nosymbol, function_table,
2020                   enable_lineno):
2021        """
2022        Initialize filter variables according with the requested mode.
2023
2024        Only one choice is valid between export, internal and symbol.
2025
2026        The nosymbol filter can be used on all modes.
2027        """
2028
2029        self.enable_lineno = enable_lineno
2030
2031        if symbol:
2032            self.out_mode = self.OUTPUT_INCLUDE
2033            function_table = symbol
2034        elif export:
2035            self.out_mode = self.OUTPUT_EXPORTED
2036        elif internal:
2037            self.out_mode = self.OUTPUT_INTERNAL
2038        else:
2039            self.out_mode = self.OUTPUT_ALL
2040
2041        if nosymbol:
2042            self.nosymbol = set(nosymbol)
2043
2044        if function_table:
2045            self.function_table = function_table
2046
2047    def highlight_block(self, block):
2048        """
2049        Apply the RST highlights to a sub-block of text.
2050        """
2051
2052        for r, sub in self.highlights:
2053            block = r.sub(sub, block)
2054
2055        return block
2056
2057    def check_doc(self, name):
2058        """Check if DOC should be output"""
2059
2060        if self.out_mode == self.OUTPUT_ALL:
2061            return True
2062
2063        if self.out_mode == self.OUTPUT_INCLUDE:
2064            if name in self.nosymbol:
2065                return False
2066
2067            if name in self.function_table:
2068                return True
2069
2070        return False
2071
2072    def check_declaration(self, dtype, name):
2073        if name in self.nosymbol:
2074            return False
2075
2076        if self.out_mode == self.OUTPUT_ALL:
2077            return True
2078
2079        if self.out_mode in [ self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED ]:
2080            if name in self.function_table:
2081                return True
2082
2083        if self.out_mode == self.OUTPUT_INTERNAL:
2084            if dtype != "function":
2085                return True
2086
2087            if name not in self.function_table:
2088                return True
2089
2090        return False
2091
2092    def check_function(self, fname, name, args):
2093        return True
2094
2095    def check_enum(self, fname, name, args):
2096        return True
2097
2098    def check_typedef(self, fname, name, args):
2099        return True
2100
2101    def msg(self, fname, name, args):
2102
2103        dtype = args.get('type', "")
2104
2105        if dtype == "doc":
2106            self.out_doc(fname, name, args)
2107            return False
2108
2109        if not self.check_declaration(dtype, name):
2110            return False
2111
2112        if dtype == "function":
2113            self.out_function(fname, name, args)
2114            return False
2115
2116        if dtype == "enum":
2117            self.out_enum(fname, name, args)
2118            return False
2119
2120        if dtype == "typedef":
2121            self.out_typedef(fname, name, args)
2122            return False
2123
2124        if dtype in ["struct", "union"]:
2125            self.out_struct(fname, name, args)
2126            return False
2127
2128        # Warn if some type requires an output logic
2129        self.config.log.warning("doesn't now how to output '%s' block",
2130                                dtype)
2131
2132        return True
2133
2134    # Virtual methods to be overridden by inherited classes
2135    def out_doc(self, fname, name, args):
2136        pass
2137
2138    def out_function(self, fname, name, args):
2139        pass
2140
2141    def out_enum(self, fname, name, args):
2142        pass
2143
2144    def out_typedef(self, fname, name, args):
2145        pass
2146
2147    def out_struct(self, fname, name, args):
2148        pass
2149
2150
2151class RestFormat(OutputFormat):
2152    # """Consts and functions used by ReST output"""
2153
2154    highlights = [
2155        (type_constant, r"``\1``"),
2156        (type_constant2, r"``\1``"),
2157
2158        # Note: need to escape () to avoid func matching later
2159        (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"),
2160        (type_member, r":c:type:`\1\2\3 <\1>`"),
2161        (type_fp_param, r"**\1\\(\\)**"),
2162        (type_fp_param2, r"**\1\\(\\)**"),
2163        (type_func, r"\1()"),
2164        (type_enum, r":c:type:`\1 <\2>`"),
2165        (type_struct, r":c:type:`\1 <\2>`"),
2166        (type_typedef, r":c:type:`\1 <\2>`"),
2167        (type_union, r":c:type:`\1 <\2>`"),
2168
2169        # in rst this can refer to any type
2170        (type_fallback, r":c:type:`\1`"),
2171        (type_param_ref, r"**\1\2**")
2172    ]
2173    blankline = "\n"
2174
2175    sphinx_literal = Re(r'^[^.].*::$', cache=False)
2176    sphinx_cblock = Re(r'^\.\.\ +code-block::', cache=False)
2177
2178    def __init__(self):
2179        """
2180        Creates class variables.
2181
2182        Not really mandatory, but it is a good coding style and makes
2183        pylint happy.
2184        """
2185
2186        super().__init__()
2187        self.lineprefix = ""
2188
2189    def print_lineno (self, ln):
2190        """Outputs a line number"""
2191
2192        if self.enable_lineno and ln:
2193            print(f".. LINENO {ln}")
2194
2195    def output_highlight(self, args):
2196        input_text = args
2197        output = ""
2198        in_literal = False
2199        litprefix = ""
2200        block = ""
2201
2202        for line in input_text.strip("\n").split("\n"):
2203
2204            # If we're in a literal block, see if we should drop out of it.
2205            # Otherwise, pass the line straight through unmunged.
2206            if in_literal:
2207                if line.strip():  # If the line is not blank
2208                    # If this is the first non-blank line in a literal block,
2209                    # figure out the proper indent.
2210                    if not litprefix:
2211                        r = Re(r'^(\s*)')
2212                        if r.match(line):
2213                            litprefix = '^' + r.group(1)
2214                        else:
2215                            litprefix = ""
2216
2217                        output += line + "\n"
2218                    elif not Re(litprefix).match(line):
2219                        in_literal = False
2220                    else:
2221                        output += line + "\n"
2222                else:
2223                    output += line + "\n"
2224
2225            # Not in a literal block (or just dropped out)
2226            if not in_literal:
2227                block += line + "\n"
2228                if self.sphinx_literal.match(line) or self.sphinx_cblock.match(line):
2229                    in_literal = True
2230                    litprefix = ""
2231                    output += self.highlight_block(block)
2232                    block = ""
2233
2234        # Handle any remaining block
2235        if block:
2236            output += self.highlight_block(block)
2237
2238        # Print the output with the line prefix
2239        for line in output.strip("\n").split("\n"):
2240            print(self.lineprefix + line)
2241
2242    def out_section(self, args, out_reference=False):
2243        """
2244        Outputs a block section.
2245
2246        This could use some work; it's used to output the DOC: sections, and
2247        starts by putting out the name of the doc section itself, but that
2248        tends to duplicate a header already in the template file.
2249        """
2250
2251        sectionlist = args.get('sectionlist', [])
2252        sections = args.get('sections', {})
2253        section_start_lines = args.get('section_start_lines', {})
2254
2255        for section in sectionlist:
2256            # Skip sections that are in the nosymbol_table
2257            if section in self.nosymbol:
2258                continue
2259
2260            if not self.out_mode == self.OUTPUT_INCLUDE:
2261                if out_reference:
2262                    print(f".. _{section}:\n")
2263
2264                if not self.symbol:
2265                    print(f'{self.lineprefix}**{section}**\n')
2266
2267            self.print_lineno(section_start_lines.get(section, 0))
2268            self.output_highlight(sections[section])
2269            print()
2270        print()
2271
2272    def out_doc(self, fname, name, args):
2273        if not self.check_doc(name):
2274            return
2275
2276        self.out_section(args, out_reference=True)
2277
2278    def out_function(self, fname, name, args):
2279
2280        oldprefix = self.lineprefix
2281        signature = ""
2282
2283        func_macro = args.get('func_macro', False)
2284        if func_macro:
2285            signature = args['function']
2286        else:
2287            if args.get('functiontype'):
2288                signature = args['functiontype'] + " "
2289            signature += args['function'] + " ("
2290
2291        parameterlist = args.get('parameterlist', [])
2292        parameterdescs = args.get('parameterdescs', {})
2293        parameterdesc_start_lines = args.get('parameterdesc_start_lines', {})
2294
2295        ln = args.get('ln', 0)
2296
2297        count = 0
2298        for parameter in parameterlist:
2299            if count != 0:
2300                signature += ", "
2301            count += 1
2302            dtype = args['parametertypes'].get(parameter, "")
2303
2304            if function_pointer.search(dtype):
2305                signature += function_pointer.group(1) + parameter + function_pointer.group(3)
2306            else:
2307                signature += dtype
2308
2309        if not func_macro:
2310            signature += ")"
2311
2312        if args.get('typedef') or not args.get('functiontype'):
2313            print(f".. c:macro:: {args['function']}\n")
2314
2315            if args.get('typedef'):
2316                self.print_lineno(ln)
2317                print("   **Typedef**: ", end="")
2318                self.lineprefix = ""
2319                self.output_highlight(args.get('purpose', ""))
2320                print("\n\n**Syntax**\n")
2321                print(f"  ``{signature}``\n")
2322            else:
2323                print(f"``{signature}``\n")
2324        else:
2325            print(f".. c:function:: {signature}\n")
2326
2327        if not args.get('typedef'):
2328            self.print_lineno(ln)
2329            self.lineprefix = "   "
2330            self.output_highlight(args.get('purpose', ""))
2331            print()
2332
2333        # Put descriptive text into a container (HTML <div>) to help set
2334        # function prototypes apart
2335        self.lineprefix = "  "
2336
2337        if parameterlist:
2338            print(".. container:: kernelindent\n")
2339            print(f"{self.lineprefix}**Parameters**\n")
2340
2341        for parameter in parameterlist:
2342            parameter_name = Re(r'\[.*').sub('', parameter)
2343            dtype = args['parametertypes'].get(parameter, "")
2344
2345            if dtype:
2346                print(f"{self.lineprefix}``{dtype}``")
2347            else:
2348                print(f"{self.lineprefix}``{parameter}``")
2349
2350            self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0))
2351
2352            self.lineprefix = "    "
2353            if parameter_name in parameterdescs and \
2354               parameterdescs[parameter_name] != KernelDoc.undescribed:
2355
2356                self.output_highlight(parameterdescs[parameter_name])
2357                print()
2358            else:
2359                print(f"{self.lineprefix}*undescribed*\n")
2360            self.lineprefix = "  "
2361
2362        self.out_section(args)
2363        self.lineprefix = oldprefix
2364
2365    def out_enum(self, fname, name, args):
2366
2367        oldprefix = self.lineprefix
2368        name = args.get('enum', '')
2369        parameterlist = args.get('parameterlist', [])
2370        parameterdescs = args.get('parameterdescs', {})
2371        ln = args.get('ln', 0)
2372
2373        print(f"\n\n.. c:enum:: {name}\n")
2374
2375        self.print_lineno(ln)
2376        self.lineprefix = "  "
2377        self.output_highlight(args.get('purpose', ''))
2378        print()
2379
2380        print(".. container:: kernelindent\n")
2381        outer = self.lineprefix + "  "
2382        self.lineprefix = outer + "  "
2383        print(f"{outer}**Constants**\n")
2384
2385        for parameter in parameterlist:
2386            print(f"{outer}``{parameter}``")
2387
2388            if parameterdescs.get(parameter, '') != KernelDoc.undescribed:
2389                self.output_highlight(parameterdescs[parameter])
2390            else:
2391                print(f"{self.lineprefix}*undescribed*\n")
2392            print()
2393
2394        self.lineprefix = oldprefix
2395        self.out_section(args)
2396
2397    def out_typedef(self, fname, name, args):
2398
2399        oldprefix = self.lineprefix
2400        name = args.get('typedef', '')
2401        ln = args.get('ln', 0)
2402
2403        print(f"\n\n.. c:type:: {name}\n")
2404
2405        self.print_lineno(ln)
2406        self.lineprefix = "   "
2407
2408        self.output_highlight(args.get('purpose', ''))
2409
2410        print()
2411
2412        self.lineprefix = oldprefix
2413        self.out_section(args)
2414
2415    def out_struct(self, fname, name, args):
2416
2417        name = args.get('struct', "")
2418        purpose = args.get('purpose', "")
2419        declaration = args.get('definition', "")
2420        dtype = args.get('type', "struct")
2421        ln = args.get('ln', 0)
2422
2423        parameterlist = args.get('parameterlist', [])
2424        parameterdescs = args.get('parameterdescs', {})
2425        parameterdesc_start_lines = args.get('parameterdesc_start_lines', {})
2426
2427        print(f"\n\n.. c:{dtype}:: {name}\n")
2428
2429        self.print_lineno(ln)
2430
2431        oldprefix = self.lineprefix
2432        self.lineprefix += "  "
2433
2434        self.output_highlight(purpose)
2435        print()
2436
2437        print(".. container:: kernelindent\n")
2438        print(f"{self.lineprefix}**Definition**::\n")
2439
2440        self.lineprefix = self.lineprefix + "  "
2441
2442        declaration = declaration.replace("\t", self.lineprefix)
2443
2444        print(f"{self.lineprefix}{dtype} {name}" + ' {')
2445        print(f"{declaration}{self.lineprefix}" + "};\n")
2446
2447        self.lineprefix = "  "
2448        print(f"{self.lineprefix}**Members**\n")
2449        for parameter in parameterlist:
2450            if not parameter or parameter.startswith("#"):
2451                continue
2452
2453            parameter_name = parameter.split("[", maxsplit=1)[0]
2454
2455            if parameterdescs.get(parameter_name) == KernelDoc.undescribed:
2456                continue
2457
2458            self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0))
2459
2460            print(f"{self.lineprefix}``{parameter}``")
2461
2462            self.lineprefix = "    "
2463            self.output_highlight(parameterdescs[parameter_name])
2464            self.lineprefix = "  "
2465
2466            print()
2467
2468        print()
2469
2470        self.lineprefix = oldprefix
2471        self.out_section(args)
2472
2473
2474class ManFormat(OutputFormat):
2475    """Consts and functions used by man pages output"""
2476
2477    highlights = (
2478        (type_constant, r"\1"),
2479        (type_constant2, r"\1"),
2480        (type_func, r"\\fB\1\\fP"),
2481        (type_enum, r"\\fI\1\\fP"),
2482        (type_struct, r"\\fI\1\\fP"),
2483        (type_typedef, r"\\fI\1\\fP"),
2484        (type_union, r"\\fI\1\\fP"),
2485        (type_param, r"\\fI\1\\fP"),
2486        (type_param_ref, r"\\fI\1\2\\fP"),
2487        (type_member, r"\\fI\1\2\3\\fP"),
2488        (type_fallback, r"\\fI\1\\fP")
2489    )
2490    blankline = ""
2491
2492    def __init__(self):
2493        """
2494        Creates class variables.
2495
2496        Not really mandatory, but it is a good coding style and makes
2497        pylint happy.
2498        """
2499
2500        super().__init__()
2501
2502        dt = datetime.now()
2503        if os.environ.get("KBUILD_BUILD_TIMESTAMP", None):
2504            # use UTC TZ
2505            to_zone = tz.gettz('UTC')
2506            dt = dt.astimezone(to_zone)
2507
2508        self.man_date = dt.strftime("%B %Y")
2509
2510    def output_highlight(self, block):
2511
2512        contents = self.highlight_block(block)
2513
2514        if isinstance(contents, list):
2515            contents = "\n".join(contents)
2516
2517        for line in contents.strip("\n").split("\n"):
2518            line = Re(r"^\s*").sub("", line)
2519
2520            if line and line[0] == ".":
2521                print("\\&" + line)
2522            else:
2523                print(line)
2524
2525    def out_doc(self, fname, name, args):
2526        module = args.get('module')
2527        sectionlist = args.get('sectionlist', [])
2528        sections = args.get('sections', {})
2529
2530        print(f'.TH "{module}" 9 "{module}" "{self.man_date}" "API Manual" LINUX')
2531
2532        for section in sectionlist:
2533            print(f'.SH "{section}"')
2534            self.output_highlight(sections.get(section))
2535
2536    def out_function(self, fname, name, args):
2537        """output function in man"""
2538
2539        parameterlist = args.get('parameterlist', [])
2540        parameterdescs = args.get('parameterdescs', {})
2541        sectionlist = args.get('sectionlist', [])
2542        sections = args.get('sections', {})
2543
2544        print(f'.TH "{args['function']}" 9 "{args['function']}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX')
2545
2546        print(".SH NAME")
2547        print(f"{args['function']} \\- {args['purpose']}")
2548
2549        print(".SH SYNOPSIS")
2550        if args.get('functiontype', ''):
2551            print(f'.B "{args['functiontype']}" {args['function']}')
2552        else:
2553            print(f'.B "{args['function']}')
2554
2555        count = 0
2556        parenth = "("
2557        post = ","
2558
2559        for parameter in parameterlist:
2560            if count == len(parameterlist) - 1:
2561                post = ");"
2562
2563            dtype = args['parametertypes'].get(parameter, "")
2564            if function_pointer.match(dtype):
2565                # Pointer-to-function
2566                print(f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"')
2567            else:
2568                dtype = Re(r'([^\*])$').sub(r'\1 ', dtype)
2569
2570                print(f'.BI "{parenth}{dtype}"  "{post}"')
2571            count += 1
2572            parenth = ""
2573
2574        if parameterlist:
2575            print(".SH ARGUMENTS")
2576
2577        for parameter in parameterlist:
2578            parameter_name = re.sub(r'\[.*', '', parameter)
2579
2580            print(f'.IP "{parameter}" 12')
2581            self.output_highlight(parameterdescs.get(parameter_name, ""))
2582
2583        for section in sectionlist:
2584            print(f'.SH "{section.upper()}"')
2585            self.output_highlight(sections[section])
2586
2587    def out_enum(self, fname, name, args):
2588
2589        name = args.get('enum', '')
2590        parameterlist = args.get('parameterlist', [])
2591        sectionlist = args.get('sectionlist', [])
2592        sections = args.get('sections', {})
2593
2594        print(f'.TH "{args['module']}" 9 "enum {args['enum']}" "{self.man_date}" "API Manual" LINUX')
2595
2596        print(".SH NAME")
2597        print(f"enum {args['enum']} \\- {args['purpose']}")
2598
2599        print(".SH SYNOPSIS")
2600        print(f"enum {args['enum']}" + " {")
2601
2602        count = 0
2603        for parameter in parameterlist:
2604            print(f'.br\n.BI "    {parameter}"')
2605            if count == len(parameterlist) - 1:
2606                print("\n};")
2607            else:
2608                print(", \n.br")
2609
2610            count += 1
2611
2612        print(".SH Constants")
2613
2614        for parameter in parameterlist:
2615            parameter_name = Re(r'\[.*').sub('', parameter)
2616            print(f'.IP "{parameter}" 12')
2617            self.output_highlight(args['parameterdescs'].get(parameter_name, ""))
2618
2619        for section in sectionlist:
2620            print(f'.SH "{section}"')
2621            self.output_highlight(sections[section])
2622
2623    def out_typedef(self, fname, name, args):
2624        module = args.get('module')
2625        typedef = args.get('typedef')
2626        purpose = args.get('purpose')
2627        sectionlist = args.get('sectionlist', [])
2628        sections = args.get('sections', {})
2629
2630        print(f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual" LINUX')
2631
2632        print(".SH NAME")
2633        print(f"typedef {typedef} \\- {purpose}")
2634
2635        for section in sectionlist:
2636            print(f'.SH "{section}"')
2637            self.output_highlight(sections.get(section))
2638
2639    def out_struct(self, fname, name, args):
2640        module = args.get('module')
2641        struct_type = args.get('type')
2642        struct_name = args.get('struct')
2643        purpose = args.get('purpose')
2644        definition = args.get('definition')
2645        sectionlist = args.get('sectionlist', [])
2646        parameterlist = args.get('parameterlist', [])
2647        sections = args.get('sections', {})
2648        parameterdescs = args.get('parameterdescs', {})
2649
2650        print(f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_date}" "API Manual" LINUX')
2651
2652        print(".SH NAME")
2653        print(f"{struct_type} {struct_name} \\- {purpose}")
2654
2655        # Replace tabs with two spaces and handle newlines
2656        declaration = definition.replace("\t", "  ")
2657        declaration = Re(r"\n").sub('"\n.br\n.BI "', declaration)
2658
2659        print(".SH SYNOPSIS")
2660        print(f"{struct_type} {struct_name} " + "{" +"\n.br")
2661        print(f'.BI "{declaration}\n' + "};\n.br\n")
2662
2663        print(".SH Members")
2664        for parameter in parameterlist:
2665            if parameter.startswith("#"):
2666                continue
2667
2668            parameter_name = re.sub(r"\[.*", "", parameter)
2669
2670            if parameterdescs.get(parameter_name) == KernelDoc.undescribed:
2671                continue
2672
2673            print(f'.IP "{parameter}" 12')
2674            self.output_highlight(parameterdescs.get(parameter_name))
2675
2676        for section in sectionlist:
2677            print(f'.SH "{section}"')
2678            self.output_highlight(sections.get(section))
2679
2680
2681# Command line interface
2682
2683
2684DESC = """
2685Read C language source or header FILEs, extract embedded documentation comments,
2686and print formatted documentation to standard output.
2687
2688The documentation comments are identified by the "/**" opening comment mark.
2689
2690See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax.
2691"""
2692
2693EXPORT_FILE_DESC = """
2694Specify an additional FILE in which to look for EXPORT_SYMBOL information.
2695
2696May be used multiple times.
2697"""
2698
2699EXPORT_DESC = """
2700Only output documentation for the symbols that have been
2701exported using EXPORT_SYMBOL() and related macros in any input
2702FILE or -export-file FILE.
2703"""
2704
2705INTERNAL_DESC = """
2706Only output documentation for the symbols that have NOT been
2707exported using EXPORT_SYMBOL() and related macros in any input
2708FILE or -export-file FILE.
2709"""
2710
2711FUNCTION_DESC = """
2712Only output documentation for the given function or DOC: section
2713title. All other functions and DOC: sections are ignored.
2714
2715May be used multiple times.
2716"""
2717
2718NOSYMBOL_DESC = """
2719Exclude the specified symbol from the output documentation.
2720
2721May be used multiple times.
2722"""
2723
2724FILES_DESC = """
2725Header and C source files to be parsed.
2726"""
2727
2728WARN_CONTENTS_BEFORE_SECTIONS_DESC = """
2729Warns if there are contents before sections (deprecated).
2730
2731This option is kept just for backward-compatibility, but it does nothing,
2732neither here nor at the original Perl script.
2733"""
2734
2735
2736class MsgFormatter(logging.Formatter):
2737    def format(self, record):
2738        record.levelname = record.levelname.capitalize()
2739        return logging.Formatter.format(self, record)
2740
2741def main():
2742    """Main program"""
2743
2744    parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
2745                                     description=DESC)
2746
2747    # Normal arguments
2748
2749    parser.add_argument("-v", "-verbose", "--verbose", action="store_true",
2750                        help="Verbose output, more warnings and other information.")
2751
2752    parser.add_argument("-d", "-debug", "--debug", action="store_true",
2753                        help="Enable debug messages")
2754
2755    parser.add_argument("-M", "-modulename", "--modulename",
2756                        help="Allow setting a module name at the output.")
2757
2758    parser.add_argument("-l", "-enable-lineno", "--enable_lineno",
2759                        action="store_true",
2760                        help="Enable line number output (only in ReST mode)")
2761
2762    # Arguments to control the warning behavior
2763
2764    parser.add_argument("-Wreturn", "--wreturn", action="store_true",
2765                        help="Warns about the lack of a return markup on functions.")
2766
2767    parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-desc",
2768                        action="store_true",
2769                        help="Warns if initial short description is missing")
2770
2771    parser.add_argument("-Wcontents-before-sections",
2772                        "--wcontents-before-sections", action="store_true",
2773                        help=WARN_CONTENTS_BEFORE_SECTIONS_DESC)
2774
2775    parser.add_argument("-Wall", "--wall", action="store_true",
2776                        help="Enable all types of warnings")
2777
2778    parser.add_argument("-Werror", "--werror", action="store_true",
2779                        help="Treat warnings as errors.")
2780
2781    parser.add_argument("-export-file", "--export-file", action='append',
2782                        help=EXPORT_FILE_DESC)
2783
2784    # Output format mutually-exclusive group
2785
2786    out_group = parser.add_argument_group("Output format selection (mutually exclusive)")
2787
2788    out_fmt = out_group.add_mutually_exclusive_group()
2789
2790    out_fmt.add_argument("-m", "-man", "--man", action="store_true",
2791                         help="Output troff manual page format.")
2792    out_fmt.add_argument("-r", "-rst", "--rst", action="store_true",
2793                         help="Output reStructuredText format (default).")
2794    out_fmt.add_argument("-N", "-none", "--none", action="store_true",
2795                         help="Do not output documentation, only warnings.")
2796
2797    # Output selection mutually-exclusive group
2798
2799    sel_group = parser.add_argument_group("Output selection (mutually exclusive)")
2800    sel_mut = sel_group.add_mutually_exclusive_group()
2801
2802    sel_mut.add_argument("-e", "-export", "--export", action='store_true',
2803                         help=EXPORT_DESC)
2804
2805    sel_mut.add_argument("-i", "-internal", "--internal", action='store_true',
2806                         help=INTERNAL_DESC)
2807
2808    sel_mut.add_argument("-s", "-function", "--symbol", action='append',
2809                         help=FUNCTION_DESC)
2810
2811    # This one is valid for all 3 types of filter
2812    parser.add_argument("-n", "-nosymbol", "--nosymbol", action='append',
2813                         help=NOSYMBOL_DESC)
2814
2815    parser.add_argument("files", metavar="FILE",
2816                        nargs="+", help=FILES_DESC)
2817
2818    args = parser.parse_args()
2819
2820    if args.wall:
2821        args.wreturn = True
2822        args.wshort_desc = True
2823        args.wcontents_before_sections = True
2824
2825    logger = logging.getLogger()
2826
2827    if not args.debug:
2828        logger.setLevel(logging.INFO)
2829    else:
2830        logger.setLevel(logging.DEBUG)
2831
2832    formatter = MsgFormatter('%(levelname)s: %(message)s')
2833
2834    handler = logging.StreamHandler()
2835    handler.setFormatter(formatter)
2836
2837    logger.addHandler(handler)
2838
2839    if args.man:
2840        out_style = ManFormat()
2841    elif args.none:
2842        out_style = None
2843    else:
2844        out_style = RestFormat()
2845
2846    kfiles = KernelFiles(files=args.files, verbose=args.verbose,
2847                         out_style=out_style, werror=args.werror,
2848                         wreturn=args.wreturn, wshort_desc=args.wshort_desc,
2849                         wcontents_before_sections=args.wcontents_before_sections,
2850                         modulename=args.modulename,
2851                         export_file=args.export_file)
2852
2853    kfiles.parse()
2854
2855    kfiles.msg(enable_lineno=args.enable_lineno, export=args.export,
2856               internal=args.internal, symbol=args.symbol,
2857               nosymbol=args.nosymbol)
2858
2859
2860# Call main method
2861if __name__ == "__main__":
2862    main()
2863