Compare commits

...

19 Commits
master ... main

Author SHA1 Message Date
Paul van Tilburg 6ee6a68694
Bump the version to 0.9 2023-12-12 15:08:04 +01:00
Paul van Tilburg 5b7130c2f9
Fix infinite loop when the viewer exits
* The viewer now sends `SIGHUP` to the main process when it exists
* The main process signal handler kills the viewer and terminates the
  notifier/main event loop when receiving `SIGINT`/`SIGTERM`/`SIGQUIT`
* The main process signal handler just terminates the notifier/main
  event loop when receiving `SIGHUP`
* Clean just after the notifier/main loop finishes
2023-12-12 15:08:04 +01:00
Paul van Tilburg fb1a0606fd
Fix code style and log message 2023-12-12 15:08:04 +01:00
Paul van Tilburg 174ec96c6d
Remove (commented) code that cleans after compile
This removes code commented out/refactored in commit 2cf1654.
2023-12-12 15:08:00 +01:00
Paul van Tilburg 2c996c7a18
Rename TODO to TODO.md because it is in Markdown format 2022-11-05 15:15:59 +01:00
Paul van Tilburg 0fe0a966f0
Rename COPYING to LICENSE for consistency 2022-11-05 15:15:23 +01:00
Paul van Tilburg 240ea58411
Fix confusing syntax 2022-11-05 15:08:15 +01:00
Paul van Tilburg 84b231d729
Bump the version to 0.8 2021-12-09 17:45:34 +01:00
Paul van Tilburg d78227a8e2
Just use relative paths (so they match up) 2021-12-09 17:45:09 +01:00
Paul van Tilburg c50791cba1
Preserve dirname for .with_extension; drop .base 2021-12-09 17:42:17 +01:00
Paul van Tilburg aa605a4306
Bump the version to 0.7 2020-12-04 22:40:45 +01:00
Paul van Tilburg c5d363406e
Fix infinite loop when exiting viewer
The CLD signal handler cleaned the ini file leading to another CLD
signal, which called the handler again etc.
Replace this by just killing the main process after the viewer exists
in the code for the fork.

Also note that we don't know which child process exited in the CLD
signal handler. This might be the viewer, but also rubber.
2020-12-04 22:37:54 +01:00
Paul van Tilburg bfdff5745b
Fix Rubocop style issues 2020-12-04 22:28:31 +01:00
Paul van Tilburg 15727a2f92 Remove trailing whitespace; fix minor warnings 2019-03-30 20:23:07 +01:00
Paul van Tilburg 11cd5be441 Againt, the directory is a Pathname object now 2018-06-22 23:13:37 +02:00
Paul van Tilburg 3a5609a5a3 Remove the unnecessary empty string argument 2018-06-22 23:13:21 +02:00
Paul van Tilburg 34bd63ca3d The directory is a Pathname object now; set the correct file path 2018-06-22 23:09:14 +02:00
Paul van Tilburg 49991c8924 Get the real path of the files and ensure uniqueness 2018-06-22 22:59:28 +02:00
Admar Schoonen c81a670261 Added printing empty lines after compilation
Adding printing an empty line after a compile iteration has finished
makes it easier to parse the output and look for errors.
2018-06-22 21:16:26 +02:00
3 changed files with 93 additions and 84 deletions

View File

View File

177
rubberin
View File

