class SFL

Constants

REDIRECTION_MAPPING
SHELL_SPECIALS
VERSION

Attributes

argument[R]
command[R]
environment[R]
option[R]

Public Class Methods

eval_ast(ast) click to toggle source
# File lib/sfl.rb, line 136
def eval_ast(ast)
  case ast
  when Array
    if ast.size > 2
      eval_ast(ast[0]).send(ast[1], *ast[2..-1].map {|i| eval_ast(i) })
    else
      eval_ast(ast[0]).send(ast[1])
    end
  else
    ast
  end
end
new(*cmdandarg) click to toggle source

SFL.new('ls', '-a') becomes

@environment = {}
@command = ['ls', 'ls']
@argument = ['-a']
@option = {}
# File lib/sfl.rb, line 13
def initialize(*cmdandarg)
  raise ArgumentError if cmdandarg.size == 0
  cmdandarg = cmdandarg.dup

  @environment =
    if Hash === cmdandarg.first
      cmdandarg.shift
    else
      {}
    end

  @option =
    if Hash === cmdandarg.last
      cmdandarg.pop
    else
      {}
    end

  if cmdandarg.size == 1
    cmdandarg = cmdandarg.first
    if String === cmdandarg
      if SHELL_SPECIALS === cmdandarg
        @command = cmdandarg
        @argument = []
      else
        cmd, *arg = self.class.parse_command_with_arg(cmdandarg)
        @command = [cmd, cmd]
        @argument = arg
      end
    else
      @command = cmdandarg
      @argument = []
    end
  else
    # 'ls', '.' -> [['ls', 'ls'], '.']
    cmd = cmdandarg.shift
    cmd = (String === cmd) ? [cmd, cmd] : cmd
    @command = cmd
    @argument = cmdandarg
  end
end
option_parser(hash) click to toggle source
# File lib/sfl.rb, line 98
def option_parser(hash)
  result = []

  # changing dir has high priority
  chdir = hash.delete(:chdir)
  if chdir
    result[0] = [Dir, :chdir, chdir]
  end

  # other options
  result += hash.map {|k, v|
    case k
    when :in, :out, :err
      if right = redirection_ast(v, k)
        [[REDIRECTION_MAPPING[k], :reopen, right]]    
      else
        [[REDIRECTION_MAPPING[k], :close]]    
      end
    when Array
      # assuming k is like [:out, :err]
      raise NotImplementedError if k.size > 2
      left1, left2 = *k.map {|i| REDIRECTION_MAPPING[i] }
      if right = redirection_ast(v)
        [
          [left1, :reopen, right],
          [left2, :reopen, left1],
        ]
      else
        [
          [left1, :close],
          [left2, :close],
        ]
      end
    end
  }.flatten(1)
  result
end
parse_command_with_arg(x) click to toggle source
# File lib/sfl.rb, line 149
def parse_command_with_arg(x)
  in_squote = false
  in_dquote = false
  tmp = ''
  cmdargs = []
  x.strip.split(//).each do |c|
    case c
    when '"'
      if in_dquote
        in_dquote = false
      else
        in_dquote = true
      end
    when "'"
      if in_squote
        in_squote = false
      else
        in_squote = true
      end
    when ' '
      if in_dquote || in_squote
        tmp << ' '
      else
        cmdargs << tmp
        tmp = ''
      end
    else
      tmp << c
    end
  end
  cmdargs << tmp
end
redirection_ast(v, what_for = :out) click to toggle source
# File lib/sfl.rb, line 81
def redirection_ast(v, what_for = :out)
  case v
  when Integer
    raise NotImplementedError, "Redirection to integer FD not yet implemented"
  when :close
    nil
  when :in, :out, :err
    REDIRECTION_MAPPING[v]
  when String # filename
    [File, :open, v, (what_for == :in ? 'r' : 'w')]
  when Array # filename with option
    [File, :open, v[0], v[1]]
  when IO
    v
  end
end

Public Instance Methods

==(o) click to toggle source
# File lib/sfl.rb, line 67
def ==(o) # Mostly for rspec
  instance_variables.all? do |i|
    i = i[1..-1] # '@a' -> 'a'
    eval "self.#{i} == o.#{i}"
  end
end
run() click to toggle source
# File lib/sfl.rb, line 55
def run
  fork {
    @environment.each do |k, v|
      ENV[k] = v
    end
    self.class.option_parser(@option).each do |ast|
      self.class.eval_ast ast
    end
    exec(@command, *@argument)
  }
end