Class: SFL

Inherits:
Object
  • Object
show all
Defined in:
lib/sfl.rb

Constant Summary collapse

VERSION =
"2.2".freeze
SHELL_SPECIALS =
%r([\*\?\{\}\[\]<>\(\)~&\|\\\$;'`"\n])m.freeze
REDIRECTION_MAPPING =
{
  :in  => STDIN,
  :out => STDOUT,
  :err => STDERR,
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*cmdandarg) ⇒ SFL

SFL.new(‘ls’, ‘-a’) becomes

@environment = {}
@command = ['ls', 'ls']
@argument = ['-a']
@option = {}

Raises:

  • (ArgumentError)


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
# 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

Instance Attribute Details

#argumentObject (readonly)

Returns the value of attribute argument.



6
7
8
# File 'lib/sfl.rb', line 6

def argument
  @argument
end

#commandObject (readonly)

Returns the value of attribute command.



6
7
8
# File 'lib/sfl.rb', line 6

def command
  @command
end

#environmentObject (readonly)

Returns the value of attribute environment.



6
7
8
# File 'lib/sfl.rb', line 6

def environment
  @environment
end

#optionObject (readonly)

Returns the value of attribute option.



6
7
8
# File 'lib/sfl.rb', line 6

def option
  @option
end

Class Method Details

.eval_ast(ast) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
# 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

.option_parser(hash) ⇒ Object



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
127
128
129
130
131
132
133
134
# 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) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# 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) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# 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

Instance Method Details

#==(o) ⇒ Object

Mostly for rspec



67
68
69
70
71
72
# 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

#runObject



55
56
57
58
59
60
61
62
63
64
65
# 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