about summary refs log tree commit diff
path: root/lrep
blob: dafc6449c5e7daf2f0f425c7dce9e1b9c7d155e4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env ruby
# lrep [-z] [-t SECS] [FILES...] - literate read-eval-print

# -*- ruby -*-
#
# To the extent possible under law,
# Christian Neukirchen <chneukirchen@gmail.com>
# 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