diff --git a/crypto/openssl/BSDmakefile b/crypto/openssl/BSDmakefile --- a/crypto/openssl/BSDmakefile +++ b/crypto/openssl/BSDmakefile @@ -64,6 +64,14 @@ ${LCRYPTO_DOC}/man/man1/ \ ${SRCTOP}/secure/usr.bin/openssl/man + # Regenerate manpage Makefiles from the manpages themselves. +.for man_sect in 3 5 7 + find ${SRCTOP}/secure/lib/libcrypto/man/man${man_sect} \ + -name "*.${man_sect}" -type f \ + -exec ${LCRYPTO_SRC}/freebsd/generate_libcrypto_manpage_makefiles.py {} \; | \ + sort > ${SRCTOP}/secure/lib/libcrypto/man/man${man_sect}/Makefile +.endfor + # This doesn't use standard patching since the generated files can vary # depending on the host architecture. diff --git a/crypto/openssl/freebsd/generate_libcrypto_manpage_makefiles.py b/crypto/openssl/freebsd/generate_libcrypto_manpage_makefiles.py new file mode 100755 --- /dev/null +++ b/crypto/openssl/freebsd/generate_libcrypto_manpage_makefiles.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +"""Parse manpages provided on the command line and output Makefile data for OpenSSL.""" + +from __future__ import annotations + +import argparse +import collections +import pathlib +import re +import sys +import typing + +if typing.TYPE_CHECKING: + import io + + +# ruff: noqa: S101, T201 + +IX_RE = re.compile(r'\.[iI][xX] Title ".+\s+(\d+)ossl"') +NAME_SEP_RE = re.compile(r",\s*") +NAME_TOKEN_TRAILING_GARBAGE_RE = re.compile(r"[^\w,\s].*") +SH_RE = re.compile(r"\.[sS][hH]\s+(.+)") + +MANPAGES = [] +MANPAGE_LINKS = collections.defaultdict(list) + + +def parse_manpage(manpage_fobj: io.TextIOBase) -> None: + """Parse a manpage for a name/links.""" + capture_aliases = False + aliases = [] + section = None + + manpage_short = pathlib.Path(manpage_fobj.name).name + manpage_short_stem = pathlib.Path(manpage_fobj.name).stem + + for line in manpage_fobj: + if (res := IX_RE.match(line)) is not None: + section = res.group(1) + elif (res := SH_RE.match(line)) is not None: + # ruff: noqa: SIM108 + if res.group(1) == "NAME": + capture_aliases = True + else: + capture_aliases = False + else: + if not capture_aliases: + continue + if "-" in line: + capture_aliases = False + # ruff: noqa: PLW2901 + line = line.rstrip() + # ruff: noqa: PLW2901 + line = NAME_TOKEN_TRAILING_GARBAGE_RE.sub("", line) + names = [ + match.strip() + for match in NAME_SEP_RE.split(line) + if match.strip() and match.strip() != manpage_short_stem + ] + if names: + aliases.extend(names) + + assert not aliases or section is not None + + MANPAGES.append(manpage_short) + MANPAGE_LINKS[manpage_short] = [ + f"{manpage_short} {alias}.{section}" + for alias in aliases + if f"{alias}.{section}" != manpage_short + ] + + +def main(argv: list[str] | None = None) -> int: + """Eponymous main.""" + argparser = argparse.ArgumentParser() + argparser.add_argument( + "manpages", + metavar="MANPAGE", + nargs="+", + type=argparse.FileType("r"), + ) + args = argparser.parse_args(argv) + + for manpage_fobj in args.manpages: + with manpage_fobj: + parse_manpage(manpage_fobj) + + for manpage in sorted(MANPAGES): + print(f"MAN+= {manpage}") + for manpage_link in sorted(MANPAGE_LINKS[manpage]): + print(f"MLINKS+= {manpage_link}") + + return 0 + + +if __name__ == "__main__": + sys.exit(main())