for parameter in args.parameterlist: self.data += f"{outer}``{parameter}``\n" if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed: self.output_highlight(args.parameterdescs[parameter]) else: self.data += f"{self.lineprefix}*undescribed*\n\n" self.data += "\n" self.lineprefix = oldprefix self.out_section(args) def out_var(self, fname, name, args): oldprefix = self.lineprefix ln = args.declaration_start_line full_proto = args.other_stuff.get("full_proto") if not full_proto: raise KeyError(f"Can't find full proto for {name} variable") self.lineprefix = " " self.data += f"\n\n.. c:macro:: {name}\n\n{self.lineprefix}``{full_proto}``\n\n" self.print_lineno(ln) self.output_highlight(args.get('purpose', '')) self.data += "\n" if args.other_stuff["default_val"]: self.data += f'{self.lineprefix}**Initialization**\n\n' self.output_highlight(f'default: ``{args.other_stuff["default_val"]}``') self.out_section(args) def out_typedef(self, fname, name, args): oldprefix = self.lineprefix ln = args.declaration_start_line self.data += f"\n\n.. c:type:: {name}\n\n" self.print_lineno(ln) self.lineprefix = " " self.output_highlight(args.get('purpose', '')) self.data += "\n" self.lineprefix = oldprefix self.out_section(args) def out_struct(self, fname, name, args): purpose = args.get('purpose', "") declaration = args.get('definition', "") dtype = args.type ln = args.declaration_start_line self.data += f"\n\n.. c:{dtype}:: {name}\n\n" self.print_lineno(ln) oldprefix = self.lineprefix self.lineprefix += " " self.output_highlight(purpose) self.data += "\n" self.data += ".. container:: kernelindent\n\n" self.data += f"{self.lineprefix}**Definition**::\n\n" self.lineprefix = self.lineprefix + " " declaration = declaration.replace("\t", self.lineprefix) self.data += f"{self.lineprefix}{dtype} {name}" + ' {' + "\n" self.data += f"{declaration}{self.lineprefix}" + "};\n\n" self.lineprefix = " " self.data += f"{self.lineprefix}**Members**\n\n" for parameter in args.parameterlist: if not parameter or parameter.startswith("#"): continue parameter_name = parameter.split("[", maxsplit=1)[0] if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: continue self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) self.data += f"{self.lineprefix}``{parameter}``\n" self.lineprefix = " " self.output_highlight(args.parameterdescs[parameter_name]) self.lineprefix = " " self.data += "\n" self.data += "\n" self.lineprefix = oldprefix self.out_section(args) class ManFormat(OutputFormat): """ Consts and functions used by man pages output. This class has one mandatory parameter and some optional ones, which are needed to define the title header contents: ``modulename`` Defines the module name to be used at the troff ``.TH`` output. This argument is optional. If not specified, it will be filled with the directory which contains the documented file. ``section`` Usually a numeric value from 0 to 9, but man pages also accept some strings like "p". Defauls to ``9`` ``manual`` Defaults to ``Kernel API Manual``. The above controls the output of teh corresponding fields on troff title headers, which will be filled like this:: .TH "{name}" {section} "{date}" "{modulename}" "{manual}" where ``name``` will match the API symbol name, and ``date`` will be either the date where the Kernel was compiled or the current date """ highlights = ( (type_constant, r"\1"), (type_constant2, r"\1"), (type_func, r"\\fB\1\\fP"), (type_enum, r"\\fI\1\\fP"), (type_struct, r"\\fI\1\\fP"), (type_typedef, r"\\fI\1\\fP"), (type_union, r"\\fI\1\\fP"), (type_param, r"\\fI\1\\fP"), (type_param_ref, r"\\fI\1\2\\fP"), (type_member, r"\\fI\1\2\3\\fP"), (type_fallback, r"\\fI\1\\fP") ) blankline = "" #: Allowed timestamp formats. date_formats = [ "%a %b %d %H:%M:%S %Z %Y", "%a %b %d %H:%M:%S %Y", "%Y-%m-%d", "%b %d %Y", "%B %d %Y", "%m %d %Y", ] def modulename(self, args): if self._modulename: return self._modulename return os.path.dirname(args.fname) def emit_th(self, name, args): """Emit a title header line.""" title = name.strip() module = self.modulename(args) self.data += f'.TH "{title}" {self.section} "{self.date}" ' self.data += f'"{module}" "{self.manual}"\n' def __init__(self, modulename=None, section="9", manual="Kernel API Manual"): """ Creates class variables. Not really mandatory, but it is a good coding style and makes pylint happy. """ super().__init__() self._modulename = modulename self.section = section self.manual = manual self.symbols = [] dt = None tstamp = os.environ.get("KBUILD_BUILD_TIMESTAMP") if tstamp: for fmt in self.date_formats: try: dt = datetime.strptime(tstamp, fmt) break except ValueError: pass if not dt: dt = datetime.now() self.date = dt.strftime("%B %Y") def arg_name(self, args, name): """ Return the name that will be used for the man page. As we may have the same name on different namespaces, prepend the data type for all types except functions and typedefs. The doc section is special: it uses the modulename. """ dtype = args.type if dtype == "doc": return name# return os.path.basename(self.modulename(args)) if dtype in ["function", "typedef"]: return name return f"{dtype} {name}" def set_symbols(self, symbols): """ Get a list of all symbols from kernel_doc. Man pages will uses it to add a SEE ALSO section with other symbols at the same file. """ self.symbols = symbols def out_tail(self, fname, name, args): """Adds a tail for all man pages.""" # SEE ALSO section self.data += f'.SH "SEE ALSO"' + "\n.PP\n" self.data += (f"Kernel file \\fB{args.fname}\\fR\n") if len(self.symbols) >= 2: cur_name = self.arg_name(args, name) related = [] for arg in self.symbols: out_name = self.arg_name(arg, arg.name) if cur_name == out_name: continue related.append(f"\\fB{out_name}\\fR(9)") self.data += ",\n".join(related) + "\n" # TODO: does it make sense to add other sections? Maybe # REPORTING ISSUES? LICENSE? def msg(self, fname, name, args): """ Handles a single entry from kernel-doc parser. Add a tail at the end of man pages output. """ super().msg(fname, name, args) self.out_tail(fname, name, args) return self.data def emit_table(self, colspec_row, rows): if not rows: return "" out = "" colspec = "\t".join(["l"] * len(rows[0])) out += "\n.TS\n" out += "box;\n" out += f"{colspec}.\n" if colspec_row: out_row = [] for text in colspec_row: out_row.append(f"\\fB{text}\\fP") out += "\t".join(out_row) + "\n_\n" for r in rows: out += "\t".join(r) + "\n" out += ".TE\n" return out def grid_table(self, lines, start): """ Ancillary function to help handling a grid table inside the text. """ i = start + 1 rows = [] colspec_row = None while i < len(lines): line = lines[i] if KernRe(r"^\s*\|.*\|\s*$").match(line): parts = [] for p in line.strip('|').split('|'): parts.append(p.strip()) rows.append(parts) elif KernRe(r'^\+\=[\+\=]+\+\s*$').match(line): if rows and rows[0]: if not colspec_row: colspec_row = [""] * len(rows[0]) for j in range(0, len(rows[0])): content = [] for row in rows: content.append(row[j]) colspec_row[j] = " ".join(content) rows = [] elif KernRe(r"^\s*\+[-+]+\+.*$").match(line): pass else: break i += 1 return i, self.emit_table(colspec_row, rows) def simple_table(self, lines, start): """ Ancillary function to help handling a simple table inside the text. """ i = start rows = [] colspec_row = None pos = [] for m in KernRe(r'\=+').finditer(lines[i]): pos.append((m.start(), m.end() - 1)) i += 1 while i < len(lines): line = lines[i] if KernRe(r"^\s*[\=]+[ \t\=]+$").match(line): i += 1 break elif KernRe(r'^[\s=]+$').match(line): if rows and rows[0]: if not colspec_row: colspec_row = [""] * len(rows[0]) for j in range(0, len(rows[0])): content = [] for row in rows: content.append(row[j]) colspec_row[j] = " ".join(content) rows = [] else: row = [""] * len(pos) for j in range(0, len(pos)): start, end = pos[j] row[j] = line[start:end].strip() rows.append(row) i += 1 return i, self.emit_table(colspec_row, rows) def code_block(self, lines, start): """ Ensure that code blocks won't be messed up at the output. By default, troff join lines at the same paragraph. Disable it, on code blocks. """ line = lines[start] if "code-block" in line: out = "\n.nf\n" elif line.startswith("..") and line.endswith("::"): # # Handle note, warning, error, ... markups # line = line[2:-1].strip().upper() out = f"\n.nf\n\\fB{line}\\fP\n" elif line.endswith("::"): out = line[:-1] out += "\n.nf\n" else: # Just in case. Should never happen in practice out = "\n.nf\n" i = start + 1 ident = None while i < len(lines): line = lines[i] m = KernRe(r"\S").match(line) if not m: out += line + "\n" i += 1 continue pos = m.start() if not ident: if pos > 0: ident = pos else: out += "\n.fi\n" if i > start + 1: return i - 1, out else: # Just in case. Should never happen in practice return i, out if pos >= ident: out += line + "\n" i += 1 continue break out += "\n.fi\n" return i, out def output_highlight(self, block): """ Outputs a C symbol that may require being highlighted with self.highlights variable using troff syntax. """ contents = self.highlight_block(block) if isinstance(contents, list): contents = "\n".join(contents) lines = contents.strip("\n").split("\n") i = 0 while i < len(lines): org_line = lines[i] line = KernRe(r"^\s*").sub("", org_line) if line: if KernRe(r"^\+\-[-+]+\+.*$").match(line): i, text = self.grid_table(lines, i) self.data += text continue if KernRe(r"^\=+[ \t]\=[ \t\=]+$").match(line): i, text = self.simple_table(lines, i) self.data += text continue if line.endswith("::") or KernRe(r"\.\.\s+code-block.*::").match(line): i, text = self.code_block(lines, i) self.data += text continue if line[0] == ".": self.data += "\\&" + line + "\n" i += 1 continue # # Handle lists # line = KernRe(r'^[-*]\s+').sub(r'.IP \[bu]\n', line) line = KernRe(r'^(\d+|a-z)[\.\)]\s+').sub(r'.IP \1\n', line) else: line = ".PP\n" i += 1 self.data += line + "\n" Lines 501–1000 of 1160. Previous lines Load more
for parameter in args.parameterlist: self.data += f"{outer}``{parameter}``\n" if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed: self.output_highlight(args.parameterdescs[parameter]) else: self.data += f"{self.lineprefix}*undescribed*\n\n" self.data += "\n" self.lineprefix = oldprefix self.out_section(args) def out_var(self, fname, name, args): oldprefix = self.lineprefix ln = args.declaration_start_line full_proto = args.other_stuff.get("full_proto") if not full_proto: raise KeyError(f"Can't find full proto for {name} variable") self.lineprefix = " " self.data += f"\n\n.. c:macro:: {name}\n\n{self.lineprefix}``{full_proto}``\n\n" self.print_lineno(ln) self.output_highlight(args.get('purpose', '')) self.data += "\n" if args.other_stuff["default_val"]: self.data += f'{self.lineprefix}**Initialization**\n\n' self.output_highlight(f'default: ``{args.other_stuff["default_val"]}``') self.out_section(args) def out_typedef(self, fname, name, args): oldprefix = self.lineprefix ln = args.declaration_start_line self.data += f"\n\n.. c:type:: {name}\n\n" self.print_lineno(ln) self.lineprefix = " " self.output_highlight(args.get('purpose', '')) self.data += "\n" self.lineprefix = oldprefix self.out_section(args) def out_struct(self, fname, name, args): purpose = args.get('purpose', "") declaration = args.get('definition', "") dtype = args.type ln = args.declaration_start_line self.data += f"\n\n.. c:{dtype}:: {name}\n\n" self.print_lineno(ln) oldprefix = self.lineprefix self.lineprefix += " " self.output_highlight(purpose) self.data += "\n" self.data += ".. container:: kernelindent\n\n" self.data += f"{self.lineprefix}**Definition**::\n\n" self.lineprefix = self.lineprefix + " " declaration = declaration.replace("\t", self.lineprefix) self.data += f"{self.lineprefix}{dtype} {name}" + ' {' + "\n" self.data += f"{declaration}{self.lineprefix}" + "};\n\n" self.lineprefix = " " self.data += f"{self.lineprefix}**Members**\n\n" for parameter in args.parameterlist: if not parameter or parameter.startswith("#"): continue parameter_name = parameter.split("[", maxsplit=1)[0] if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: continue self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) self.data += f"{self.lineprefix}``{parameter}``\n" self.lineprefix = " " self.output_highlight(args.parameterdescs[parameter_name]) self.lineprefix = " " self.data += "\n" self.data += "\n" self.lineprefix = oldprefix self.out_section(args) class ManFormat(OutputFormat): """ Consts and functions used by man pages output. This class has one mandatory parameter and some optional ones, which are needed to define the title header contents: ``modulename`` Defines the module name to be used at the troff ``.TH`` output. This argument is optional. If not specified, it will be filled with the directory which contains the documented file. ``section`` Usually a numeric value from 0 to 9, but man pages also accept some strings like "p". Defauls to ``9`` ``manual`` Defaults to ``Kernel API Manual``. The above controls the output of teh corresponding fields on troff title headers, which will be filled like this:: .TH "{name}" {section} "{date}" "{modulename}" "{manual}" where ``name``` will match the API symbol name, and ``date`` will be either the date where the Kernel was compiled or the current date """ highlights = ( (type_constant, r"\1"), (type_constant2, r"\1"), (type_func, r"\\fB\1\\fP"), (type_enum, r"\\fI\1\\fP"), (type_struct, r"\\fI\1\\fP"), (type_typedef, r"\\fI\1\\fP"), (type_union, r"\\fI\1\\fP"), (type_param, r"\\fI\1\\fP"), (type_param_ref, r"\\fI\1\2\\fP"), (type_member, r"\\fI\1\2\3\\fP"), (type_fallback, r"\\fI\1\\fP") ) blankline = "" #: Allowed timestamp formats. date_formats = [ "%a %b %d %H:%M:%S %Z %Y", "%a %b %d %H:%M:%S %Y", "%Y-%m-%d", "%b %d %Y", "%B %d %Y", "%m %d %Y", ] def modulename(self, args): if self._modulename: return self._modulename return os.path.dirname(args.fname) def emit_th(self, name, args): """Emit a title header line.""" title = name.strip() module = self.modulename(args) self.data += f'.TH "{title}" {self.section} "{self.date}" ' self.data += f'"{module}" "{self.manual}"\n' def __init__(self, modulename=None, section="9", manual="Kernel API Manual"): """ Creates class variables. Not really mandatory, but it is a good coding style and makes pylint happy. """ super().__init__() self._modulename = modulename self.section = section self.manual = manual self.symbols = [] dt = None tstamp = os.environ.get("KBUILD_BUILD_TIMESTAMP") if tstamp: for fmt in self.date_formats: try: dt = datetime.strptime(tstamp, fmt) break except ValueError: pass if not dt: dt = datetime.now() self.date = dt.strftime("%B %Y") def arg_name(self, args, name): """ Return the name that will be used for the man page. As we may have the same name on different namespaces, prepend the data type for all types except functions and typedefs. The doc section is special: it uses the modulename. """ dtype = args.type if dtype == "doc": return name # return os.path.basename(self.modulename(args)) if dtype in ["function", "typedef"]: return name return f"{dtype} {name}" def set_symbols(self, symbols): """ Get a list of all symbols from kernel_doc. Man pages will uses it to add a SEE ALSO section with other symbols at the same file. """ self.symbols = symbols def out_tail(self, fname, name, args): """Adds a tail for all man pages.""" # SEE ALSO section self.data += f'.SH "SEE ALSO"' + "\n.PP\n" self.data += (f"Kernel file \\fB{args.fname}\\fR\n") if len(self.symbols) >= 2: cur_name = self.arg_name(args, name) related = [] for arg in self.symbols: out_name = self.arg_name(arg, arg.name) if cur_name == out_name: continue related.append(f"\\fB{out_name}\\fR(9)") self.data += ",\n".join(related) + "\n" # TODO: does it make sense to add other sections? Maybe # REPORTING ISSUES? LICENSE? def msg(self, fname, name, args): """ Handles a single entry from kernel-doc parser. Add a tail at the end of man pages output. """ super().msg(fname, name, args) self.out_tail(fname, name, args) return self.data def emit_table(self, colspec_row, rows): if not rows: return "" out = "" colspec = "\t".join(["l"] * len(rows[0])) out += "\n.TS\n" out += "box;\n" out += f"{colspec}.\n" if colspec_row: out_row = [] for text in colspec_row: out_row.append(f"\\fB{text}\\fP") out += "\t".join(out_row) + "\n_\n" for r in rows: out += "\t".join(r) + "\n" out += ".TE\n" return out def grid_table(self, lines, start): """ Ancillary function to help handling a grid table inside the text. """ i = start + 1 rows = [] colspec_row = None while i < len(lines): line = lines[i] if KernRe(r"^\s*\|.*\|\s*$").match(line): parts = [] for p in line.strip('|').split('|'): parts.append(p.strip()) rows.append(parts) elif KernRe(r'^\+\=[\+\=]+\+\s*$').match(line): if rows and rows[0]: if not colspec_row: colspec_row = [""] * len(rows[0]) for j in range(0, len(rows[0])): content = [] for row in rows: content.append(row[j]) colspec_row[j] = " ".join(content) rows = [] elif KernRe(r"^\s*\+[-+]+\+.*$").match(line): pass else: break i += 1 return i, self.emit_table(colspec_row, rows) def simple_table(self, lines, start): """ Ancillary function to help handling a simple table inside the text. """ i = start rows = [] colspec_row = None pos = [] for m in KernRe(r'\=+').finditer(lines[i]): pos.append((m.start(), m.end() - 1)) i += 1 while i < len(lines): line = lines[i] if KernRe(r"^\s*[\=]+[ \t\=]+$").match(line): i += 1 break elif KernRe(r'^[\s=]+$').match(line): if rows and rows[0]: if not colspec_row: colspec_row = [""] * len(rows[0]) for j in range(0, len(rows[0])): content = [] for row in rows: content.append(row[j]) colspec_row[j] = " ".join(content) rows = [] else: row = [""] * len(pos) for j in range(0, len(pos)): start, end = pos[j] row[j] = line[start:end].strip() rows.append(row) i += 1 return i, self.emit_table(colspec_row, rows) def code_block(self, lines, start): """ Ensure that code blocks won't be messed up at the output. By default, troff join lines at the same paragraph. Disable it, on code blocks. """ line = lines[start] if "code-block" in line: out = "\n.nf\n" elif line.startswith("..") and line.endswith("::"): # # Handle note, warning, error, ... markups # line = line[2:-1].strip().upper() out = f"\n.nf\n\\fB{line}\\fP\n" elif line.endswith("::"): out = line[:-1] out += "\n.nf\n" else: # Just in case. Should never happen in practice out = "\n.nf\n" i = start + 1 ident = None while i < len(lines): line = lines[i] m = KernRe(r"\S").match(line) if not m: out += line + "\n" i += 1 continue pos = m.start() if not ident: if pos > 0: ident = pos else: out += "\n.fi\n" if i > start + 1: return i - 1, out else: # Just in case. Should never happen in practice return i, out if pos >= ident: out += line + "\n" i += 1 continue break out += "\n.fi\n" return i, out def output_highlight(self, block): """ Outputs a C symbol that may require being highlighted with self.highlights variable using troff syntax. """ contents = self.highlight_block(block) if isinstance(contents, list): contents = "\n".join(contents) lines = contents.strip("\n").split("\n") i = 0 while i < len(lines): org_line = lines[i] line = KernRe(r"^\s*").sub("", org_line) if line: if KernRe(r"^\+\-[-+]+\+.*$").match(line): i, text = self.grid_table(lines, i) self.data += text continue if KernRe(r"^\=+[ \t]\=[ \t\=]+$").match(line): i, text = self.simple_table(lines, i) self.data += text continue if line.endswith("::") or KernRe(r"\.\.\s+code-block.*::").match(line): i, text = self.code_block(lines, i) self.data += text continue if line[0] == ".": self.data += "\\&" + line + "\n" i += 1 continue # # Handle lists # line = KernRe(r'^[-*]\s+').sub(r'.IP \[bu]\n', line) line = KernRe(r'^(\d+|a-z)[\.\)]\s+').sub(r'.IP \1\n', line) else: line = ".PP\n" i += 1 self.data += line + "\n"