From: Peter Eisentraut Date: Sat, 29 Jun 2019 13:47:43 +0000 (+0200) Subject: Rewrite man page filter to work independent of Pandoc X-Git-Tag: pgbouncer_1_10_0~2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=83c140b2082290171e61f16ca89a82e3fa3cb0e4;p=pgbouncer Rewrite man page filter to work independent of Pandoc This allows it to work with really old Pandoc versions that don't have the --filter option (e.g., on CentOS 6). This just makes it a plain text-munging filter script. --- diff --git a/.gitignore b/.gitignore index 79216fe..4e9ae89 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,4 @@ *.xml *.exe *.gz -*.pyc *.swp diff --git a/doc/Makefile b/doc/Makefile index afd3b60..1ee6f22 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -6,7 +6,9 @@ dist_man_MANS = $(manpages) EXTRA_DIST = config.md usage.md Makefile $(manpages) \ frag-config-man.md frag-usage-man.md \ - filter.py pandocfilters.py + filter.py + +CLEANFILES = pgbouncer_1.md pgbouncer_5.md # make maintainer-clean removes those MAINTAINERCLEANFILES = $(manpages) @@ -15,12 +17,18 @@ SUBLOC = doc abs_top_srcdir ?= $(CURDIR)/.. include $(abs_top_srcdir)/lib/mk/antimake.mk +PYTHON = python + +pgbouncer.%: pgbouncer_%.md + $(PANDOC) -s -t man -o $@ $< + +pgbouncer_1.md: filter.py frag-usage-man.md usage.md + $(PYTHON) $^ >$@ -pgbouncer.1: frag-usage-man.md usage.md - $(PANDOC) --filter=./filter.py -s -t man -o $@ $^ +pgbouncer_5.md: filter.py frag-config-man.md config.md + $(PYTHON) $^ >$@ -pgbouncer.5: frag-config-man.md config.md - $(PANDOC) --filter=./filter.py -s -t man -o $@ $^ +.INTERMEDIATE: pgbouncer_1.md pgbouncer_5.md web: make -C ../../pgbouncer.github.io diff --git a/doc/filter.py b/doc/filter.py old mode 100755 new mode 100644 index 97a9ec0..04ea1ed --- a/doc/filter.py +++ b/doc/filter.py @@ -1,26 +1,16 @@ #!/usr/bin/env python -from pandocfilters import toJSONFilter, walk, Str, Header - - -def caps(key, value, fmt, meta): - if key == "Str": - return Str(value.upper()) - - -def manify(key, value, fmt, meta): - if key == "Header": - # drop level-1 header - if value[0] == 1: - return [] - - # decrease level of all headers by 1 - value[0] -= 1 - - # convert level-1 headers to uppercase - if value[0] == 1: - return Header(*walk(value, caps, fmt, meta)) - - -if __name__ == "__main__": - toJSONFilter(manify) +import fileinput +import sys + +for line in fileinput.input(): + # drop level-1 header + if line.startswith('# '): + continue + # decrease level of all headers by 1 + if line.startswith('##'): + line = line.replace('#', '', 1) + # convert level-1 headers to uppercase + if line.startswith('# '): + line = line.upper() + sys.stdout.write(line) diff --git a/doc/pandocfilters.py b/doc/pandocfilters.py deleted file mode 100644 index 81b7269..0000000 --- a/doc/pandocfilters.py +++ /dev/null @@ -1,288 +0,0 @@ -# Author: John MacFarlane -# Copyright: (C) 2013 John MacFarlane -# License: BSD3 - -""" -Functions to aid writing python scripts that process the pandoc -AST serialized as JSON. -""" - -import codecs -import hashlib -import io -import json -import os -import sys - - -# some utility-functions: make it easier to create your own filters - - -def get_filename4code(module, content, ext=None): - """Generate filename based on content - - The function ensures that the (temporary) directory exists, so that the - file can be written. - - Example: - filename = get_filename4code("myfilter", code) - """ - imagedir = module + "-images" - fn = hashlib.sha1(content.encode(sys.getfilesystemencoding())).hexdigest() - try: - os.mkdir(imagedir) - sys.stderr.write('Created directory ' + imagedir + '\n') - except OSError: - pass - if ext: - fn += "." + ext - return os.path.join(imagedir, fn) - -def get_value(kv, key, value = None): - """get value from the keyvalues (options)""" - res = [] - for k, v in kv: - if k == key: - value = v - else: - res.append([k, v]) - return value, res - -def get_caption(kv): - """get caption from the keyvalues (options) - - Example: - if key == 'CodeBlock': - [[ident, classes, keyvals], code] = value - caption, typef, keyvals = get_caption(keyvals) - ... - return Para([Image([ident, [], keyvals], caption, [filename, typef])]) - """ - caption = [] - typef = "" - value, res = get_value(kv, u"caption") - if value is not None: - caption = [Str(value)] - typef = "fig:" - - return caption, typef, res - - -def get_extension(format, default, **alternates): - """get the extension for the result, needs a default and some specialisations - - Example: - filetype = get_extension(format, "png", html="svg", latex="eps") - """ - try: - return alternates[format] - except KeyError: - return default - -# end of utilities - - -def walk(x, action, format, meta): - """Walk a tree, applying an action to every object. - Returns a modified tree. An action is a function of the form - `action(key, value, format, meta)`, where: - - * `key` is the type of the pandoc object (e.g. 'Str', 'Para') `value` is - * the contents of the object (e.g. a string for 'Str', a list of - inline elements for 'Para') - * `format` is the target output format (as supplied by the - `format` argument of `walk`) - * `meta` is the document's metadata - - The return of an action is either: - - * `None`: this means that the object should remain unchanged - * a pandoc object: this will replace the original object - * a list of pandoc objects: these will replace the original object; the - list is merged with the neighbors of the orignal objects (spliced into - the list the original object belongs to); returning an empty list deletes - the object - """ - if isinstance(x, list): - array = [] - for item in x: - if isinstance(item, dict) and 't' in item: - res = action(item['t'], - item['c'] if 'c' in item else None, format, meta) - if res is None: - array.append(walk(item, action, format, meta)) - elif isinstance(res, list): - for z in res: - array.append(walk(z, action, format, meta)) - else: - array.append(walk(res, action, format, meta)) - else: - array.append(walk(item, action, format, meta)) - return array - elif isinstance(x, dict): - for k in x: - x[k] = walk(x[k], action, format, meta) - return x - else: - return x - -def toJSONFilter(action): - """Like `toJSONFilters`, but takes a single action as argument. - """ - toJSONFilters([action]) - - -def toJSONFilters(actions): - """Generate a JSON-to-JSON filter from stdin to stdout - - The filter: - - * reads a JSON-formatted pandoc document from stdin - * transforms it by walking the tree and performing the actions - * returns a new JSON-formatted pandoc document to stdout - - The argument `actions` is a list of functions of the form - `action(key, value, format, meta)`, as described in more - detail under `walk`. - - This function calls `applyJSONFilters`, with the `format` - argument provided by the first command-line argument, - if present. (Pandoc sets this by default when calling - filters.) - """ - try: - input_stream = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8') - except AttributeError: - # Python 2 does not have sys.stdin.buffer. - # REF: https://stackoverflow.com/questions/2467928/python-unicodeencode - input_stream = codecs.getreader("utf-8")(sys.stdin) - - source = input_stream.read() - if len(sys.argv) > 1: - format = sys.argv[1] - else: - format = "" - - sys.stdout.write(applyJSONFilters(actions, source, format)) - -def applyJSONFilters(actions, source, format=""): - """Walk through JSON structure and apply filters - - This: - - * reads a JSON-formatted pandoc document from a source string - * transforms it by walking the tree and performing the actions - * returns a new JSON-formatted pandoc document as a string - - The `actions` argument is a list of functions (see `walk` - for a full description). - - The argument `source` is a string encoded JSON object. - - The argument `format` is a string describing the output format. - - Returns a the new JSON-formatted pandoc document. - """ - - doc = json.loads(source) - - if 'meta' in doc: - meta = doc['meta'] - elif doc[0]: # old API - meta = doc[0]['unMeta'] - else: - meta = {} - altered = doc - for action in actions: - altered = walk(altered, action, format, meta) - - return json.dumps(altered) - - -def stringify(x): - """Walks the tree x and returns concatenated string content, - leaving out all formatting. - """ - result = [] - - def go(key, val, format, meta): - if key in ['Str', 'MetaString']: - result.append(val) - elif key == 'Code': - result.append(val[1]) - elif key == 'Math': - result.append(val[1]) - elif key == 'LineBreak': - result.append(" ") - elif key == 'SoftBreak': - result.append(" ") - elif key == 'Space': - result.append(" ") - - walk(x, go, "", {}) - return ''.join(result) - - -def attributes(attrs): - """Returns an attribute list, constructed from the - dictionary attrs. - """ - attrs = attrs or {} - ident = attrs.get("id", "") - classes = attrs.get("classes", []) - keyvals = [[x, attrs[x]] for x in attrs if (x != "classes" and x != "id")] - return [ident, classes, keyvals] - - -def elt(eltType, numargs): - def fun(*args): - lenargs = len(args) - if lenargs != numargs: - raise ValueError(eltType + ' expects ' + str(numargs) + - ' arguments, but given ' + str(lenargs)) - if numargs == 0: - xs = [] - elif len(args) == 1: - xs = args[0] - else: - xs = list(args) - return {'t': eltType, 'c': xs} - return fun - -# Constructors for block elements - -Plain = elt('Plain', 1) -Para = elt('Para', 1) -CodeBlock = elt('CodeBlock', 2) -RawBlock = elt('RawBlock', 2) -BlockQuote = elt('BlockQuote', 1) -OrderedList = elt('OrderedList', 2) -BulletList = elt('BulletList', 1) -DefinitionList = elt('DefinitionList', 1) -Header = elt('Header', 3) -HorizontalRule = elt('HorizontalRule', 0) -Table = elt('Table', 5) -Div = elt('Div', 2) -Null = elt('Null', 0) - -# Constructors for inline elements - -Str = elt('Str', 1) -Emph = elt('Emph', 1) -Strong = elt('Strong', 1) -Strikeout = elt('Strikeout', 1) -Superscript = elt('Superscript', 1) -Subscript = elt('Subscript', 1) -SmallCaps = elt('SmallCaps', 1) -Quoted = elt('Quoted', 2) -Cite = elt('Cite', 2) -Code = elt('Code', 2) -Space = elt('Space', 0) -LineBreak = elt('LineBreak', 0) -Math = elt('Math', 2) -RawInline = elt('RawInline', 2) -Link = elt('Link', 3) -Image = elt('Image', 3) -Note = elt('Note', 1) -SoftBreak = elt('SoftBreak', 0) -Span = elt('Span', 2)