Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 466 lines (422 sloc) 9.632 kb
#!/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$
###
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
Jump to Line
Something went wrong with that request. Please try again.