#!/usr/bin/env ruby # lrep [-z] [-t SECS] [FILES...] - literate read-eval-print # -*- ruby -*- # # To the extent possible under law, # Christian Neukirchen # has waived all copyright and related or neighboring rights to this work. # http://creativecommons.org/publicdomain/zero/1.0/ require 'pty' require 'io/console' require 'optparse' params = ARGV.getopts("t:z") zap = params["z"] $timeout = params["t"] ? params["t"].to_f : 0.04 def readp(output, ps, timeout=$timeout) return "[DEAD]" if output == nil o = ps ? output.readpartial(1024) : "" timedout = true loop { if o.split("\r\n").last =~ ps timedout = false break end if IO.select([output],[],[],timeout) begin o << output.readpartial(1024) rescue Errno::EIO # eof break end else break end } o = "[TIMEOUT]\r\n" + o if timedout && ps l = o.split("\r\n") if ps && l[-1] l[-1].gsub!(ps, "") l.pop if l[-1].empty? end l.join("\n") end def ind(txt) txt.split("\n").map { |l| "\t#{l}\n" }.join end def kill(pid) if pid n = 0 until Process.waitpid(pid, Process::WNOHANG) Process.kill(n > 10 ? "KILL" : "TERM", pid) sleep 0.1 n += 1 end end end output = input = pid = nil trap(:SIGCHLD) { input = output = nil } ps = nil while line = gets if line =~ /^\t!(.*?)(?:!(.*?))?!(.*)/ puts line begin if $1.empty? && $2.empty? kill pid output = input = pid = nil next if zap cmd = $3 silent = cmd.gsub!(/>-$/, '') o = `#{cmd}` print ind(o) unless silent else if $1.empty? ps = nil psl = /^\t.*#{Regexp.quote $2}(.*)/ else ps = /.*?#{Regexp.union [$1,$2].compact}$/ psl = /^\t.*?#{Regexp.union [$1,$2].compact}(.*)/ end next if zap cmd = $3 silent = cmd.gsub!(/>-$/, '') kill pid output, input, pid = PTY.spawn cmd input.echo = false # more timeout to launch program o = readp(output, ps, 20*$timeout) print ind(o) unless silent end rescue print ind("ERROR: #{$!.message}") end elsif line =~ psl puts line next if zap if input cmd = $1 silent = cmd.gsub!(/>-$/, '') input.write cmd + "\n" o = readp(output, ps) print ind(o) unless silent end elsif line =~ /^\t/ # old output, remove else puts line end end kill pid