summaryrefslogtreecommitdiff
path: root/lib/rubygems/util.rb
blob: 9bfe13f815df3d4b478ded761e2a371c2934f317 (plain)
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
127
128
129
130
131
132
133
134
##
# This module contains various utility methods as module methods.

module Gem::Util

  @silent_mutex = nil

  ##
  # Zlib::GzipReader wrapper that unzips +data+.

  def self.gunzip(data)
    require 'zlib'
    require 'stringio'
    data = StringIO.new(data, 'r')

    unzipped = Zlib::GzipReader.new(data).read
    unzipped.force_encoding Encoding::BINARY if Object.const_defined? :Encoding
    unzipped
  end

  ##
  # Zlib::GzipWriter wrapper that zips +data+.

  def self.gzip(data)
    require 'zlib'
    require 'stringio'
    zipped = StringIO.new('','w')
    zipped.set_encoding Encoding::BINARY if Object.const_defined? :Encoding

    Zlib::GzipWriter.wrap zipped do |io| io.write data end

    zipped.string
  end

  ##
  # A Zlib::Inflate#inflate wrapper

  def self.inflate(data)
    require 'zlib'
    Zlib::Inflate.inflate data
  end

  ##
  # This calls IO.popen where it accepts an array for a +command+ (Ruby 1.9+)
  # and implements an IO.popen-like behavior where it does not accept an array
  # for a command.

  def self.popen *command
    IO.popen command, &:read
  rescue TypeError # ruby 1.8 only supports string command
    r, w = IO.pipe

    pid = fork do
      STDIN.close
      STDOUT.reopen w

      exec(*command)
    end

    w.close

    begin
      return r.read
    ensure
      Process.wait pid
    end
  end

  NULL_DEVICE = defined?(IO::NULL) ? IO::NULL : Gem.win_platform? ? 'NUL' : '/dev/null'

  ##
  # Invokes system, but silences all output.

  def self.silent_system *command
    opt = {:out => NULL_DEVICE, :err => [:child, :out]}
    if Hash === command.last
      opt.update(command.last)
      cmds = command[0...-1]
    else
      cmds = command.dup
    end
    return system(*(cmds << opt))
  rescue TypeError
    require 'thread'

    @silent_mutex ||= Mutex.new

    null_device = NULL_DEVICE

    @silent_mutex.synchronize do
      begin
        stdout = STDOUT.dup
        stderr = STDERR.dup

        STDOUT.reopen null_device, 'w'
        STDERR.reopen null_device, 'w'

        return system(*command)
      ensure
        STDOUT.reopen stdout
        STDERR.reopen stderr
        stdout.close
        stderr.close
      end
    end
  end

  ##
  # Enumerates the parents of +directory+.

  def self.traverse_parents directory
    return enum_for __method__, directory unless block_given?

    here = File.expand_path directory
    start = here

    Dir.chdir start

    begin
      loop do
        yield here

        Dir.chdir '..'

        return if Dir.pwd == here # toplevel

        here = Dir.pwd
      end
    ensure
      Dir.chdir start
    end
  end

end