commit 91b56eca6122cb1198848b15cd9fc58e1abd3a55
Author: Paul van Tilburg
Date: Tue Mar 24 19:53:54 2009 +0100
Initial import of rubberin.
diff --git a/rubberin b/rubberin
new file mode 100755
index 0000000..6b0c77f
--- /dev/null
+++ b/rubberin
@@ -0,0 +1,177 @@
+#!/usr/bin/env ruby
+#
+# rubberin - a rubber wrapper using inotify
+#
+# Copyright (C) 2007 Paul van Tilburg
+#
+# This script can assist the authoring of LaTeX documents. It acts as an
+# drop-in replacement for rubber. While rubber would just perform a
+# compilation of the LaTeX file, rubberin will perform a compilation (using
+# rubber) each time it detects a modification in the LaTeX file or any of
+# its dependencies (include files, BibTeX files, image, etc.) until
+# rubberin is interrupted. Additionally, rubberin spawns a viewer to view
+# the result of the LaTeX compilation and forces it to reload every time
+# rubberin has recompiled the LaTeX file.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WAR RANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this p rogram; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+require 'getoptlong'
+require 'inotify'
+require 'pathname'
+
+# Small necessary Pathname class extension.
+class Pathname
+ def extname=(ext)
+ raise "Invalid extension" unless ext.is_a? String and ext.match(/^\./)
+ @path.sub!(/#{extname}$/, ext)
+ end
+end
+
+## Helper methods
+
+# Print the usage.
+def usage
+ puts "Usage: #{Program} [options]... [tex-file] [rubber-options]..."
+end
+
+# Print the command line help.
+def help
+ usage
+ puts "Wrapper for rubber that facilitates automatic compiling and viewer updating"
+ puts "when the input file or dependancy files are modified."
+ puts
+ puts "Available options:"
+ puts " -d, --pdf produce a PDF file (via ps2pdf if used with -p)"
+ puts " -h, --help display this help"
+ puts " -p, --ps produce a PostScript file"
+ puts "The options --pdf and --ps are passed to rubber like all options not listed above."
+end
+
+# Compile the input file using the right options based on the mode.
+def compile(infile, mode)
+ mode_opt = {:pdf => "--pdf",
+ :ps => "--ps",
+ :pspdf => "--ps --pdf"}[mode]
+ params = ARGV.join(' ')
+ err_file = infile.with_extname('err')
+
+ system "rubber --inplace #{mode_opt} #{params} #{infile} 2> #{err_file}"
+ File.open(err_file) { |file| puts file.read }
+end
+
+# Start the right viewer based on the mode.
+def view(infile, mode)
+ case mode
+ when :dvi
+ exec "xdvi.bin", "-watchfile", "1",
+ "-name", "xdvi", infile.with_extname('dvi')
+ when :ps
+ exec "evince", infile.with_extname('ps')
+ when :pdf, :pspdf
+ exec "evince", infile.with_extname('pdf')
+ end
+end
+
+# Reload the right viewer based on the mode.
+def reload(infile, mode)
+ case mode
+ when :ps
+ system "evince", infile.with_extname('ps')
+ when :pdf, :pspdf
+ system "evince", infile.with_extname('pdf')
+ end
+end
+
+## Initialisation
+Program = File.basename $0
+Version = '0.4a'
+
+# Parse the command line options.
+# Determine the compile mode from the options.
+mode = :dvi
+opts = GetoptLong.new(
+ [ "--help", "-h", GetoptLong::NO_ARGUMENT ],
+ [ "--ps", "-p", GetoptLong::NO_ARGUMENT ],
+ [ "--pdf", "-d", GetoptLong::NO_ARGUMENT ])
+opts.ordering = GetoptLong::REQUIRE_ORDER
+opts.quiet = true
+opts.each do |opt, arg|
+ case opt
+ when "--help"
+ help
+ exit 0
+ when "--ps"
+ mode = :ps
+ when "--pdf"
+ mode = if mode == :ps then :pspdf
+ else :pdf
+ end
+ end
+end
+
+# Get the input file from the arguments.
+if ARGV.length < 1
+ usage
+ exit 1
+else
+ infile = Pathname.new(ARGV.shift)
+ def infile.base
+ self.basename(self.extname)
+ end
+ def infile.with_extname(ext)
+ file = self.base
+ file.extname = ".#{ext}"
+ file
+ end
+end
+
+# Check the input file.
+begin
+ File.open(infile) {}
+rescue => err
+ puts "#{Program}: #{err}"
+ exit 2
+end
+
+# Find the dependancies of the input file using rubber-info and
+# do an initial run.
+files = `rubber-info --deps #{infile}`.chomp.split
+compile(infile, mode)
+
+# Spawn a viewer based on the mode.
+viewer_pid = fork
+view(infile, mode) if not viewer_pid
+
+# Handle signals.
+["INT", "TERM", "QUIT"].each do |sig|
+ Signal.trap(sig) { Process.kill(sig, viewer_pid); exit }
+end
+# If xdvi exits, this program should exit too.
+Signal.trap("CLD") { puts "#{Program}: viewer exited, so will I!"; exit }
+
+## Main event loop
+
+# Add input file with dependancies to the watch list and start event loop.
+i = Inotify.new
+files.each do |file|
+ i.add_watch(file, Inotify::MODIFY)
+end
+# Process the events, recompile and reload when necessary.
+i.each_event do |ev|
+ puts "I: file #{ev.name} modified, compiling #{infile}..."
+ compile(infile, mode)
+ reload(infile, mode)
+end