Class: Chef::Util::PathHelper

Inherits:
Object
  • Object
show all
Defined in:
lib/chef/util/path_helper.rb

Constant Summary collapse

WIN_MAX_PATH =

Maximum characters in a standard Windows path (260 including drive letter and NUL)

259
BACKSLASH =
'\\'.freeze

Class Method Summary collapse

Class Method Details

.canonical_path(path, add_prefix = true) ⇒ Object

Produces a comparable path.

Raises:

  • (NotImplementedError)


103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/chef/util/path_helper.rb', line 103

def self.canonical_path(path, add_prefix=true)
  # Rather than find an equivalent for File.absolute_path on 1.8.7, just bail out
  raise NotImplementedError, "This feature is not supported on Ruby versions < 1.9" if RUBY_VERSION.to_f < 1.9

  # First remove extra separators and resolve any relative paths
  abs_path = File.absolute_path(path)

  if Chef::Platform.windows?
    # Add the \\?\ API prefix on Windows unless add_prefix is false
    # Downcase on Windows where paths are still case-insensitive
    abs_path.gsub!(::File::SEPARATOR, path_separator)
    if add_prefix && abs_path !~ /^\\\\?\\/
      abs_path.insert(0, "\\\\?\\")
    end

    abs_path.downcase!
  end

  abs_path
end

.cleanpath(path) ⇒ Object



124
125
126
127
128
129
130
131
# File 'lib/chef/util/path_helper.rb', line 124

def self.cleanpath(path)
  path = Pathname.new(path).cleanpath.to_s
  # ensure all forward slashes are backslashes
  if Chef::Platform.windows?
    path = path.gsub(File::SEPARATOR, path_separator)
  end
  path
end

.dirname(path) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/chef/util/path_helper.rb', line 25

def self.dirname(path)
  if Chef::Platform.windows?
    # Find the first slash, not counting trailing slashes
    end_slash = path.size
    while true
      slash = path.rindex(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]/, end_slash - 1)
      if !slash
        return end_slash == path.size ? '.' : path_separator
      elsif slash == end_slash - 1
        end_slash = slash
      else
        return path[0..slash-1]
      end
    end
  else
    ::File.dirname(path)
  end
end

.escape_glob(*parts) ⇒ Object

Paths which may contain glob-reserved characters need to be escaped before globbing can be done. stackoverflow.com/questions/14127343



140
141
142
143
# File 'lib/chef/util/path_helper.rb', line 140

def self.escape_glob(*parts)
  path = cleanpath(join(*parts))
  path.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\"+x }
end

.join(*args) ⇒ Object



54
55
56
57
58
59
60
61
# File 'lib/chef/util/path_helper.rb', line 54

def self.join(*args)
  args.flatten.inject do |joined_path, component|
    # Joined path ends with /
    joined_path = joined_path.sub(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]+$/, '')
    component = component.sub(/^[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]+/, '')
    joined_path += "#{path_separator}#{component}"
  end
end

.path_separatorObject



46
47
48
49
50
51
52
# File 'lib/chef/util/path_helper.rb', line 46

def self.path_separator
  if Chef::Platform.windows?
    File::ALT_SEPARATOR || BACKSLASH
  else
    File::SEPARATOR
  end
end

.paths_eql?(path1, path2) ⇒ Boolean

Returns:

  • (Boolean)


133
134
135
# File 'lib/chef/util/path_helper.rb', line 133

def self.paths_eql?(path1, path2)
  canonical_path(path1) == canonical_path(path2)
end

.printable?(string) ⇒ Boolean

Returns:

  • (Boolean)


92
93
94
95
96
97
98
99
100
# File 'lib/chef/util/path_helper.rb', line 92

def self.printable?(string)
  # returns true if string is free of non-printable characters (escape sequences)
  # this returns false for whitespace escape sequences as well, e.g. \n\t
  if string =~ /[^[:print:]]/
    false
  else
    true
  end
end

.relative_path_from(from, to) ⇒ Object



145
146
147
# File 'lib/chef/util/path_helper.rb', line 145

def self.relative_path_from(from, to)
  pathname = Pathname.new(Chef::Util::PathHelper.cleanpath(to)).relative_path_from(Pathname.new(Chef::Util::PathHelper.cleanpath(from)))
end

.validate_path(path) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/chef/util/path_helper.rb', line 63

def self.validate_path(path)
  if Chef::Platform.windows?
    unless printable?(path)
      msg = "Path '#{path}' contains non-printable characters. Check that backslashes are escaped with another backslash (e.g. C:\\\\Windows) in double-quoted strings."
      Chef::Log.error(msg)
      raise Chef::Exceptions::ValidationFailed, msg
    end

    if windows_max_length_exceeded?(path)
      Chef::Log.debug("Path '#{path}' is longer than #{WIN_MAX_PATH}, prefixing with'\\\\?\\'")
      path.insert(0, "\\\\?\\")
    end
  end

  path
end

.windows_max_length_exceeded?(path) ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
83
84
85
86
87
88
89
90
# File 'lib/chef/util/path_helper.rb', line 80

def self.windows_max_length_exceeded?(path)
  # Check to see if paths without the \\?\ prefix are over the maximum allowed length for the Windows API
  # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
  unless path =~ /^\\\\?\\/
    if path.length > WIN_MAX_PATH
      return true
    end
  end

  false
end