#!/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 .", "This manual page has been composed from Miller's help output by Eric MSP Veith ." ]) 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