diff options
| author | Felix (xq) Queißner <git@mq32.de> | 2020-06-09 18:27:38 +0200 |
|---|---|---|
| committer | Felix (xq) Queißner <git@mq32.de> | 2020-06-09 18:27:38 +0200 |
| commit | de8fd9328e9deb0d1ec596d7486686ea3cb688c2 (patch) | |
| tree | 9c25d7568dee522c1d2ed7b6d238d1dad54163f6 /lib/cmark/man/make_man_page.py | |
| parent | 661ddc244793102ee0720871c4edcd64f80bc744 (diff) | |
| download | kristall-de8fd9328e9deb0d1ec596d7486686ea3cb688c2.tar.gz | |
Includes cmark markdown parser library.
Diffstat (limited to 'lib/cmark/man/make_man_page.py')
| -rw-r--r-- | lib/cmark/man/make_man_page.py | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/lib/cmark/man/make_man_page.py b/lib/cmark/man/make_man_page.py new file mode 100644 index 0000000..4b49dbd --- /dev/null +++ b/lib/cmark/man/make_man_page.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python + +# Creates a man page from a C file. + +# first argument if present is path to cmark dynamic library + +# Comments beginning with `/**` are treated as Groff man, except that +# 'this' is converted to \fIthis\f[], and ''this'' to \fBthis\f[]. + +# Non-blank lines immediately following a man page comment are treated +# as function signatures or examples and parsed into .Ft, .Fo, .Fa, .Fc. The +# immediately preceding man documentation chunk is printed after the example +# as a comment on it. + +# That's about it! + +import sys, re, os, platform +from datetime import date +from ctypes import CDLL, c_char_p, c_long, c_void_p + +sysname = platform.system() + +if sysname == 'Darwin': + cmark = CDLL("build/src/libcmark.dylib") +else: + cmark = CDLL("build/src/libcmark.so") + +parse_document = cmark.cmark_parse_document +parse_document.restype = c_void_p +parse_document.argtypes = [c_char_p, c_long] + +render_man = cmark.cmark_render_man +render_man.restype = c_char_p +render_man.argtypes = [c_void_p, c_long, c_long] + +def md2man(text): + if sys.version_info >= (3,0): + textbytes = text.encode('utf-8') + textlen = len(textbytes) + return render_man(parse_document(textbytes, textlen), 0, 65).decode('utf-8') + else: + textbytes = text + textlen = len(text) + return render_man(parse_document(textbytes, textlen), 0, 72) + +comment_start_re = re.compile('^\/\*\* ?') +comment_delim_re = re.compile('^[/ ]\** ?') +comment_end_re = re.compile('^ \**\/') +function_re = re.compile('^ *(?:CMARK_EXPORT\s+)?(?P<type>(?:const\s+)?\w+(?:\s*[*])?)\s*(?P<name>\w+)\s*\((?P<args>[^)]*)\)') +blank_re = re.compile('^\s*$') +macro_re = re.compile('CMARK_EXPORT *') +typedef_start_re = re.compile('typedef.*{$') +typedef_end_re = re.compile('}') +single_quote_re = re.compile("(?<!\w)'([^']+)'(?!\w)") +double_quote_re = re.compile("(?<!\w)''([^']+)''(?!\w)") + +def handle_quotes(s): + return re.sub(double_quote_re, '**\g<1>**', re.sub(single_quote_re, '*\g<1>*', s)) + +typedef = False +mdlines = [] +chunk = [] +sig = [] + +if len(sys.argv) > 1: + sourcefile = sys.argv[1] +else: + print("Usage: make_man_page.py sourcefile") + exit(1) + +with open(sourcefile, 'r') as cmarkh: + state = 'default' + for line in cmarkh: + # state transition + oldstate = state + if comment_start_re.match(line): + state = 'man' + elif comment_end_re.match(line) and state == 'man': + continue + elif comment_delim_re.match(line) and state == 'man': + state = 'man' + elif not typedef and blank_re.match(line): + state = 'default' + elif typedef and typedef_end_re.match(line): + typedef = False + elif typedef_start_re.match(line): + typedef = True + state = 'signature' + elif state == 'man': + state = 'signature' + + # handle line + if state == 'man': + chunk.append(handle_quotes(re.sub(comment_delim_re, '', line))) + elif state == 'signature': + ln = re.sub(macro_re, '', line) + if typedef or not re.match(blank_re, ln): + sig.append(ln) + elif oldstate == 'signature' and state != 'signature': + if len(mdlines) > 0 and mdlines[-1] != '\n': + mdlines.append('\n') + rawsig = ''.join(sig) + m = function_re.match(rawsig) + mdlines.append('.PP\n') + if m: + mdlines.append('\\fI' + m.group('type') + '\\f[]' + ' ') + mdlines.append('\\fB' + m.group('name') + '\\f[]' + '(') + first = True + for argument in re.split(',', m.group('args')): + if not first: + mdlines.append(', ') + first = False + mdlines.append('\\fI' + argument.strip() + '\\f[]') + mdlines.append(')\n') + else: + mdlines.append('.nf\n\\fC\n.RS 0n\n') + mdlines += sig + mdlines.append('.RE\n\\f[]\n.fi\n') + if len(mdlines) > 0 and mdlines[-1] != '\n': + mdlines.append('\n') + mdlines += md2man(''.join(chunk)) + mdlines.append('\n') + chunk = [] + sig = [] + elif oldstate == 'man' and state != 'signature': + if len(mdlines) > 0 and mdlines[-1] != '\n': + mdlines.append('\n') + mdlines += md2man(''.join(chunk)) # add man chunk + chunk = [] + mdlines.append('\n') + +sys.stdout.write('.TH ' + os.path.basename(sourcefile).replace('.h','') + ' 3 "' + date.today().strftime('%B %d, %Y') + '" "LOCAL" "Library Functions Manual"\n') +sys.stdout.write(''.join(mdlines)) |
