module ACOC
Constants
- VERSION
Public Instance Methods
cmd()
click to toggle source
All options from the configuration file, in a Hash
# File lib/acoc.rb, line 23 def cmd @@cmd = Config.new unless defined? @@cmd @@cmd end
colour(prog, *cmd_line)
click to toggle source
process program output, one line at a time
# File lib/acoc.rb, line 248 def colour(prog, *cmd_line) block = proc do |f| while ! f.eof? begin line = f.gets rescue # why do we need rescue here? exit # why the Errno::EIO when running ls(1)? end coloured_line = colour_line(prog, line) begin print coloured_line rescue Errno::EPIPE => reason # catch broken pipes $stderr.puts reason exit Errno::EPIPE::Errno end end end # take care of any embedded single quotes in args expanded from globs cmd_line.map! { |arg| arg.gsub(/'/, %q('"'"')) } # prepare command line: requote each argument for the shell cmd_line = "'" << cmd_line.join(%q(' ')) << "'" # redirect stderr to stdout if /e flag given cmd_line << " 2>&1" if @@cmd[prog].flags.include? 'e' # make sure we don't buffer output when stdout is connected to a pipe $stdout.sync = true # install signal handler trap_signal(%w(HUP INT QUIT CLD)) if @@cmd[prog].flags.include?('p') && $LOADED_FEATURES.include?('tpty.so') # allocate program a pseudo-terminal and run through that pty = TPty.new do |s,| fork do # redirect child streams to slave STDIN.reopen(s) STDOUT.reopen(s) #STDERR.reopen(s) s.close run(cmd_line) end end # no buffering on pty # pty.master.sync = true block.call(pty.master) else # execute command IO.popen(cmd_line) { |f| block.call(f) } end end
colour_line(prog, line)
click to toggle source
match and colour an individual line
# File lib/acoc.rb, line 199 def colour_line(prog, line) matched = false # act on only the first match unless the /a flag was given return if matched && ! @@cmd[prog].flags.include?('a') # get a pattern and attribute set pairing for this command @@cmd[prog].specs.each do |spec| if r = spec.regex.match(line) # line matches this regex matched = true if spec.flags.include? 'g' # global flag matches = 0 # perform global substitution line.gsub!(spec.regex) do |match| index = [matches, spec.colours.size - 1].min spec.colours[index].split(/[+\s]+/).each do |colour| match = match.send(colour) end matches += 1 match end else # colour each match separately # work from right to left, bracketing each match (r.size - 1).downto(1) do |i| start = r.begin(i) length = r.end(i) - start index = [i - 1, spec.colours.size - 1].min ansi_offset = 0 spec.colours[index].split(/[+\s]+/).each do |colour| line[start + ansi_offset, length] = line[start + ansi_offset, length].send(colour) # when applying multiple colours, we apply them one at a # time, so we need to compensate for the start of the string # moving to the right as the colour codes are applied ansi_offset += send(colour).length end end end end end line end
initialise()
click to toggle source
set things up
# File lib/acoc.rb, line 31 def initialise # Queen's or Dubya's English? if ENV['LANG'] == "en_US" || ENV['LC_ALL'] == "en_US" @@colour = "color" else @@colour = "colour" end config_files = %w(/etc/acoc.conf /usr/local/etc/acoc.conf) config_files << ENV['HOME'] + "/.acoc.conf" config_files << ENV['ACOCRC'] if ENV['ACOCRC'] # If there's no config file on user's home directory, # we'll place our default one there. # # The default one lies on the same directory as the # rest of the source code. # Since __FILE__ is inside lib/, we'll need to jump # above. # if not File.exists? File.expand_path '~/.acoc.conf' fixed_config = File.expand_path(File.dirname(__FILE__) + '/../acoc.conf') FileUtils.cp(fixed_config, File.expand_path('~/.acoc.conf')) end if parse_config(*config_files) == 0 $stderr.puts "No readable config files found." exit 1 end end
parse_config(*files)
click to toggle source
get configuration data
# File lib/acoc.rb, line 91 def parse_config(*files) @@cmd = Config.new parsed = 0 files.each do |file| $stderr.printf("Attempting to read config file: %s\n", file) if $DEBUG next unless file && FileTest::file?(file) && FileTest::readable?(file) begin f = File.open(file) do |f| while line = f.gets do next if line =~ /^(#|$)/ # skip blank lines and comments if line =~ /^[@\[]([^\]]+)[@\]]$/ # start of program section # get program invocation progs = $1.split(/\s*,\s*/) progs.each do |prog| invocation, flags = prog.split(%r(/)) if ! flags.nil? && flags.include?('r') # remove matching entries for this program program = invocation.sub(/\s.*$/, '') @@cmd.each_key do |key| @@cmd.delete(key) if key =~ /^#{program}\b/ end flags.delete 'r' end # create entry for this program if @@cmd.has_key?(invocation) @@cmd[invocation].flags += flags unless flags.nil? else @@cmd[invocation] = Program.new(flags) end prog.sub!(%r(/\w+), '') end next end begin regex, flags, colours = /^(.)([^\1]*)\1(g?)\s+(.*)/.match(line)[2..4] rescue $stderr.puts "Ignoring bad config line #{$NR}: #{line}" end colours = colours.split(/\s*,\s*/) colours.join(' ').split(/[+\s]+/).each do |colour| raise "#{@@colour} is not a supported #{@@colour}" \ unless attributes.collect { |a| a.to_s }.include? colour end progs.each do |prog| @@cmd[prog].specs << Rule.new(Regexp.new(regex), flags, colours) end end end rescue Errno::ENOENT $stderr.puts "Failed to open config file: #{$ERROR_INFO}" exit 1 rescue $stderr.puts "Error while parsing config file #{file} @ line #{$NR}: #{$ERROR_INFO}" exit 2 end parsed += 1 end $stderr.printf("Action data: %s\n", @@cmd.inspect) if $DEBUG parsed end
run(args)
click to toggle source
# File lib/acoc.rb, line 189 def run(args) exec(*args) rescue Errno::ENOENT => reason # can't find the program we're supposed to run $stderr.puts reason exit Errno::ENOENT::Errno end
trap_signal(signals)
click to toggle source
# File lib/acoc.rb, line 164 def trap_signal(signals) signals.each do |signal| trap(signal) do # make sure terminal is never left in a coloured state begin print reset rescue Errno::EPIPE # Errno::EPIPE can occur when we're being piped end # reap the child and collect its exit status # WNOHANG is needed to prevent hang when pty is used begin pid, status = Process.waitpid2(-1, Process::WNOHANG) rescue Errno::ECHILD # Errno::ECHILD can occur in waitpid2 ensure # exit must be wrapped in at_exit to make sure all output buffers # are flushed. Shift 8 bits to convert exit/signal to just exit status at_exit { exit status.nil? ? 0 : status >> 8 } end end end end
usage(code = 0)
click to toggle source
display usage message and exit
# File lib/acoc.rb, line 65 def usage(code = 0) $stderr.puts <<EOF Usage: acoc command [arg1 .. argN] acoc [-h|--help|-v|--version] EOF exit code end
version()
click to toggle source
display version and copyright message, then exit
# File lib/acoc.rb, line 76 def version $stderr.puts <<EOF acoc #{ACOC::VERSION} Copyright 2003-2005 Ian Macdonald <ian@caliban.org> This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, to the extent permitted by law. EOF exit end