(linenum→info "unix/slp.c:2238")

ruby/1.9.0/mdoc2man.rb

    1: #!/usr/bin/env ruby
    2: ###
    3: ### mdoc2man - mdoc to man converter
    4: ###
    5: ### Quick usage:  mdoc2man.rb < mdoc_manpage.8 > man_manpage.8
    6: ###
    7: ### Ported from Perl by Akinori MUSHA.
    8: ###
    9: ###  Copyright (c) 2001 University of Illinois Board of Trustees
   10: ###  Copyright (c) 2001 Mark D. Roth
   11: ###  Copyright (c) 2002, 2003 Akinori MUSHA
   12: ###  All rights reserved.
   13: ### 
   14: ###  Redistribution and use in source and binary forms, with or without
   15: ###  modification, are permitted provided that the following conditions
   16: ###  are met:
   17: ###  1. Redistributions of source code must retain the above copyright
   18: ###     notice, this list of conditions and the following disclaimer.
   19: ###  2. Redistributions in binary form must reproduce the above copyright
   20: ###     notice, this list of conditions and the following disclaimer in the
   21: ###     documentation and/or other materials provided with the distribution.
   22: ###  3. All advertising materials mentioning features or use of this software
   23: ###     must display the following acknowledgement:
   24: ###     This product includes software developed by the University of
   25: ###     Illinois at Urbana, and their contributors.
   26: ###  4. The University nor the names of their
   27: ###     contributors may be used to endorse or promote products derived from
   28: ###     this software without specific prior written permission.
   29: ### 
   30: ###  THIS SOFTWARE IS PROVIDED BY THE TRUSTEES AND CONTRIBUTORS ``AS IS'' AND
   31: ###  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   32: ###  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   33: ###  ARE DISCLAIMED.  IN NO EVENT SHALL THE TRUSTEES OR CONTRIBUTORS BE LIABLE
   34: ###  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   35: ###  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   36: ###  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   37: ###  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   38: ###  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   39: ###  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   40: ###  SUCH DAMAGE.
   41: ###
   42: ### $Id: mdoc2man.rb 11708 2007-02-12 23:01:19Z shyouhei $
   43: ###
   44: 
   45: class Mdoc2Man
   46:   ANGLE = 1
   47:   OPTION = 2
   48:   PAREN = 3
   49: 
   50:   RE_PUNCT = /^[!"'),\.\/:;>\?\]`]$/
   51: 
   52:   def initialize
   53:     @name = @date = @id = nil
   54:     @refauthors = @reftitle = @refissue = @refdate = @refopt = nil
   55: 
   56:     @optlist = 0                ### 1 = bullet, 2 = enum, 3 = tag, 4 = item
   57:     @oldoptlist = 0
   58:     @nospace = 0                ### 0, 1, 2
   59:     @enum = 0
   60:     @synopsis = true
   61:     @reference = false
   62:     @ext = false
   63:     @extopt = false
   64:     @literal = false
   65:   end
   66: 
   67:   def mdoc2man(i, o)
   68:     i.each { |line|
   69:       if /^\./ !~ line
   70:         o.print line
   71:         o.print ".br\n" if @literal
   72:         next
   73:       end
   74: 
   75:       line.slice!(0, 1)
   76: 
   77:       next if /\\"/ =~ line
   78: 
   79:       line = parse_macro(line) and o.print line
   80:     }
   81: 
   82:     initialize
   83:   end
   84: 
   85:   def parse_macro(line)
   86:     words = line.split
   87:     retval = ''
   88: 
   89:     quote = []
   90:     dl = false
   91: 
   92:     while word = words.shift
   93:       case word
   94:       when RE_PUNCT
   95:         while q = quote.pop
   96:           case q
   97:           when OPTION
   98:             retval << ']'
   99:           when PAREN
  100:             retval << ')'
  101:           when ANGLE
  102:             retval << '>'
  103:           end
  104:         end
  105:         retval << word
  106:         next
  107:       when 'Li', 'Pf'
  108:         @nospace = 1
  109:         next
  110:       when 'Xo'
  111:         @ext = true
  112:         retval << ' ' unless retval.empty? || /[\n ]\z/ =~ retval
  113:         next
  114:       when 'Xc'
  115:         @ext = false
  116:         retval << "\n" unless @extopt
  117:         break
  118:       when 'Bd'
  119:         @literal = true if words[0] == '-literal'
  120:         retval << "\n"
  121:         break
  122:       when 'Ed'
  123:         @literal = false
  124:         break
  125:       when 'Ns'
  126:         @nospace = 1 if @nospace == 0
  127:         retval.chomp!(' ')
  128:         next
  129:       when 'No'
  130:         retval.chomp!(' ')
  131:         retval << words.shift
  132:         next
  133:       when 'Dq'
  134:         retval << '``'
  135:         begin
  136:           retval << words.shift << ' '
  137:         end until words.empty? || RE_PUNCT =~ words[0]
  138:         retval.chomp!(' ')
  139:         retval << '\'\''
  140:         @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
  141:         next
  142:       when 'Sq', 'Ql'
  143:         retval << '`' << words.shift << '\''
  144:         @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
  145:         next
  146:         # when  'Ic'
  147:         #   retval << '\\fB' << words.shift << '\\fP'
  148:         #   next
  149:       when 'Oo'
  150:         #retval << "[\\c\n"
  151:         @extopt = true
  152:         @nospace = 1 if @nospace == 0
  153:         retval << '['
  154:         next
  155:       when 'Oc'
  156:         @extopt = false
  157:         retval << ']'
  158:         next
  159:       when 'Ao'
  160:         @nospace = 1 if @nospace == 0
  161:         retval << '<'
  162:         next
  163:       when 'Ac'
  164:         retval << '>'
  165:         next
  166:       end
  167: 
  168:       retval << ' ' if @nospace == 0 && !(retval.empty? || /[\n ]\z/ =~ retval)
  169:       @nospace = 0 if @nospace == 1
  170: 
  171:       case word
  172:       when 'Dd'
  173:         @date = words.join(' ')
  174:         return nil
  175:       when 'Dt'
  176:         if words.size >= 2 && words[1] == '""' &&
  177:             /^(.*)\(([0-9])\)$/ =~ words[0]
  178:           words[0] = $1
  179:           words[1] = $2
  180:         end
  181:         @id = words.join(' ')
  182:         return nil
  183:       when 'Os'
  184:         retval << '.TH ' << @id << ' "' << @date << '" "' <<
  185:           words.join(' ') << '"'
  186:         break
  187:       when 'Sh'
  188:         retval << '.SH'
  189:         @synopsis = (words[0] == 'SYNOPSIS')
  190:         next
  191:       when 'Xr'
  192:         retval << '\\fB' << words.shift <<
  193:           '\\fP(' << words.shift << ')' << words.shift
  194:         break
  195:       when 'Rs'
  196:         @refauthors = []
  197:         @reftitle = ''
  198:         @refissue = ''
  199:         @refdate = ''
  200:         @refopt = ''
  201:         @reference = true
  202:         break
  203:       when 'Re'
  204:         retval << "\n"
  205: 
  206:         # authors
  207:         while @refauthors.size > 1
  208:           retval << @refauthors.shift << ', '
  209:         end
  210:         retval << 'and ' unless retval.empty?
  211:         retval << @refauthors.shift
  212: 
  213:         # title 
  214:         retval << ', \\fI' << @reftitle << '\\fP'
  215: 
  216:         # issue
  217:         retval << ', ' << @refissue unless @refissue.empty?
  218: 
  219:         # date
  220:         retval << ', ' << @refdate unless @refdate.empty?
  221: 
  222:         # optional info
  223:         retval << ', ' << @refopt unless @refopt.empty?
  224: 
  225:         retval << ".\n"
  226: 
  227:         @reference = false
  228:         break
  229:       when 'An'
  230:         next
  231:       when 'Dl'
  232:         retval << ".nf\n" << '\\&  '
  233:         dl = true
  234:         next
  235:       when 'Ux'
  236:         retval << "UNIX"
  237:         next
  238:       end
  239: 
  240:       if @reference
  241:         case word
  242:         when '%A'
  243:           @refauthors.unshift(words.join(' '))
  244:           break
  245:         when '%T'
  246:           @reftitle = words.join(' ')
  247:           @reftitle.sub!(/^"/, '')
  248:           @reftitle.sub!(/"$/, '')
  249:           break
  250:         when '%N'
  251:           @refissue = words.join(' ')
  252:           break
  253:         when '%D'
  254:           @refdate = words.join(' ')
  255:           break
  256:         when '%O'
  257:           @refopt = words.join(' ')
  258:           break
  259:         end
  260:       end
  261: 
  262:       case word
  263:       when 'Nm'
  264:         name = words.empty? ? @name : words.shift
  265:         @name ||= name
  266:         retval << ".br\n" if @synopsis
  267:         retval << "\\fB" << name << "\\fP"
  268:         @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
  269:         next
  270:       when 'Nd'
  271:         retval << '\\-'
  272:         next
  273:       when 'Fl'
  274:         retval << '\\fB\\-' << words.shift << '\\fP'
  275:         @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
  276:         next
  277:       when 'Ar'
  278:         retval << '\\fI'
  279:         if words.empty?
  280:           retval << 'file ...\\fP'
  281:         else
  282:           retval << words.shift << '\\fP'
  283:           while words[0] == '|'
  284:             retval << ' ' << words.shift << ' \\fI' << words.shift << '\\fP'
  285:           end
  286:           @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
  287:           next
  288:         end
  289:       when 'Cm'
  290:         retval << '\\fB' << words.shift << '\\fP'
  291:         while RE_PUNCT =~ words[0]
  292:           retval << words.shift
  293:         end
  294:         next
  295:       when 'Op'
  296:         quote << OPTION
  297:         @nospace = 1 if @nospace == 0
  298:         retval << '['
  299:         # words.push(words.pop + ']')
  300:         next
  301:       when 'Aq'
  302:         quote << ANGLE
  303:         @nospace = 1 if @nospace == 0
  304:         retval << '<'
  305:         # words.push(words.pop + '>')
  306:         next
  307:       when 'Pp'
  308:         retval << "\n"
  309:         next
  310:       when 'Ss'
  311:         retval << '.SS'
  312:         next
  313:       end
  314: 
  315:       if word == 'Pa' && !quote.include?(OPTION)
  316:         retval << '\\fI'
  317:         retval << '\\&' if /^\./ =~ words[0]
  318:         retval << words.shift << '\\fP'
  319:         while RE_PUNCT =~ words[0]
  320:           retval << words.shift
  321:         end
  322:         # @nospace = 1 if @nospace == 0 && RE_PUNCT =~ words[0]
  323:         next
  324:       end
  325: 
  326:       case word
  327:       when 'Dv'
  328:         retval << '.BR'
  329:         next
  330:       when 'Em', 'Ev'
  331:         retval << '.IR'
  332:         next
  333:       when 'Pq'
  334:         retval << '('
  335:         @nospace = 1
  336:         quote << PAREN
  337:         next
  338:       when 'Sx', 'Sy'
  339:         retval << '.B ' << words.join(' ')
  340:         break
  341:       when 'Ic'
  342:         retval << '\\fB'
  343:         until words.empty? || RE_PUNCT =~ words[0]
  344:           case words[0]
  345:           when 'Op'
  346:             words.shift
  347:             retval << '['
  348:             words.push(words.pop + ']')
  349:             next
  350:           when 'Aq'
  351:             words.shift
  352:             retval << '<'
  353:             words.push(words.pop + '>')
  354:             next
  355:           when 'Ar'
  356:             words.shift
  357:             retval << '\\fI' << words.shift << '\\fP'
  358:           else
  359:             retval << words.shift
  360:           end
  361: 
  362:           retval << ' ' if @nospace == 0
  363:         end
  364: 
  365:         retval.chomp!(' ')
  366:         retval << '\\fP'
  367:         retval << words.shift unless words.empty?
  368:         break
  369:       when 'Bl'
  370:         @oldoptlist = @optlist
  371: 
  372:         case words[0]
  373:         when '-bullet'
  374:           @optlist = 1
  375:         when '-enum'
  376:           @optlist = 2
  377:           @enum = 0
  378:         when '-tag'
  379:           @optlist = 3
  380:         when '-item'
  381:           @optlist = 4
  382:         end
  383: 
  384:         break
  385:       when 'El'
  386:         @optlist = @oldoptlist
  387:         next
  388:       end
  389: 
  390:       if @optlist != 0 && word == 'It'
  391:         case @optlist
  392:         when 1
  393:           # bullets
  394:           retval << '.IP \\(bu'
  395:         when 2
  396:           # enum
  397:           @enum += 1
  398:           retval << '.IP ' << @enum << '.'
  399:         when 3
  400:           # tags
  401:           retval << ".TP\n"
  402:           case words[0]
  403:           when 'Pa', 'Ev'
  404:             words.shift
  405:             retval << '.B'
  406:           end
  407:         when 4
  408:           # item
  409:           retval << ".IP\n"
  410:         end
  411: 
  412:         next
  413:       end
  414: 
  415:       case word
  416:       when 'Sm'
  417:         case words[0]
  418:         when 'off'
  419:           @nospace = 2
  420:         when 'on'
  421:           # retval << "\n"
  422:           @nospace = 0
  423:         end
  424:         words.shift
  425:         next
  426:       end
  427: 
  428:       retval << word
  429:     end
  430: 
  431:     return nil if retval == '.'
  432: 
  433:     retval.sub!(/\A\.([^a-zA-Z])/, "\\1")
  434:     # retval.chomp!(' ')
  435: 
  436:     while q = quote.pop
  437:       case q
  438:       when OPTION
  439:         retval << ']'
  440:       when PAREN
  441:         retval << ')'
  442:       when ANGLE
  443:         retval << '>'
  444:       end
  445:     end
  446: 
  447:     # retval << ' ' unless @nospace == 0 || retval.empty? || /\n\z/ =~ retval
  448: 
  449:     retval << ' ' unless !@ext || @extopt || / $/ =~ retval
  450: 
  451:     retval << "\n" unless @ext || @extopt || retval.empty? || /\n\z/ =~ retval
  452: 
  453:     retval << ".fi\n" if dl
  454: 
  455:     return retval
  456:   end
  457: 
  458:   def self.mdoc2man(i, o)
  459:     new.mdoc2man(i, o)
  460:   end
  461: end
  462: 
  463: if $0 == __FILE__
  464:   Mdoc2Man.mdoc2man(ARGF, STDOUT)
  465: end
Syntax (Markdown)