mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@31573 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			465 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			Ruby
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			465 lines
		
	
	
	
		
			9.4 KiB
		
	
	
	
		
			Ruby
		
	
	
		
			Executable file
		
	
	
	
	
#!/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
 |