#!/usr/pkg/bin/ruby31
#/ Usage: ronn <options> <file>...
#/        ronn -m|--man <file>
#/        ronn -S|--server <file> ...
#/        ronn --pipe [<file>...]
#/ Convert ronn source <file>s to roff or HTML manpage. In the first synopsis form,
#/ build HTML and roff output files based on the input file names.
#/
#/ Mode options alter the default behavior of generating files:
#/       --pipe                write to standard output instead of generating files
#/   -m, --man                 show manual like with man(1)
#/   -S, --server              serve <file>s at http://localhost:1207/
#/
#/ Format options control which files / formats are generated:
#/   -r, --roff                generate roff output
#/   -5, --html                generate entire HTML page with layout
#/   -f, --fragment            generate HTML fragment
#/       --markdown            generate post-processed markdown output
#/
#/ Document attributes:
#/       --date=<date>          published date in YYYY-MM-DD format (bottom-center)
#/       --manual=<name>        name of the manual (top-center)
#/       --organization=<name>  publishing group or individual (bottom-left)
#/
#/ Misc options:
#/   -w, --warnings            show troff warnings on stderr
#/   -W                        disable previously enabled troff warnings
#/       --version             show ronn version and exit
#/       --help                show this help message
#/
#/ A <file> named example.1.ronn generates example.1.html (HTML manpage)
#/ and example.1 (roff manpage) by default.
require 'date'
require 'optparse'

def usage
  puts File.readlines(__FILE__).
    grep(/^#\/.*/).
    map { |line| line.chomp[3..-1] }.
    join("\n")
end

##
# Libraries and LOAD_PATH shenanigans

begin
  require 'rdiscount'
  require 'hpricot'
  require 'ronn'
rescue LoadError => boom
  if boom.to_s =~ /ronn/
    libdir = File.expand_path("../../lib", __FILE__).sub(%r|^#{Dir.pwd}/|, './')
    if File.directory?(libdir) && !$:.include?(libdir)
      warn "warn: #{boom}. adding #{libdir} to RUBYLIB ..."
      $:.unshift libdir
      retry
    end
  elsif !defined?(Gem)
    warn "warn: #{boom}. loading rubygems ..."
    require 'rubygems'
    retry
  end
  abort boom.to_s
end

##
# Argument defaults

build   = true
view    = false
server  = false
formats = nil
options = {}
write_index = false
styles  = %w[man]
groff   = "groff -Wall -mtty-char -mandoc -Tascii"
pager   = ENV['MANPAGER'] || ENV['PAGER'] || 'more'

##
# Environment variables

%w[manual organization date].each do |attribute|
  value = ENV["RONN_#{attribute.upcase}"]
  next if value.nil? or value.empty?
  options[attribute] = value
end

##
# Argument parsing

ARGV.options do |argv|
  # modes
  argv.on("--pipe")           { build = server = false }
  argv.on("-b", "--build")    { build = true; server = false }
  argv.on("-m", "--man")      { build = server = false; view = true }
  argv.on("-S", "--server")   { build = view = false; server = true }
  argv.on("-i", "--index")    { write_index = true }

  # format options
  argv.on("-r", "--roff")     { (formats ||= []) << 'roff' }
  argv.on("-5", "--html")     { (formats ||= []) << 'html' }
  argv.on("-f", "--fragment") { (formats ||= []) << 'html_fragment' }
  argv.on("--markdown")       { (formats ||= []) << 'markdown' }

  # html output options
  argv.on("-s", "--style=V")  { |val| styles += val.split(/[, \n]+/) }

  # manual attribute options
  %w[name section manual organization date].each do |attribute|
    argv.on("--#{attribute}=VALUE") { |val| options[attribute] = val }
  end

  # misc
  argv.on("-w", "--warnings") { groff += ' -ww' }
  argv.on("-W")               { groff += ' -Ww' }
  argv.on("-v", "--version")  do
    require 'ronn'
    if Ronn.release?
      printf "Ronn v%s\n", Ronn::VERSION
    else
      printf "Ronn v%s (%s)\n", Ronn::VERSION, Ronn::REV
    end
    printf "http://github.com/rtomayko/ronn/tree/%s\n", Ronn.revision
    exit 0
  end
  argv.on_tail("--help") { usage ; exit 0 }
  argv.parse!
end

##
# Modes, Formats, Options

case
when ARGV.empty? && $stdin.tty?
  usage
  exit 2
when ARGV.empty? && !server
  ARGV.push '-'
  build = false
  formats ||= %w[roff]
when view
  formats ||= %w[roff]
when build
  formats ||= %w[roff html]
end
formats ||= []
formats.delete('html') if formats.include?('html_fragment')

options['date'] &&= Date.strptime(options['date'], '%Y-%m-%d')
options['styles'] = styles

##
# Server

if server
  require 'ronn/server'
  Ronn::Server.run(ARGV, options)
  exit 0
end

##
# Build Pipeline

pid = nil
wr = STDOUT
documents = ARGV.map { |file| Ronn::Document.new(file, options) }
documents.each do |doc|
  # setup the man pipeline if the --man option was specified
  if view && !build
    rd, wr = IO.pipe
    if pid = fork
      rd.close
    else
      wr.close
      STDIN.reopen rd
      exec "#{groff} | #{pager}"
    end
  end

  # write output for each format
  formats.each do |format|
    if build
      path = doc.path_for(format)
      case format
      when 'html'
        warn "%9s: %-43s%15s" % [format, path, '+' + doc.styles.join(',')]
      when 'roff', 'html_fragment', 'markdown'
        warn "%9s: %-43s" % [format, path]
      end

      output = doc.convert(format)
      File.open(path, 'wb') { |f| f.puts(output) }

      if format == 'roff'
        if view
          system "man #{path}"
        else
          system "#{groff} <#{path} >/dev/null"
        end
      end
    else
      output = doc.convert(format)
      wr.puts(output)
    end
  end

  # wait for children to exit
  if pid
    wr.close
    Process.wait
  end
end

# Write index.txt files

if write_index
  indexes = documents.map { |doc| doc.index }.uniq
  indexes.each do |index|
    File.open(index.path, 'wb') do |fd|
      fd.puts(index.to_text)
    end
  end
end
