#!/usr/bin/env python3 # # markdown-preprocess - filter *.md.in files, convert to .md # import glob import os import re import sys def main(): script_dir = os.path.abspath(os.path.dirname(__file__)) man_dir = os.path.join(script_dir,"../docs/source/markdown") try: os.chdir(man_dir) except FileNotFoundError: raise Exception("Please invoke me from the base repo dir") # If called with args, process only those files infiles = [ os.path.basename(x) for x in sys.argv[1:] ] if len(infiles) == 0: # Called without args: process all *.md.in files infiles = glob.glob('*.md.in') for infile in infiles: process(infile) def process(infile): # Some options are the same between containers and pods; determine # which description to use from the name of the source man page. pod_or_container = 'container' if '-pod-' in infile or '-kube-' in infile: pod_or_container = 'pod' # Sometimes a man page includes the subcommand. subcommand = podman_subcommand(infile) # foo.md.in -> foo.md -- but always write to a tmpfile outfile = os.path.splitext(infile)[0] outfile_tmp = outfile + '.tmp.' + str(os.getpid()) # print("got here: ",infile, " -> ", outfile) with open(infile, 'r') as fh_in, open(outfile_tmp, 'w') as fh_out: for line in fh_in: # '@@option foo' -> include file options/foo.md if line.startswith('@@option '): _, optionname = line.strip().split(" ") optionfile = os.path.join("options", optionname + '.md') # Comment intended to help someone viewing the .md file. # Leading newline is important because if two lines are # consecutive without a break, sphinx (but not go-md2man) # treats them as one line and will unwantedly render the # comment in its output. fh_out.write("\n[//]: # (BEGIN included file " + optionfile + ")\n") with open(optionfile, 'r') as fh_optfile: for opt_line in fh_optfile: opt_line = replace_type(opt_line, pod_or_container) opt_line = opt_line.replace('<>', subcommand) fh_out.write(opt_line) fh_out.write("\n[//]: # (END included file " + optionfile + ")\n") else: fh_out.write(line) os.chmod(outfile_tmp, 0o444) os.rename(outfile_tmp, outfile) # Given a file path of the form podman-foo-bar.1.md.in, return "foo bar" def podman_subcommand(string: str) -> str: # Special case: 'podman-pod-start' becomes just 'start' if string.startswith("podman-pod-"): string = string[len("podman-pod-"):] if string.startswith("podman-"): string = string[len("podman-"):] if string.endswith(".1.md.in"): string = string[:-len(".1.md.in")] return string.replace("-", " ") # Replace instances of '<>' with the desired one (based on # 'type' which is 'pod' or 'container'). def replace_type(line: str, type: str) -> str: # Internal helper function: determines the desired half of the string def replwith(matchobj): lhs, rhs = matchobj[0].split('|') # Strip off '<<' and '>>' lhs = lhs[2:] rhs = rhs[:len(rhs)-2] # Check both sides for 'pod' followed by (non-"m" or end-of-string). # The non-m prevents us from triggering on 'podman', which could # conceivably be present in both sides. And we check for 'pod', # not 'container', because it's possible to have something like # <>. if re.match('.*pod([^m]|$)', lhs, re.IGNORECASE): if re.match('.*pod([^m]|$)', rhs, re.IGNORECASE): raise Exception("'%s' matches 'pod' in both left and right sides" % matchobj[0]) # Only left-hand side has "pod" if type == 'pod': return lhs else: return rhs else: if not re.match('.*pod([^m]|$)', rhs, re.IGNORECASE): raise Exception("'%s' does not match 'pod' in either side" % matchobj[0]) if type == 'pod': return rhs else: return lhs return re.sub('<<[^\|>]*\|[^\|>]*>>', replwith, line) if __name__ == "__main__": main()