@ -13,29 +13,31 @@
# the result of the LaTeX compilation and forces it to reload every time # the result of the LaTeX compilation and forces it to reload every time
# rubberin has recompiled the LaTeX file. # rubberin has recompiled the LaTeX file.
# #
# This program is free software; you can redistribute it and/or modify it # 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 # 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 # Free Software Foundation; either version 2 of the License, or (at your
# option) any later version. # option) any later version.
# #
# This program is distributed in the hope that it will be useful, but # This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WAR RANTY; without even the implied warranty of # WITHOUT ANY WAR RANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details. # Public License for more details.
# #
# You should have received a copy of the GNU General Public License along # 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., # with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
require 'getoptlong' require "English"
require 'rb-inotify' require "getoptlong"
require 'pathname' require "rb-inotify"
require "pathname"
# Small necessary Pathname class extension. # Small necessary Pathname class extension.
class Pathname class Pathname
def extname=(ext) def extname=(ext)
raise "Invalid extension" unless ext.is_a? String and ext.match(/^\./) raise "Invalid extension" unless ext.is_a?(String) && ext.match(/^\./)
@path.sub!(/#{extname}$/, ext) @path.sub!(/#{extname}$/, ext)
end end
end end
@ -44,7 +46,7 @@ end
# Print the usage. # Print the usage.
def usage def usage
puts "Usage: #{Program} [options]... [tex-file] [rubber-options]..." puts "Usage: #{PROGRAM} [options]... [tex-file] [rubber-options]..."
end end
# Print the command line help. # Print the command line help.
@ -62,34 +64,33 @@ end
# Compile the input file using the right options based on the mode. # Compile the input file using the right options based on the mode.
def compile(infile, mode) def compile(infile, mode)
mode_opt = {:pdf => "--pdf", mode_opt = { pdf: "--pdf",
:ps => "--ps", ps: "--ps",
:pspdf => "--ps --pdf"}[mode] pspdf: "--ps --pdf" }[mode]
params = ARGV.join(' ') params = ARGV.join(" ")
err_file = infile.with_extname('err') err_file = infile.with_extname("err")
ret = system "rubber --inplace #{mode_opt} #{params} #{infile} 2> #{err_file}" system "rubber --inplace #{mode_opt} #{params} #{infile} 2> #{err_file}"
File.open(err_file) { |file| puts file.read } File.open(err_file) { |file| puts file.read }
# Remove the output save file if compile was succesful.
#clean(infile) if ret
end end
# Start the right viewer based on the mode. # Start the right viewer based on the mode.
def view(infile, mode) def view(infile, mode)
case mode case mode
when :dvi when :dvi
exec "xdvi.bin", "-watchfile", "1", system "xdvi.bin",
"-name", "xdvi", infile.with_extname('dvi').to_s "-watchfile", "1",
when :ps "-name", "xdvi", infile.with_extname("dvi").to_s
exec "evince", infile.with_extname('ps').to_s when :ps
when :pdf, :pspdf system "evince", infile.with_extname("ps").to_s
exec "evince", infile.with_extname('pdf').to_s when :pdf, :pspdf
system "evince", infile.with_extname("pdf").to_s
end end
end end
# Clean up cruft related to compilation of the input file. # Clean up cruft related to compilation of the input file.
def clean(infile) def clean(infile)
err_file = infile.with_extname('err') err_file = infile.with_extname("err")
File.unlink(err_file) if err_file.exist? File.unlink(err_file) if err_file.exist?
system "rubber --clean --inplace #{infile}" system "rubber --clean --inplace #{infile}"
end end
@ -97,55 +98,54 @@ end
# Reload the right viewer based on the mode. # Reload the right viewer based on the mode.
def reload(infile, mode) def reload(infile, mode)
case mode case mode
when :ps when :ps
system "evince", infile.with_extname('ps').to_s system "evince", infile.with_extname("ps").to_s
when :pdf, :pspdf when :pdf, :pspdf
system "evince", infile.with_extname('pdf').to_s system "evince", infile.with_extname("pdf").to_s
end end
end end
## Initialisation ## Initialisation
Program = File.basename $0 PROGRAM = File.basename($PROGRAM_NAME).freeze
Version = '0.6' VERSION = "0.9".freeze
# Parse the command line options. # Parse the command line options.
# Determine the compile mode from the options. # Determine the compile mode from the options.
mode = :dvi mode = :dvi
opts = GetoptLong.new( opts = GetoptLong.new(["--help", "-h", GetoptLong::NO_ARGUMENT],
[ "--help", "-h", GetoptLong::NO_ARGUMENT ], ["--ps", "-p", GetoptLong::NO_ARGUMENT],
[ "--ps", "-p", GetoptLong::NO_ARGUMENT ], ["--pdf", "-d", GetoptLong::NO_ARGUMENT],
[ "--pdf", "-d", GetoptLong::NO_ARGUMENT ], ["--version", "-v", GetoptLong::NO_ARGUMENT])
[ "--version", "-v", GetoptLong::NO_ARGUMENT ])
opts.ordering = GetoptLong::REQUIRE_ORDER opts.ordering = GetoptLong::REQUIRE_ORDER
opts.quiet = true opts.quiet = true
opts.each do |opt, arg| opts.each do |opt, _arg|
case opt case opt
when "--help" when "--help"
help help
exit 0 exit 0
when "--ps" when "--ps"
mode = :ps mode = :ps
when "--pdf" when "--pdf"
mode = if mode == :ps then :pspdf mode = if mode == :ps
else :pdf :pspdf
end else
when "--version" :pdf
puts "#{Program} #{Version}" end
exit 0 when "--version"
puts "#{PROGRAM} #{VERSION}"
exit 0
end end
end end
# Get the input file from the arguments. # Get the input file from the arguments.
if ARGV.length < 1 if ARGV.empty?
usage usage
exit 1 exit 1
else else
infile = Pathname.new(ARGV.shift) infile = Pathname.new(ARGV.shift)
def infile.base
self.basename(self.extname)
end
def infile.with_extname(ext) def infile.with_extname(ext)
file = self.base file = dirname + basename(extname)
file.extname = ".#{ext}" file.extname = ".#{ext}"
file file
end end
@ -154,49 +154,58 @@ end
# Check the input file. # Check the input file.
begin begin
File.open(infile) {} File.open(infile) {}
rescue => err rescue SystemCallError => e
puts "#{Program}: #{err}" puts "#{PROGRAM}: #{e}"
exit 2 exit 2
end end
# Find the dependancies of the input file using rubber-info and # Find the dependencies of the input file using rubber-info and
# do an initial run. # do an initial run.
files = `rubber-info --deps #{infile}`.chomp.split files = `rubber-info --deps #{infile}`.chomp.split
compile(infile, mode) compile(infile, mode)
puts
# Spawn a viewer based on the mode. # Spawn a viewer based on the mode.
viewer_pid = fork pid = $PROCESS_ID
view(infile, mode) if not viewer_pid viewer_pid =
fork do
# Handle signals. view(infile, mode)
["INT", "TERM", "QUIT"].each do |sig| # If xdvi/evince exits, this program should exit too.
Signal.trap(sig) do puts "#{PROGRAM}: viewer exited, so will I!"
Process.kill(sig, viewer_pid) Process.kill("HUP", pid)
clean(infile)
exit
end end
end
# If xdvi exits, this program should exit too.
Signal.trap("CLD") do
puts "#{Program}: viewer exited, so will I!"
clean(infile)
exit
end
## Main event loop ## Main event loop
# Add input file with dependancies to the watch list and start event loop. # Add input file with dependancies to the watch list and start event loop.
notifier = INotify::Notifier.new notifier = INotify::Notifier.new
dirs = files.map { |file| File.dirname(file) } dirs = files.map { |file| Pathname.new(file).dirname }.uniq
dirs.each do |dir| dirs.each do |dir|
# Set up a watch per directory # Set up a watch per directory
notifier.watch(dir, :close_write) do |ev| notifier.watch(dir.to_s, :close_write) do |ev|
# Only compile if a dependency of the input file has been modified # Only compile if a dependency of the input file has been modified
next unless files.include?(dir + "/" + ev.name) file_path = (dir + ev.name).to_s
puts "I: file #{ev.name} modified, compiling #{infile}..." next unless files.include? file_path
puts "#{PROGRAM}: file #{ev.name} modified, compiling #{infile}..."
compile(infile, mode) compile(infile, mode)
reload(infile, mode) reload(infile, mode)
puts
end end
end end
# Handle signals during the main event loop.
["INT", "TERM", "QUIT"].each do |sig|
Signal.trap(sig) do
puts "#{PROGRAM}: caught signal #{sig}, stopping the viewer..."
Process.kill(sig, viewer_pid)
notifier.stop
end
end
Signal.trap("HUP") { notifier.stop }
# Run the main event loop.
notifier.run notifier.run
# Clean before finishing!
clean(infile)