miller/man/mkman.rb
John Kerl 794a754c36
Support PPRINT barred input (#1472)
* Support PPRINT barred input

* regression-test files

* output from `make dev`

* doc updates
2024-01-20 12:59:12 -05:00

220 lines
6.8 KiB
Ruby
Executable file

#!/usr/bin/env ruby
# ================================================================
# This is a manpage autogenerator for Miller. There are various tools out there
# for creating xroff-formatted manpages, but I wanted something with minimal
# external dependencies which would also automatically generate most of its
# output from the mlr executable itself. It turns out it's easy enough to get
# this in just a few lines of Ruby.
#
# Note for dev-viewing of the output:
# ./mkman.rb | groff -man -Tascii | less
# ================================================================
# ----------------------------------------------------------------
def main
# In case the user running this has a .mlrrc
ENV['MLRRC'] = '__none__'
# Live code-generation needs to be using mlr from *this* tree, not from
# somewhere else in the PATH.
unless File.executable?('../mlr')
$stderr.puts "#{$0}: Need ../mlr to exist: please check 'make build' in ../.."
exit 1
end
`../mlr --version`
unless $?.success?
$stderr.puts "#{$0}: Need '../../mlr --version' to succeed; please check 'make build' in ../.."
exit 1
end
ENV['PATH'] = '..:' + ENV['PATH']
print make_top
print make_section('NAME', [
"Miller \\\-\\\- like awk, sed, cut, join, and sort for name-indexed data such as CSV and tabular JSON."
])
print make_section('SYNOPSIS', [
`mlr --help`
])
print make_section('DESCRIPTION', [
"""Miller operates on key-value-pair data while the familiar Unix tools operate
on integer-indexed fields: if the natural data structure for the latter is the
array, then Miller's natural data structure is the insertion-ordered hash map.
This encompasses a variety of data formats, including but not limited to the
familiar CSV, TSV, and JSON. (Miller can handle positionally-indexed data as
a special case.) This manpage documents #{`mlr --version`.chomp}."""
])
print make_section('EXAMPLES', [
""
])
print make_code_block(`mlr help basic-examples`)
print make_section('FILE FORMATS', [])
print make_code_block(`mlr help file-formats`)
print make_section('HELP OPTIONS', [])
print make_code_block(`mlr help topics`)
print make_section('VERB LIST', [])
print make_code_block(`mlr help list-verbs-as-paragraph`)
print make_section('FUNCTION LIST', [])
print make_code_block(`mlr help list-functions-as-paragraph`)
section_names = `mlr help list-flag-sections`.split("\n")
for section_name in section_names
print make_section(section_name.upcase, [""])
print make_code_block(`mlr help show-help-for-section '#{section_name}'`)
end
print make_section('AUXILIARY COMMANDS', [])
print make_code_block(`mlr aux-list`)
print make_section('MLRRC', [])
print make_code_block(`mlr help mlrrc`)
print make_section('REPL', [])
print make_code_block(`mlr repl -h`)
verbs = `mlr help list-verbs`
print make_section('VERBS', [
""
])
verbs = verbs.strip.split("\n")
for verb in verbs
print make_subsection(verb, [])
print make_code_block(`mlr #{verb} -h`)
end
functions = `mlr help list-functions`
print make_section('FUNCTIONS FOR FILTER/PUT', [
""
])
functions = functions.strip.split("\n").uniq
for function in functions
print make_subsection(function, [])
text = `mlr help function '#{function}'`
text = text.sub(function + ' ', '')
print make_code_block(text)
end
keywords = `mlr help list-keywords`
print make_section('KEYWORDS FOR PUT AND FILTER', [
""
])
keywords = keywords.strip.split("\n").uniq
for keyword in keywords
print make_subsection(keyword, [])
text = `mlr help keyword '#{keyword}'`
print make_code_block(text)
end
print make_section('AUTHOR', [
"Miller is written by John Kerl <kerl.john.r@gmail.com>.",
"This manual page has been composed from Miller's help output by Eric MSP Veith <eveith@veith-m.de>."
])
print make_section('SEE ALSO', [
"awk(1), sed(1), cut(1), join(1), sort(1), RFC 4180: Common Format and MIME Type for " +
"Comma-Separated Values (CSV) Files, the Miller docsite https://miller.readthedocs.io"
])
end
# ================================================================
def make_top()
t = Time::new
stamp = t.gmtime.strftime("%Y-%m-%d")
# Portability definitions thanks to some asciidoc output
"""'\\\" t
.\\\" Title: mlr
.\\\" Author: [see the \"AUTHOR\" section]
.\\\" Generator: #{$0}
.\\\" Date: #{stamp}
.\\\" Manual: \\ \\&
.\\\" Source: \\ \\&
.\\\" Language: English
.\\\"
.TH \"MILLER\" \"1\" \"#{stamp}\" \"\\ \\&\" \"\\ \\&\"
.\\\" -----------------------------------------------------------------
.\\\" * Portability definitions
.\\\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\\\" http://bugs.debian.org/507673
.\\\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\\\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \\n(.g .ds Aq \(aq
.el .ds Aq '
.\\\" -----------------------------------------------------------------
.\\\" * set default formatting
.\\\" -----------------------------------------------------------------
.\\\" disable hyphenation
.nh
.\\\" disable justification (adjust text to left margin only)
.ad l
.\\\" -----------------------------------------------------------------
"""
end
# ----------------------------------------------------------------
def make_section(title, paragraphs)
retval = ".SH \"#{title}\"\n"
paragraphs.each do |paragraph|
retval += ".sp\n"
retval += groff_encode(paragraph) + "\n"
end
retval
end
# ----------------------------------------------------------------
def make_subsection(title, paragraphs)
retval = ".SS \"#{title}\"\n"
paragraphs.each do |paragraph|
retval += ".sp\n"
retval += groff_encode(paragraph) + "\n"
end
retval
end
# ----------------------------------------------------------------
def make_subsubsection(title, paragraphs)
retval = ".sp\n";
retval += "\\fB#{title}\\fR\n"
paragraphs.each do |paragraph|
retval += ".sp\n"
retval += groff_encode(paragraph) + "\n"
end
retval
end
# ----------------------------------------------------------------
def make_code_block(block)
retval = ".if n \\{\\\n"
retval += ".RS 0\n"
retval += ".\\}\n"
retval += ".nf\n"
# In case the line starts with a dot:
retval += block.gsub('\\', '\e').gsub(/^\./){'\&.'}
# In case the line starts with a single quote:
retval = retval.gsub(/^'/, '\(cq')
retval += ".fi\n"
retval += ".if n \\{\\\n"
retval += ".RE\n"
end
# ----------------------------------------------------------------
def groff_encode(line)
#line = line.gsub(/'/, '\(cq')
#line = line.gsub(/"/, '\(dq')
#line = line.gsub(/\./, '\&')
#line = line.gsub(/-/, '\-')
line = line.gsub(/\\([^-])/, '\e\1')
line = line.gsub(/^\./){'\&.'}
line
end
# ================================================================
main