Class: Aspera::Environment
- Inherits:
-
Object
- Object
- Aspera::Environment
- Includes:
- Singleton
- Defined in:
- lib/aspera/environment.rb
Overview
detect OS, architecture, and specific stuff
Constant Summary collapse
- OS_WINDOWS =
:windows- OS_MACOS =
:osx- OS_LINUX =
:linux- OS_AIX =
:aix- OS_LIST =
[OS_WINDOWS, OS_MACOS, OS_LINUX, OS_AIX].freeze
- CPU_X86_64 =
:x86_64- CPU_ARM64 =
:arm64- CPU_PPC64 =
:ppc64- CPU_PPC64LE =
:ppc64le- CPU_S390 =
:s390- CPU_LIST =
[CPU_X86_64, CPU_ARM64, CPU_PPC64, CPU_PPC64LE, CPU_S390].freeze
- BITS_PER_BYTE =
8- MEBI =
1024 * 1024
- BYTES_PER_MEBIBIT =
MEBI / BITS_PER_BYTE
- I18N_VARS =
%w(LC_ALL LC_CTYPE LANG).freeze
- WINDOWS_FILENAME_INVALID_CHARACTERS =
"/" is invalid on both Unix and Windows, other are Windows special characters See: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
'<>:"/\\|?*'- REPLACE_CHARACTER =
'_'- RB_EXT =
'.rb'- PROCESS_MODES =
%i[execute background capture].freeze
Instance Attribute Summary collapse
-
#cpu ⇒ Object
readonly
Returns the value of attribute cpu.
-
#default_gui_mode ⇒ Object
readonly
Returns the value of attribute default_gui_mode.
-
#file_illegal_characters ⇒ Object
Returns the value of attribute file_illegal_characters.
-
#os ⇒ Object
readonly
Returns the value of attribute os.
-
#url_method ⇒ Object
Returns the value of attribute url_method.
Class Method Summary collapse
-
.build_spawn_argv(cmd, kwargs) ⇒ Object
Build argv for Process.spawn / Kernel.system (no shell).
-
.empty_binding ⇒ Object
Empty variable binding for secure eval.
-
.force_terminal_c ⇒ Object
force locale to C so that unicode characters are not used.
-
.instance ⇒ Environment
Returns the singleton instance of Environment.
-
.restrict_file_access(path, mode: nil) ⇒ Object
restrict access to a file or folder to user only.
- .ruby_version ⇒ Object
-
.secure_eval(code, file, line, user_binding = nil) ⇒ Object
Secure execution of Ruby code.
-
.secure_execute(*cmd, mode: :execute, **kwargs) ⇒ Boolean, ...
Executes a command without invoking a shell.
-
.shell_escape_pretty(str) ⇒ Object
like
Shellwords.shellescape, but does not escape=. -
.terminal? ⇒ Boolean
True if we are in a terminal.
-
.terminal_supports_unicode? ⇒ Boolean
Uses Encoding.locale_charmap for OS-independent detection.
-
.write_file_restricted(path, force: false, mode: nil) ⇒ Object
Write content to a file, with restricted access.
Instance Method Summary collapse
-
#architecture ⇒ Object
Normalized architecture name See constants: OS_* and CPU_*.
-
#exe_file(name = nil) ⇒ String
Add executable file extension (e.g. ".exe") for current OS.
-
#fix_home ⇒ Object
on Windows, the env var %USERPROFILE% provides the path to user's home more reliably than %HOMEDRIVE%%HOMEPATH% so, tell Ruby the right way.
- #graphical? ⇒ Boolean
-
#initialize ⇒ Environment
constructor
A new instance of Environment.
-
#initialize_fields ⇒ Object
initialize fields from environment.
-
#open_editor(file_path) ⇒ Object
open a file in an editor.
-
#open_uri(the_url) ⇒ Object
Allows a user to open a URL if method is :text, then URL is displayed on terminal if method is :graphical, then the URL will be opened with the default browser.
-
#open_uri_graphical(uri) ⇒ Object
Open a URI in a graphical browser Command must be non blocking.
-
#safe_filename_character ⇒ String
Replacement character for illegal filename characters Can also be used as safe "join" character.
-
#sanitized_filename(filename) ⇒ String
Sanitize a filename by replacing illegal characters.
Constructor Details
#initialize ⇒ Environment
Returns a new instance of Environment.
222 223 224 |
# File 'lib/aspera/environment.rb', line 222 def initialize initialize_fields end |
Instance Attribute Details
#cpu ⇒ Object (readonly)
Returns the value of attribute cpu.
220 221 222 |
# File 'lib/aspera/environment.rb', line 220 def cpu @cpu end |
#default_gui_mode ⇒ Object (readonly)
Returns the value of attribute default_gui_mode.
220 221 222 |
# File 'lib/aspera/environment.rb', line 220 def default_gui_mode @default_gui_mode end |
#file_illegal_characters ⇒ Object
Returns the value of attribute file_illegal_characters.
219 220 221 |
# File 'lib/aspera/environment.rb', line 219 def file_illegal_characters @file_illegal_characters end |
#os ⇒ Object (readonly)
Returns the value of attribute os.
220 221 222 |
# File 'lib/aspera/environment.rb', line 220 def os @os end |
#url_method ⇒ Object
Returns the value of attribute url_method.
219 220 221 |
# File 'lib/aspera/environment.rb', line 219 def url_method @url_method end |
Class Method Details
.build_spawn_argv(cmd, kwargs) ⇒ Object
Build argv for Process.spawn / Kernel.system (no shell)
72 73 74 75 76 77 78 79 |
# File 'lib/aspera/environment.rb', line 72 def build_spawn_argv(cmd, kwargs) env = kwargs.delete(:env) argv = [] argv << env if env argv << [cmd.first, cmd.first] # no shell, preserve argv[0] argv.concat(cmd.drop(1)) argv end |
.empty_binding ⇒ Object
Empty variable binding for secure eval
57 58 59 |
# File 'lib/aspera/environment.rb', line 57 def empty_binding return Kernel.binding end |
.force_terminal_c ⇒ Object
force locale to C so that unicode characters are not used
194 195 196 |
# File 'lib/aspera/environment.rb', line 194 def force_terminal_c I18N_VARS.each{ |var| ENV[var] = 'C'} end |
.instance ⇒ Environment
Returns the singleton instance of Environment
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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 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 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/aspera/environment.rb', line 20 class Environment include Singleton OS_WINDOWS = :windows OS_MACOS = :osx OS_LINUX = :linux OS_AIX = :aix OS_LIST = [OS_WINDOWS, OS_MACOS, OS_LINUX, OS_AIX].freeze CPU_X86_64 = :x86_64 CPU_ARM64 = :arm64 CPU_PPC64 = :ppc64 CPU_PPC64LE = :ppc64le CPU_S390 = :s390 CPU_LIST = [CPU_X86_64, CPU_ARM64, CPU_PPC64, CPU_PPC64LE, CPU_S390].freeze BITS_PER_BYTE = 8 MEBI = 1024 * 1024 BYTES_PER_MEBIBIT = MEBI / BITS_PER_BYTE I18N_VARS = %w(LC_ALL LC_CTYPE LANG).freeze # "/" is invalid on both Unix and Windows, other are Windows special characters # See: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file WINDOWS_FILENAME_INVALID_CHARACTERS = '<>:"/\\|?*' REPLACE_CHARACTER = '_' RB_EXT = '.rb' PROCESS_MODES = %i[execute background capture].freeze class << self def ruby_version return RbConfig::CONFIG['RUBY_PROGRAM_VERSION'] end # Empty variable binding for secure eval def empty_binding return Kernel.binding end # Secure execution of Ruby code # @param code [String] Ruby code to execute # @param file [String] File name for error reporting # @param line [Integer] Line number for error reporting def secure_eval(code, file, line, user_binding = nil) Kernel.send('lave'.reverse, code, user_binding || empty_binding, file, line) end # Build argv for Process.spawn / Kernel.system (no shell) # @param cmd [Array] Command and arguments # @param kwargs [Hash] Additional arguments to `secure_execute` def build_spawn_argv(cmd, kwargs) env = kwargs.delete(:env) argv = [] argv << env if env argv << [cmd.first, cmd.first] # no shell, preserve argv[0] argv.concat(cmd.drop(1)) argv end # like `Shellwords.shellescape`, but does not escape `=` def shell_escape_pretty(str) # Safe unquoted characters + '=' explicitly allowed return str if str.match?(%r{\A[A-Za-z0-9_.,:/@+=-]+\z}) # return str if Shellwords.shellescape(str) == str # Otherwise use single quotes "'#{str.gsub("'", %q('\'\''))}'" end # Executes a command without invoking a shell. # # The command is provided as an array to avoid shell interpolation and # ensure safer execution. # # @param cmd [Array<#to_s>] The executable and its arguments. # @param mode [:execute, :background, :capture] The execution strategy: # - `:execute` Uses {Kernel.system}. Returns `true`, `false`, or `nil`. (Default) # - `:background` Uses {Process.spawn}. Returns the spawned process PID. # - `:capture` Uses {Open3.capture3}. Returns captured out, err, and status. # # @param kwargs [Hash] Additional options forwarded to the underlying call. # # @option kwargs [Hash{String => String}] :env Environment variables to set for the process. # @option kwargs [Boolean] :exception (false) When `true` in `:capture` mode, # raises an error if the command exits with a non-zero status. # @option kwargs [Boolean] :close_others (true) When `true` in `:background` mode, # closes all other file descriptors in the child process. # # @return [Boolean, nil] For `:execute` mode (`true`, `false`, or `nil`). # @return [Integer] For `:background` mode (process ID). # @return [Array(String, String, Process::Status)] For `:capture` mode # (`stdout`, `stderr`, `status`). # # @raise [RuntimeError] If `:exception` is `true` and the process fails in `:capture` mode. def secure_execute(*cmd, mode: :execute, **kwargs) cmd = cmd.map(&:to_s) Aspera.assert(cmd.size.positive?, 'executable must be present', type: ArgumentError) Aspera.assert_values(mode, PROCESS_MODES, type: ArgumentError){'mode'} Log.log.debug do parts = [mode.to_s, 'command:'] kwargs[:env]&.each{ |k, v| parts << "#{k}=#{shell_escape_pretty(v.to_s)}"} cmd.each{ |a| parts << shell_escape_pretty(a)} parts.join(' ') end case mode when :execute # https://docs.ruby-lang.org/en/master/Kernel.html#method-i-system # https://docs.ruby-lang.org/en/master/Process.html#module-Process-label-Execution+Options kwargs[:exception] = true unless kwargs.key?(:exception) Kernel.system(*build_spawn_argv(cmd, kwargs), **kwargs) when :background # https://docs.ruby-lang.org/en/master/Process.html#method-c-spawn # https://docs.ruby-lang.org/en/master/Process.html#module-Process-label-Execution+Options kwargs[:close_others] = true unless kwargs.key?(:close_others) pid = Process.spawn(*build_spawn_argv(cmd, kwargs), **kwargs) Log.dump(:pid, pid) pid when :capture # https://docs.ruby-lang.org/en/master/Open3.html#method-c-capture3 # https://docs.ruby-lang.org/en/master/Process.html#module-Process-label-Execution+Options argv = [kwargs.delete(:env)].compact + cmd exception = kwargs.delete(:exception){true} result = Open3.capture3(*argv, **kwargs) Log.dump(:stdout, result[0], level: :trace1) Log.dump(:stderr, result[1], level: :trace1) Log.dump(:status, result[2]) raise "Process failed: #{result[2].exitstatus} (#{result[1]})" if exception && !result[2].success? result else Aspera.error_unreachable_line end end # Write content to a file, with restricted access # @param path [String] the file path # @param force [Boolean] if true, overwrite the file # @param mode [Integer] the file mode (permissions) # @yieldreturn [String] The content to write to the file def write_file_restricted(path, force: false, mode: nil) Aspera.assert(block_given?, 'block required for write_file_restricted', type: Aspera::InternalError) if force || !File.exist?(path) # Windows may give error File.unlink(path) rescue nil # content provided by block File.write(path, yield) restrict_file_access(path, mode: mode) end return path end # restrict access to a file or folder to user only def restrict_file_access(path, mode: nil) if mode.nil? # or FileUtils ? if File.file?(path) mode = 0o600 elsif File.directory?(path) mode = 0o700 else Log.log.debug{"No restriction can be set for #{path}"} end end File.chmod(mode, path) unless mode.nil? rescue => e Log.log.warn(e.) end # @return true if we are in a terminal def terminal? $stdout.tty? end # force locale to C so that unicode characters are not used def force_terminal_c I18N_VARS.each{ |var| ENV[var] = 'C'} end # @return true if we can display Unicode characters # Uses Encoding.locale_charmap for OS-independent detection. # Falls back to locale env vars for systems where charmap is not available. # https://www.gnu.org/software/libc/manual/html_node/Locale-Categories.html # https://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html def terminal_supports_unicode? return false unless terminal? locale_charmap_utf8? || I18N_VARS.any?{ |var| ENV[var]&.include?('UTF-8')} end private # @return true if the locale charmap resolves to UTF-8 # Unix: nl_langinfo(CODESET) returns "UTF-8" # Windows: GetACP returns "CP65001" which Encoding.find resolves as UTF-8 def locale_charmap_utf8? Encoding.find(Encoding.locale_charmap) == Encoding::UTF_8 rescue ArgumentError false end end attr_accessor :url_method, :file_illegal_characters attr_reader :os, :cpu, :default_gui_mode def initialize initialize_fields end # initialize fields from environment def initialize_fields @os = case RbConfig::CONFIG['host_os'] when /mswin/, /msys/, /mingw/, /cygwin/, /bccwin/, /wince/, /emc/ OS_WINDOWS when /darwin/, /mac os/ OS_MACOS when /linux/ OS_LINUX when /aix/ OS_AIX else Aspera.error_unexpected_value(RbConfig::CONFIG['host_os']){'host_os'} end @cpu = case RbConfig::CONFIG['host_cpu'] when /x86_64/, /x64/ CPU_X86_64 when /powerpc/, /ppc64/ @os.eql?(OS_LINUX) ? CPU_PPC64LE : CPU_PPC64 when /s390/ CPU_S390 when /arm/, /aarch64/ CPU_ARM64 else Aspera.error_unexpected_value(RbConfig::CONFIG['host_cpu']){'host_cpu'} end @executable_extension = @os.eql?(OS_WINDOWS) ? '.exe' : nil # :text or :graphical depending on the environment @default_gui_mode = if [Environment::OS_WINDOWS, Environment::OS_MACOS].include?(os) || (ENV.key?('DISPLAY') && !ENV['DISPLAY'].empty?) # assume not remotely connected on macos and windows or unix family :graphical else :text end @url_method = @default_gui_mode @file_illegal_characters = REPLACE_CHARACTER + WINDOWS_FILENAME_INVALID_CHARACTERS nil end # Normalized architecture name # See constants: OS_* and CPU_* def architecture "#{@os}-#{@cpu}" end # Add executable file extension (e.g. ".exe") for current OS # @param name [String,nil] Path or file name # @return [String] Executable name with extension def exe_file(name = nil) return name unless @executable_extension return "#{name}#{@executable_extension}" end # on Windows, the env var %USERPROFILE% provides the path to user's home more reliably than %HOMEDRIVE%%HOMEPATH% # so, tell Ruby the right way def fix_home return unless @os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV.fetch('USERPROFILE', nil)) ENV['HOME'] = ENV.fetch('USERPROFILE', nil) Log.log.debug{"Windows: set HOME to USERPROFILE: #{Dir.home}"} nil end def graphical? @default_gui_mode == :graphical end # Open a URI in a graphical browser # Command must be non blocking # @param uri [String] the URI to open def open_uri_graphical(uri) case @os when Environment::OS_MACOS then return self.class.secure_execute('open', uri.to_s) when Environment::OS_WINDOWS then return self.class.secure_execute('start', 'explorer', %Q{"#{uri}"}) when Environment::OS_LINUX then return self.class.secure_execute('xdg-open', uri.to_s) else Aspera.error_unexpected_value(os){'no graphical open method'} end end # open a file in an editor def open_editor(file_path) if ENV.key?('EDITOR') self.class.secure_execute(ENV['EDITOR'], file_path.to_s) elsif @os.eql?(Environment::OS_WINDOWS) self.class.secure_execute('notepad.exe', %Q{"#{file_path}"}) else open_uri_graphical(file_path.to_s) end end # Allows a user to open a URL # if method is :text, then URL is displayed on terminal # if method is :graphical, then the URL will be opened with the default browser. # this is non blocking def open_uri(the_url) case @url_method when :graphical open_uri_graphical(the_url) when :text case the_url.to_s when /^http/ puts "USER ACTION: please enter this URL in a browser:\n#{the_url.to_s.red}\n" else puts "USER ACTION: open this:\n#{the_url.to_s.red}\n" end else Aspera.error_unexpected_value(@url_method){'URL open method'} end end # Replacement character for illegal filename characters # Can also be used as safe "join" character # @return [String] One character def safe_filename_character return REPLACE_CHARACTER if @file_illegal_characters.nil? || @file_illegal_characters.empty? @file_illegal_characters[0] end # Sanitize a filename by replacing illegal characters # @param filename [String] the original filename # @return [String] A file name safe to use on file system def sanitized_filename(filename) safe_char = safe_filename_character # Windows does not allow file name: # - with control characters anywhere # - ending with space or dot filename = filename.gsub(/[\x00-\x1F\x7F]/, safe_char) filename = filename.chop while filename.end_with?(' ', '.') if @file_illegal_characters&.size.to_i >= 2 # replace all illegal characters with safe_char filename = filename.tr(@file_illegal_characters[1..], safe_char) end # ensure only one safe_char is used at a time return filename.gsub(/#{Regexp.escape(safe_char)}+/, safe_char).chomp(safe_char) end end |
.restrict_file_access(path, mode: nil) ⇒ Object
restrict access to a file or folder to user only
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/aspera/environment.rb', line 172 def restrict_file_access(path, mode: nil) if mode.nil? # or FileUtils ? if File.file?(path) mode = 0o600 elsif File.directory?(path) mode = 0o700 else Log.log.debug{"No restriction can be set for #{path}"} end end File.chmod(mode, path) unless mode.nil? rescue => e Log.log.warn(e.) end |
.ruby_version ⇒ Object
52 53 54 |
# File 'lib/aspera/environment.rb', line 52 def ruby_version return RbConfig::CONFIG['RUBY_PROGRAM_VERSION'] end |
.secure_eval(code, file, line, user_binding = nil) ⇒ Object
Secure execution of Ruby code
65 66 67 |
# File 'lib/aspera/environment.rb', line 65 def secure_eval(code, file, line, user_binding = nil) Kernel.send('lave'.reverse, code, user_binding || empty_binding, file, line) end |
.secure_execute(*cmd, mode: :execute, **kwargs) ⇒ Boolean, ...
Executes a command without invoking a shell.
The command is provided as an array to avoid shell interpolation and ensure safer execution.
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/aspera/environment.rb', line 116 def secure_execute(*cmd, mode: :execute, **kwargs) cmd = cmd.map(&:to_s) Aspera.assert(cmd.size.positive?, 'executable must be present', type: ArgumentError) Aspera.assert_values(mode, PROCESS_MODES, type: ArgumentError){'mode'} Log.log.debug do parts = [mode.to_s, 'command:'] kwargs[:env]&.each{ |k, v| parts << "#{k}=#{shell_escape_pretty(v.to_s)}"} cmd.each{ |a| parts << shell_escape_pretty(a)} parts.join(' ') end case mode when :execute # https://docs.ruby-lang.org/en/master/Kernel.html#method-i-system # https://docs.ruby-lang.org/en/master/Process.html#module-Process-label-Execution+Options kwargs[:exception] = true unless kwargs.key?(:exception) Kernel.system(*build_spawn_argv(cmd, kwargs), **kwargs) when :background # https://docs.ruby-lang.org/en/master/Process.html#method-c-spawn # https://docs.ruby-lang.org/en/master/Process.html#module-Process-label-Execution+Options kwargs[:close_others] = true unless kwargs.key?(:close_others) pid = Process.spawn(*build_spawn_argv(cmd, kwargs), **kwargs) Log.dump(:pid, pid) pid when :capture # https://docs.ruby-lang.org/en/master/Open3.html#method-c-capture3 # https://docs.ruby-lang.org/en/master/Process.html#module-Process-label-Execution+Options argv = [kwargs.delete(:env)].compact + cmd exception = kwargs.delete(:exception){true} result = Open3.capture3(*argv, **kwargs) Log.dump(:stdout, result[0], level: :trace1) Log.dump(:stderr, result[1], level: :trace1) Log.dump(:status, result[2]) raise "Process failed: #{result[2].exitstatus} (#{result[1]})" if exception && !result[2].success? result else Aspera.error_unreachable_line end end |
.shell_escape_pretty(str) ⇒ Object
like Shellwords.shellescape, but does not escape =
82 83 84 85 86 87 88 89 |
# File 'lib/aspera/environment.rb', line 82 def shell_escape_pretty(str) # Safe unquoted characters + '=' explicitly allowed return str if str.match?(%r{\A[A-Za-z0-9_.,:/@+=-]+\z}) # return str if Shellwords.shellescape(str) == str # Otherwise use single quotes "'#{str.gsub("'", %q('\'\''))}'" end |
.terminal? ⇒ Boolean
Returns true if we are in a terminal.
189 190 191 |
# File 'lib/aspera/environment.rb', line 189 def terminal? $stdout.tty? end |
.terminal_supports_unicode? ⇒ Boolean
Uses Encoding.locale_charmap for OS-independent detection. Falls back to locale env vars for systems where charmap is not available. https://www.gnu.org/software/libc/manual/html_node/Locale-Categories.html https://pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html
203 204 205 206 |
# File 'lib/aspera/environment.rb', line 203 def terminal_supports_unicode? return false unless terminal? locale_charmap_utf8? || I18N_VARS.any?{ |var| ENV[var]&.include?('UTF-8')} end |
.write_file_restricted(path, force: false, mode: nil) ⇒ Object
Write content to a file, with restricted access
159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/aspera/environment.rb', line 159 def write_file_restricted(path, force: false, mode: nil) Aspera.assert(block_given?, 'block required for write_file_restricted', type: Aspera::InternalError) if force || !File.exist?(path) # Windows may give error File.unlink(path) rescue nil # content provided by block File.write(path, yield) restrict_file_access(path, mode: mode) end return path end |
Instance Method Details
#architecture ⇒ Object
Normalized architecture name See constants: OS_* and CPU_*
269 270 271 |
# File 'lib/aspera/environment.rb', line 269 def architecture "#{@os}-#{@cpu}" end |
#exe_file(name = nil) ⇒ String
Add executable file extension (e.g. ".exe") for current OS
276 277 278 279 |
# File 'lib/aspera/environment.rb', line 276 def exe_file(name = nil) return name unless @executable_extension return "#{name}#{@executable_extension}" end |
#fix_home ⇒ Object
on Windows, the env var %USERPROFILE% provides the path to user's home more reliably than %HOMEDRIVE%%HOMEPATH% so, tell Ruby the right way
283 284 285 286 287 288 |
# File 'lib/aspera/environment.rb', line 283 def fix_home return unless @os.eql?(OS_WINDOWS) && ENV.key?('USERPROFILE') && Dir.exist?(ENV.fetch('USERPROFILE', nil)) ENV['HOME'] = ENV.fetch('USERPROFILE', nil) Log.log.debug{"Windows: set HOME to USERPROFILE: #{Dir.home}"} nil end |
#graphical? ⇒ Boolean
290 291 292 |
# File 'lib/aspera/environment.rb', line 290 def graphical? @default_gui_mode == :graphical end |
#initialize_fields ⇒ Object
initialize fields from environment
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'lib/aspera/environment.rb', line 227 def initialize_fields @os = case RbConfig::CONFIG['host_os'] when /mswin/, /msys/, /mingw/, /cygwin/, /bccwin/, /wince/, /emc/ OS_WINDOWS when /darwin/, /mac os/ OS_MACOS when /linux/ OS_LINUX when /aix/ OS_AIX else Aspera.error_unexpected_value(RbConfig::CONFIG['host_os']){'host_os'} end @cpu = case RbConfig::CONFIG['host_cpu'] when /x86_64/, /x64/ CPU_X86_64 when /powerpc/, /ppc64/ @os.eql?(OS_LINUX) ? CPU_PPC64LE : CPU_PPC64 when /s390/ CPU_S390 when /arm/, /aarch64/ CPU_ARM64 else Aspera.error_unexpected_value(RbConfig::CONFIG['host_cpu']){'host_cpu'} end @executable_extension = @os.eql?(OS_WINDOWS) ? '.exe' : nil # :text or :graphical depending on the environment @default_gui_mode = if [Environment::OS_WINDOWS, Environment::OS_MACOS].include?(os) || (ENV.key?('DISPLAY') && !ENV['DISPLAY'].empty?) # assume not remotely connected on macos and windows or unix family :graphical else :text end @url_method = @default_gui_mode @file_illegal_characters = REPLACE_CHARACTER + WINDOWS_FILENAME_INVALID_CHARACTERS nil end |
#open_editor(file_path) ⇒ Object
open a file in an editor
307 308 309 310 311 312 313 314 315 |
# File 'lib/aspera/environment.rb', line 307 def open_editor(file_path) if ENV.key?('EDITOR') self.class.secure_execute(ENV['EDITOR'], file_path.to_s) elsif @os.eql?(Environment::OS_WINDOWS) self.class.secure_execute('notepad.exe', %Q{"#{file_path}"}) else open_uri_graphical(file_path.to_s) end end |
#open_uri(the_url) ⇒ Object
Allows a user to open a URL if method is :text, then URL is displayed on terminal if method is :graphical, then the URL will be opened with the default browser. this is non blocking
321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/aspera/environment.rb', line 321 def open_uri(the_url) case @url_method when :graphical open_uri_graphical(the_url) when :text case the_url.to_s when /^http/ puts "USER ACTION: please enter this URL in a browser:\n#{the_url.to_s.red}\n" else puts "USER ACTION: open this:\n#{the_url.to_s.red}\n" end else Aspera.error_unexpected_value(@url_method){'URL open method'} end end |
#open_uri_graphical(uri) ⇒ Object
Open a URI in a graphical browser Command must be non blocking
297 298 299 300 301 302 303 304 |
# File 'lib/aspera/environment.rb', line 297 def open_uri_graphical(uri) case @os when Environment::OS_MACOS then return self.class.secure_execute('open', uri.to_s) when Environment::OS_WINDOWS then return self.class.secure_execute('start', 'explorer', %Q{"#{uri}"}) when Environment::OS_LINUX then return self.class.secure_execute('xdg-open', uri.to_s) else Aspera.error_unexpected_value(os){'no graphical open method'} end end |
#safe_filename_character ⇒ String
Replacement character for illegal filename characters Can also be used as safe "join" character
339 340 341 342 |
# File 'lib/aspera/environment.rb', line 339 def safe_filename_character return REPLACE_CHARACTER if @file_illegal_characters.nil? || @file_illegal_characters.empty? @file_illegal_characters[0] end |
#sanitized_filename(filename) ⇒ String
Sanitize a filename by replacing illegal characters
347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
# File 'lib/aspera/environment.rb', line 347 def sanitized_filename(filename) safe_char = safe_filename_character # Windows does not allow file name: # - with control characters anywhere # - ending with space or dot filename = filename.gsub(/[\x00-\x1F\x7F]/, safe_char) filename = filename.chop while filename.end_with?(' ', '.') if @file_illegal_characters&.size.to_i >= 2 # replace all illegal characters with safe_char filename = filename.tr(@file_illegal_characters[1..], safe_char) end # ensure only one safe_char is used at a time return filename.gsub(/#{Regexp.escape(safe_char)}+/, safe_char).chomp(safe_char) end |