diff options
Diffstat (limited to 'jni/ruby/tool/mdoc2man.rb')
-rwxr-xr-x | jni/ruby/tool/mdoc2man.rb | 465 |
1 files changed, 465 insertions, 0 deletions
diff --git a/jni/ruby/tool/mdoc2man.rb b/jni/ruby/tool/mdoc2man.rb new file mode 100755 index 0000000..49058af --- /dev/null +++ b/jni/ruby/tool/mdoc2man.rb @@ -0,0 +1,465 @@ +#!/usr/bin/env ruby +### +### mdoc2man - mdoc to man converter +### +### Quick usage: mdoc2man.rb < mdoc_manpage.8 > man_manpage.8 +### +### Ported from Perl by Akinori MUSHA. +### +### Copyright (c) 2001 University of Illinois Board of Trustees +### Copyright (c) 2001 Mark D. Roth +### Copyright (c) 2002, 2003 Akinori MUSHA +### All rights reserved. +### +### Redistribution and use in source and binary forms, with or without +### modification, are permitted provided that the following conditions +### are met: +### 1. Redistributions of source code must retain the above copyright +### notice, this list of conditions and the following disclaimer. +### 2. Redistributions in binary form must reproduce the above copyright +### notice, this list of conditions and the following disclaimer in the +### documentation and/or other materials provided with the distribution. +### 3. All advertising materials mentioning features or use of this software +### must display the following acknowledgement: +### This product includes software developed by the University of +### Illinois at Urbana, and their contributors. +### 4. The University nor the names of their +### contributors may be used to endorse or promote products derived from +### this software without specific prior written permission. +### +### THIS SOFTWARE IS PROVIDED BY THE TRUSTEES AND CONTRIBUTORS ``AS IS'' AND +### ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +### IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +### ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR CONTRIBUTORS BE LIABLE +### FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +### DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +### OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +### HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +### LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +### OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +### SUCH DAMAGE. +### +### $Id: mdoc2man.rb 31573 2011-05-15 11:55:52Z nobu $ +### + +class Mdoc2Man + ANGLE = 1 + OPTION = 2 + PAREN = 3 + + RE_PUNCT = /^[!"'),\.\/:;>\?\]`]$/ + + def initialize + @name = @date = @id = nil + @refauthors = @reftitle = @refissue = @refdate = @refopt = nil + + @optlist = 0 ### 1 = bullet, 2 = enum, 3 = tag, 4 = item + @oldoptlist = 0 + @nospace = 0 ### 0, 1, 2 + @enum = 0 + @synopsis = true + @reference = false + @ext = false + @extopt = false + @literal = false + end + + def mdoc2man(i, o) + i.each { |line| + if /^\./ !~ line + o.print line + o.print ".br\n" if @literal + next + end + + line.slice!(0, 1) + + next if /\\"/ =~ line + + line = parse_macro(line) and o.print line + } + + initialize + end + + def parse_macro(line) + words = line.split + retval = '' + + quote = [] + dl = false + + while word = words.shift + case word + when RE_PUNCT + while q = quote.pop + case q + when OPTION + retval << ']' + when PAREN + retval << ')' + when ANGLE + retval << '>' + end + end + retval << word + next + when 'Li', 'Pf' + @nospace = 1 + next + when 'Xo' + @ext = true + retval << ' ' unless retval.empty? || /[\n ]\z/ =~ retval + next + when 'Xc' + @ext = false + retval << "\n" unless @extopt + break + when 'Bd' + @literal = true if words[0] == '-literal' + retval << "\n" + break + when 'Ed' + @literal = false + break + when 'Ns' + @nospace = 1 if @nospace == 0 + retval.chomp!(' ') + next + when 'No' + retval.chomp!(' ') + retval << words.shift + next + when 'Dq' + retval << '``' + begin + retval << words.shift << ' ' + end until words.empty? || RE_PUNCT =~ words[0] + retval.chomp!(' ') + retval << '\'\'' + @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0] + next + when 'Sq', 'Ql' + retval << '`' << words.shift << '\'' + @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0] + next + # when 'Ic' + # retval << '\\fB' << words.shift << '\\fP' + # next + when 'Oo' + #retval << "[\\c\n" + @extopt = true + @nospace = 1 if @nospace == 0 + retval << '[' + next + when 'Oc' + @extopt = false + retval << ']' + next + when 'Ao' + @nospace = 1 if @nospace == 0 + retval << '<' + next + when 'Ac' + retval << '>' + next + end + + retval << ' ' if @nospace == 0 && !(retval.empty? || /[\n ]\z/ =~ retval) + @nospace = 0 if @nospace == 1 + + case word + when 'Dd' + @date = words.join(' ') + return nil + when 'Dt' + if words.size >= 2 && words[1] == '""' && + /^(.*)\(([0-9])\)$/ =~ words[0] + words[0] = $1 + words[1] = $2 + end + @id = words.join(' ') + return nil + when 'Os' + retval << '.TH ' << @id << ' "' << @date << '" "' << + words.join(' ') << '"' + break + when 'Sh' + retval << '.SH' + @synopsis = (words[0] == 'SYNOPSIS') + next + when 'Xr' + retval << '\\fB' << words.shift << + '\\fP(' << words.shift << ')' << (words.shift||'') + break + when 'Rs' + @refauthors = [] + @reftitle = '' + @refissue = '' + @refdate = '' + @refopt = '' + @reference = true + break + when 'Re' + retval << "\n" + + # authors + while @refauthors.size > 1 + retval << @refauthors.shift << ', ' + end + retval << 'and ' unless retval.empty? + retval << @refauthors.shift + + # title + retval << ', \\fI' << @reftitle << '\\fP' + + # issue + retval << ', ' << @refissue unless @refissue.empty? + + # date + retval << ', ' << @refdate unless @refdate.empty? + + # optional info + retval << ', ' << @refopt unless @refopt.empty? + + retval << ".\n" + + @reference = false + break + when 'An' + next + when 'Dl' + retval << ".nf\n" << '\\& ' + dl = true + next + when 'Ux' + retval << "UNIX" + next + end + + if @reference + case word + when '%A' + @refauthors.unshift(words.join(' ')) + break + when '%T' + @reftitle = words.join(' ') + @reftitle.sub!(/^"/, '') + @reftitle.sub!(/"$/, '') + break + when '%N' + @refissue = words.join(' ') + break + when '%D' + @refdate = words.join(' ') + break + when '%O' + @refopt = words.join(' ') + break + end + end + + case word + when 'Nm' + name = words.empty? ? @name : words.shift + @name ||= name + retval << ".br\n" if @synopsis + retval << "\\fB" << name << "\\fP" + @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0] + next + when 'Nd' + retval << '\\-' + next + when 'Fl' + retval << '\\fB\\-' << words.shift << '\\fP' + @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0] + next + when 'Ar' + retval << '\\fI' + if words.empty? + retval << 'file ...\\fP' + else + retval << words.shift << '\\fP' + while words[0] == '|' + retval << ' ' << words.shift << ' \\fI' << words.shift << '\\fP' + end + @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0] + next + end + when 'Cm' + retval << '\\fB' << words.shift << '\\fP' + while RE_PUNCT =~ words[0] + retval << words.shift + end + next + when 'Op' + quote << OPTION + @nospace = 1 if @nospace == 0 + retval << '[' + # words.push(words.pop + ']') + next + when 'Aq' + quote << ANGLE + @nospace = 1 if @nospace == 0 + retval << '<' + # words.push(words.pop + '>') + next + when 'Pp' + retval << "\n" + next + when 'Ss' + retval << '.SS' + next + end + + if word == 'Pa' && !quote.include?(OPTION) + retval << '\\fI' + retval << '\\&' if /^\./ =~ words[0] + retval << words.shift << '\\fP' + while RE_PUNCT =~ words[0] + retval << words.shift + end + # @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0] + next + end + + case word + when 'Dv' + retval << '.BR' + next + when 'Em', 'Ev' + retval << '.IR' + next + when 'Pq' + retval << '(' + @nospace = 1 + quote << PAREN + next + when 'Sx', 'Sy' + retval << '.B ' << words.join(' ') + break + when 'Ic' + retval << '\\fB' + until words.empty? || RE_PUNCT =~ words[0] + case words[0] + when 'Op' + words.shift + retval << '[' + words.push(words.pop + ']') + next + when 'Aq' + words.shift + retval << '<' + words.push(words.pop + '>') + next + when 'Ar' + words.shift + retval << '\\fI' << words.shift << '\\fP' + else + retval << words.shift + end + + retval << ' ' if @nospace == 0 + end + + retval.chomp!(' ') + retval << '\\fP' + retval << words.shift unless words.empty? + break + when 'Bl' + @oldoptlist = @optlist + + case words[0] + when '-bullet' + @optlist = 1 + when '-enum' + @optlist = 2 + @enum = 0 + when '-tag' + @optlist = 3 + when '-item' + @optlist = 4 + end + + break + when 'El' + @optlist = @oldoptlist + next + end + + if @optlist != 0 && word == 'It' + case @optlist + when 1 + # bullets + retval << '.IP \\(bu' + when 2 + # enum + @enum += 1 + retval << '.IP ' << @enum << '.' + when 3 + # tags + retval << ".TP\n" + case words[0] + when 'Pa', 'Ev' + words.shift + retval << '.B' + end + when 4 + # item + retval << ".IP\n" + end + + next + end + + case word + when 'Sm' + case words[0] + when 'off' + @nospace = 2 + when 'on' + # retval << "\n" + @nospace = 0 + end + words.shift + next + end + + retval << word + end + + return nil if retval == '.' + + retval.sub!(/\A\.([^a-zA-Z])/, "\\1") + # retval.chomp!(' ') + + while q = quote.pop + case q + when OPTION + retval << ']' + when PAREN + retval << ')' + when ANGLE + retval << '>' + end + end + + # retval << ' ' unless @nospace == 0 || retval.empty? || /\n\z/ =~ retval + + retval << ' ' unless !@ext || @extopt || / $/ =~ retval + + retval << "\n" unless @ext || @extopt || retval.empty? || /\n\z/ =~ retval + + retval << ".fi\n" if dl + + return retval + end + + def self.mdoc2man(i, o) + new.mdoc2man(i, o) + end +end + +if $0 == __FILE__ + Mdoc2Man.mdoc2man(ARGF, STDOUT) +end |