1#
2# Copyright (c) 2008 James M. Lawrence
3#
4# Permission is hereby granted, free of charge, to any person
5# obtaining a copy of this software and associated documentation files
6# (the "Software"), to deal in the Software without restriction,
7# including without limitation the rights to use, copy, modify, merge,
8# publish, distribute, sublicense, and/or sell copies of the Software,
9# and to permit persons to whom the Software is furnished to do so,
10# subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be
13# included in all copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22# SOFTWARE.
23#
24
25require 'rbconfig'
26
27#
28# Alternate implementations of system() and backticks `` on Windows
29# for ruby-1.8 and earlier.
30#
31module Rake::AltSystem
32  WINDOWS = RbConfig::CONFIG["host_os"] =~
33    %r!(msdos|mswin|djgpp|mingw|[Ww]indows)!
34
35  class << self
36    def define_module_function(name, &block)
37      define_method(name, &block)
38      module_function(name)
39    end
40  end
41
42  if WINDOWS and RUBY_VERSION < "1.9.0"
43    RUNNABLE_EXTS = %w[com exe bat cmd]
44    RUNNABLE_PATTERN = %r!\.(#{RUNNABLE_EXTS.join('|')})\Z!i
45
46    define_module_function :kernel_system, &Kernel.method(:system)
47    define_module_function :kernel_backticks, &Kernel.method(:'`')
48
49    module_function
50
51    def repair_command(cmd)
52      "call " + (
53        if cmd =~ %r!\A\s*\".*?\"!
54          # already quoted
55          cmd
56        elsif match = cmd.match(%r!\A\s*(\S+)!)
57          if match[1] =~ %r!/!
58            # avoid x/y.bat interpretation as x with option /y
59            %Q!"#{match[1]}"! + match.post_match
60          else
61            # a shell command will fail if quoted
62            cmd
63          end
64        else
65          # empty or whitespace
66          cmd
67        end
68      )
69    end
70
71    def find_runnable(file)
72      if file =~ RUNNABLE_PATTERN
73        file
74      else
75        RUNNABLE_EXTS.each { |ext|
76          if File.exist?(test = "#{file}.#{ext}")
77            return test
78          end
79        }
80        nil
81      end
82    end
83
84    def system(cmd, *args)
85      repaired = (
86        if args.empty?
87          [repair_command(cmd)]
88        elsif runnable = find_runnable(cmd)
89          [File.expand_path(runnable), *args]
90        else
91          # non-existent file
92          [cmd, *args]
93        end
94      )
95      kernel_system(*repaired)
96    end
97
98    def backticks(cmd)
99      kernel_backticks(repair_command(cmd))
100    end
101
102    define_module_function :'`', &method(:backticks)
103  else
104    # Non-Windows or ruby-1.9+: same as Kernel versions
105    define_module_function :system, &Kernel.method(:system)
106    define_module_function :backticks, &Kernel.method(:'`')
107    define_module_function :'`', &Kernel.method(:'`')
108  end
109end
110