mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			439 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			439 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| # frozen_string_literal: false
 | |
| #
 | |
| # = un.rb
 | |
| #
 | |
| # Copyright (c) 2003 WATANABE Hirofumi <eban@ruby-lang.org>
 | |
| #
 | |
| # This program is free software.
 | |
| # You can distribute/modify this program under the same terms of Ruby.
 | |
| #
 | |
| # == Utilities to replace common UNIX commands in Makefiles etc
 | |
| #
 | |
| # == SYNOPSIS
 | |
| #
 | |
| #   ruby -run -e cp -- [OPTION] SOURCE DEST
 | |
| #   ruby -run -e ln -- [OPTION] TARGET LINK_NAME
 | |
| #   ruby -run -e mv -- [OPTION] SOURCE DEST
 | |
| #   ruby -run -e rm -- [OPTION] FILE
 | |
| #   ruby -run -e mkdir -- [OPTION] DIRS
 | |
| #   ruby -run -e rmdir -- [OPTION] DIRS
 | |
| #   ruby -run -e install -- [OPTION] SOURCE DEST
 | |
| #   ruby -run -e chmod -- [OPTION] OCTAL-MODE FILE
 | |
| #   ruby -run -e touch -- [OPTION] FILE
 | |
| #   ruby -run -e wait_writable -- [OPTION] FILE
 | |
| #   ruby -run -e mkmf -- [OPTION] EXTNAME [OPTION]
 | |
| #   ruby -run -e httpd -- [OPTION] [DocumentRoot]
 | |
| #   ruby -run -e colorize -- [FILE]
 | |
| #   ruby -run -e help [COMMAND]
 | |
| 
 | |
| require "fileutils"
 | |
| require "optparse"
 | |
| 
 | |
| module FileUtils
 | |
| #  @fileutils_label = ""
 | |
|   @fileutils_output = $stdout
 | |
| end
 | |
| 
 | |
| # :nodoc:
 | |
| def setup(options = "", *long_options)
 | |
|   caller = caller_locations(1, 1)[0].label
 | |
|   opt_hash = {}
 | |
|   argv = []
 | |
|   OptionParser.new do |o|
 | |
|     options.scan(/.:?/) do |s|
 | |
|       opt_name = s.delete(":").intern
 | |
|       o.on("-" + s.tr(":", " ")) do |val|
 | |
|         opt_hash[opt_name] = val
 | |
|       end
 | |
|     end
 | |
|     long_options.each do |s|
 | |
|       opt_name, arg_name = s.split(/(?=[\s=])/, 2)
 | |
|       opt_name.delete_prefix!('--')
 | |
|       s = "--#{opt_name.gsub(/([A-Z]+|[a-z])([A-Z])/, '\1-\2').downcase}#{arg_name}"
 | |
|       puts "#{opt_name}=>#{s}" if $DEBUG
 | |
|       opt_name = opt_name.intern
 | |
|       o.on(s) do |val|
 | |
|         opt_hash[opt_name] = val
 | |
|       end
 | |
|     end
 | |
|     o.on("-v") do opt_hash[:verbose] = true end
 | |
|     o.on("--help") do
 | |
|       UN.help([caller])
 | |
|       exit
 | |
|     end
 | |
|     o.order!(ARGV) do |x|
 | |
|       if /[*?\[{]/ =~ x
 | |
|         argv.concat(Dir[x])
 | |
|       else
 | |
|         argv << x
 | |
|       end
 | |
|     end
 | |
|   end
 | |
|   yield argv, opt_hash
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY
 | |
| #
 | |
| #   ruby -run -e cp -- [OPTION] SOURCE DEST
 | |
| #
 | |
| #   -p          preserve file attributes if possible
 | |
| #   -r          copy recursively
 | |
| #   -l          make hard link instead of copying (implies -r)
 | |
| #   -v          verbose
 | |
| #
 | |
| 
 | |
| def cp
 | |
|   setup("prl") do |argv, options|
 | |
|     cmd = "cp"
 | |
|     cmd += "_r" if options.delete :r
 | |
|     cmd = "cp_lr" if options.delete :l
 | |
|     options[:preserve] = true if options.delete :p
 | |
|     dest = argv.pop
 | |
|     argv = argv[0] if argv.size == 1
 | |
|     FileUtils.__send__ cmd, argv, dest, **options
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Create a link to the specified TARGET with LINK_NAME.
 | |
| #
 | |
| #   ruby -run -e ln -- [OPTION] TARGET LINK_NAME
 | |
| #
 | |
| #   -s          make symbolic links instead of hard links
 | |
| #   -f          remove existing destination files
 | |
| #   -v          verbose
 | |
| #
 | |
| 
 | |
| def ln
 | |
|   setup("sf") do |argv, options|
 | |
|     cmd = "ln"
 | |
|     cmd += "_s" if options.delete :s
 | |
|     options[:force] = true if options.delete :f
 | |
|     dest = argv.pop
 | |
|     argv = argv[0] if argv.size == 1
 | |
|     FileUtils.__send__ cmd, argv, dest, **options
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
 | |
| #
 | |
| #   ruby -run -e mv -- [OPTION] SOURCE DEST
 | |
| #
 | |
| #   -v          verbose
 | |
| #
 | |
| 
 | |
| def mv
 | |
|   setup do |argv, options|
 | |
|     dest = argv.pop
 | |
|     argv = argv[0] if argv.size == 1
 | |
|     FileUtils.mv argv, dest, **options
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Remove the FILE
 | |
| #
 | |
| #   ruby -run -e rm -- [OPTION] FILE
 | |
| #
 | |
| #   -f          ignore nonexistent files
 | |
| #   -r          remove the contents of directories recursively
 | |
| #   -v          verbose
 | |
| #
 | |
| 
 | |
| def rm
 | |
|   setup("fr") do |argv, options|
 | |
|     cmd = "rm"
 | |
|     cmd += "_r" if options.delete :r
 | |
|     options[:force] = true if options.delete :f
 | |
|     FileUtils.__send__ cmd, argv, **options
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Create the DIR, if they do not already exist.
 | |
| #
 | |
| #   ruby -run -e mkdir -- [OPTION] DIR
 | |
| #
 | |
| #   -p          no error if existing, make parent directories as needed
 | |
| #   -v          verbose
 | |
| #
 | |
| 
 | |
| def mkdir
 | |
|   setup("p") do |argv, options|
 | |
|     cmd = "mkdir"
 | |
|     cmd += "_p" if options.delete :p
 | |
|     FileUtils.__send__ cmd, argv, **options
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Remove the DIR.
 | |
| #
 | |
| #   ruby -run -e rmdir -- [OPTION] DIR
 | |
| #
 | |
| #   -p          remove DIRECTORY and its ancestors.
 | |
| #   -v          verbose
 | |
| #
 | |
| 
 | |
| def rmdir
 | |
|   setup("p") do |argv, options|
 | |
|     options[:parents] = true if options.delete :p
 | |
|     FileUtils.rmdir argv, **options
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Copy SOURCE to DEST.
 | |
| #
 | |
| #   ruby -run -e install -- [OPTION] SOURCE DEST
 | |
| #
 | |
| #   -p          apply access/modification times of SOURCE files to
 | |
| #               corresponding destination files
 | |
| #   -m          set permission mode (as in chmod), instead of 0755
 | |
| #   -o          set owner user id, instead of the current owner
 | |
| #   -g          set owner group id, instead of the current group
 | |
| #   -v          verbose
 | |
| #
 | |
| 
 | |
| def install
 | |
|   setup("pm:o:g:") do |argv, options|
 | |
|     (mode = options.delete :m) and options[:mode] = /\A\d/ =~ mode ? mode.oct : mode
 | |
|     options[:preserve] = true if options.delete :p
 | |
|     (owner = options.delete :o) and options[:owner] = owner
 | |
|     (group = options.delete :g) and options[:group] = group
 | |
|     dest = argv.pop
 | |
|     argv = argv[0] if argv.size == 1
 | |
|     FileUtils.install argv, dest, **options
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Change the mode of each FILE to OCTAL-MODE.
 | |
| #
 | |
| #   ruby -run -e chmod -- [OPTION] OCTAL-MODE FILE
 | |
| #
 | |
| #   -v          verbose
 | |
| #
 | |
| 
 | |
| def chmod
 | |
|   setup do |argv, options|
 | |
|     mode = argv.shift
 | |
|     mode = /\A\d/ =~ mode ? mode.oct : mode
 | |
|     FileUtils.chmod mode, argv, **options
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Update the access and modification times of each FILE to the current time.
 | |
| #
 | |
| #   ruby -run -e touch -- [OPTION] FILE
 | |
| #
 | |
| #   -v          verbose
 | |
| #
 | |
| 
 | |
| def touch
 | |
|   setup do |argv, options|
 | |
|     FileUtils.touch argv, **options
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Wait until the file becomes writable.
 | |
| #
 | |
| #   ruby -run -e wait_writable -- [OPTION] FILE
 | |
| #
 | |
| #   -n RETRY    count to retry
 | |
| #   -w SEC      each wait time in seconds
 | |
| #   -v          verbose
 | |
| #
 | |
| 
 | |
| def wait_writable
 | |
|   setup("n:w:v") do |argv, options|
 | |
|     verbose = options[:verbose]
 | |
|     n = options[:n] and n = Integer(n)
 | |
|     wait = (wait = options[:w]) ? Float(wait) : 0.2
 | |
|     argv.each do |file|
 | |
|       begin
 | |
|         open(file, "r+b")
 | |
|       rescue Errno::ENOENT
 | |
|         break
 | |
|       rescue Errno::EACCES => e
 | |
|         raise if n and (n -= 1) <= 0
 | |
|         if verbose
 | |
|           puts e
 | |
|           STDOUT.flush
 | |
|         end
 | |
|         sleep wait
 | |
|         retry
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Create makefile using mkmf.
 | |
| #
 | |
| #   ruby -run -e mkmf -- [OPTION] EXTNAME [OPTION]
 | |
| #
 | |
| #   -d ARGS     run dir_config
 | |
| #   -h ARGS     run have_header
 | |
| #   -l ARGS     run have_library
 | |
| #   -f ARGS     run have_func
 | |
| #   -v ARGS     run have_var
 | |
| #   -t ARGS     run have_type
 | |
| #   -m ARGS     run have_macro
 | |
| #   -c ARGS     run have_const
 | |
| #   --vendor    install to vendor_ruby
 | |
| #
 | |
| 
 | |
| def mkmf
 | |
|   setup("d:h:l:f:v:t:m:c:", "vendor") do |argv, options|
 | |
|     require 'mkmf'
 | |
|     opt = options[:d] and opt.split(/:/).each {|n| dir_config(*n.split(/,/))}
 | |
|     opt = options[:h] and opt.split(/:/).each {|n| have_header(*n.split(/,/))}
 | |
|     opt = options[:l] and opt.split(/:/).each {|n| have_library(*n.split(/,/))}
 | |
|     opt = options[:f] and opt.split(/:/).each {|n| have_func(*n.split(/,/))}
 | |
|     opt = options[:v] and opt.split(/:/).each {|n| have_var(*n.split(/,/))}
 | |
|     opt = options[:t] and opt.split(/:/).each {|n| have_type(*n.split(/,/))}
 | |
|     opt = options[:m] and opt.split(/:/).each {|n| have_macro(*n.split(/,/))}
 | |
|     opt = options[:c] and opt.split(/:/).each {|n| have_const(*n.split(/,/))}
 | |
|     $configure_args["--vendor"] = true if options[:vendor]
 | |
|     create_makefile(*argv)
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Run WEBrick HTTP server.
 | |
| #
 | |
| #   ruby -run -e httpd -- [OPTION] [DocumentRoot]
 | |
| #
 | |
| #   --bind-address=ADDR         address to bind
 | |
| #   --port=NUM                  listening port number
 | |
| #   --max-clients=MAX           max number of simultaneous clients
 | |
| #   --temp-dir=DIR              temporary directory
 | |
| #   --do-not-reverse-lookup     disable reverse lookup
 | |
| #   --request-timeout=SECOND    request timeout in seconds
 | |
| #   --http-version=VERSION      HTTP version
 | |
| #   --server-name=NAME          name of the server host
 | |
| #   --server-software=NAME      name and version of the server
 | |
| #   --ssl-certificate=CERT      The SSL certificate file for the server
 | |
| #   --ssl-private-key=KEY       The SSL private key file for the server certificate
 | |
| #   -v                          verbose
 | |
| #
 | |
| 
 | |
| def httpd
 | |
|   setup("", "BindAddress=ADDR", "Port=PORT", "MaxClients=NUM", "TempDir=DIR",
 | |
|         "DoNotReverseLookup", "RequestTimeout=SECOND", "HTTPVersion=VERSION",
 | |
|         "ServerName=NAME", "ServerSoftware=NAME",
 | |
|         "SSLCertificate=CERT", "SSLPrivateKey=KEY") do
 | |
|     |argv, options|
 | |
|     begin
 | |
|       require 'webrick'
 | |
|     rescue LoadError
 | |
|       abort "webrick is not found. You may need to `gem install webrick` to install webrick."
 | |
|     end
 | |
|     opt = options[:RequestTimeout] and options[:RequestTimeout] = opt.to_i
 | |
|     [:Port, :MaxClients].each do |name|
 | |
|       opt = options[name] and (options[name] = Integer(opt)) rescue nil
 | |
|     end
 | |
|     if cert = options[:SSLCertificate]
 | |
|       key = options[:SSLPrivateKey] or
 | |
|         raise "--ssl-private-key option must also be given"
 | |
|       require 'webrick/https'
 | |
|       options[:SSLEnable] = true
 | |
|       options[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.read(cert))
 | |
|       options[:SSLPrivateKey] = OpenSSL::PKey.read(File.read(key))
 | |
|       options[:Port] ||= 8443   # HTTPS Alternate
 | |
|     end
 | |
|     options[:Port] ||= 8080     # HTTP Alternate
 | |
|     options[:DocumentRoot] = argv.shift || '.'
 | |
|     s = nil
 | |
|     options[:StartCallback] = proc {
 | |
|       logger = s.logger
 | |
|       logger.info("To access this server, open this URL in a browser:")
 | |
|       s.listeners.each do |listener|
 | |
|         if options[:SSLEnable]
 | |
|           addr = listener.addr
 | |
|           addr[3] = "127.0.0.1" if addr[3] == "0.0.0.0"
 | |
|           addr[3] = "::1" if addr[3] == "::"
 | |
|           logger.info("    https://#{Addrinfo.new(addr).inspect_sockaddr}")
 | |
|         else
 | |
|           logger.info("    http://#{listener.connect_address.inspect_sockaddr}")
 | |
|         end
 | |
|       end
 | |
|     }
 | |
|     s = WEBrick::HTTPServer.new(options)
 | |
|     shut = proc {s.shutdown}
 | |
|     siglist = %w"TERM QUIT"
 | |
|     siglist.concat(%w"HUP INT") if STDIN.tty?
 | |
|     siglist &= Signal.list.keys
 | |
|     siglist.each do |sig|
 | |
|       Signal.trap(sig, shut)
 | |
|     end
 | |
|     s.start
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Colorize ruby code.
 | |
| #
 | |
| #   ruby -run -e colorize -- [FILE]
 | |
| #
 | |
| 
 | |
| def colorize
 | |
|   begin
 | |
|     require "irb/color"
 | |
|   rescue LoadError
 | |
|     raise "colorize requires irb 1.1.0 or later"
 | |
|   end
 | |
|   setup do |argv, |
 | |
|     if argv.empty?
 | |
|       puts IRB::Color.colorize_code STDIN.read
 | |
|       return
 | |
|     end
 | |
|     argv.each do |file|
 | |
|       puts IRB::Color.colorize_code File.read(file)
 | |
|     end
 | |
|   end
 | |
| end
 | |
| 
 | |
| ##
 | |
| # Display help message.
 | |
| #
 | |
| #   ruby -run -e help [COMMAND]
 | |
| #
 | |
| 
 | |
| def help
 | |
|   setup do |argv,|
 | |
|     UN.help(argv)
 | |
|   end
 | |
| end
 | |
| 
 | |
| module UN # :nodoc:
 | |
|   module_function
 | |
|   def help(argv, output: $stdout)
 | |
|     all = argv.empty?
 | |
|     cmd = nil
 | |
|     if all
 | |
|       store = proc {|msg| output << msg}
 | |
|     else
 | |
|       messages = {}
 | |
|       store = proc {|msg| messages[cmd] = msg}
 | |
|     end
 | |
|     open(__FILE__) do |me|
 | |
|       while me.gets("##\n")
 | |
|         if help = me.gets("\n\n")
 | |
|           if all or argv.include?(cmd = help[/^#\s*ruby\s.*-e\s+(\w+)/, 1])
 | |
|             store[help.gsub(/^# ?/, "")]
 | |
|             break unless all or argv.size > messages.size
 | |
|           end
 | |
|         end
 | |
|       end
 | |
|     end
 | |
|     if messages
 | |
|       argv.each {|arg| output << messages[arg]}
 | |
|     end
 | |
|   end
 | |
| end
 | 
