diff options
Diffstat (limited to 'lib/un.rb')
| -rw-r--r-- | lib/un.rb | 356 |
1 files changed, 285 insertions, 71 deletions
@@ -1,11 +1,12 @@ -# +# 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 @@ -19,36 +20,55 @@ # 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 'getopts' +require "fileutils" +require "optparse" module FileUtils -# @fileutils_label = '' @fileutils_output = $stdout end -def setup(options = "") - options += "v" - ARGV.map! do |x| - case x - when /^-/ - x.delete "^-#{options}" - when /[*?\[{]/ - Dir[x] - else - x +# :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 - ARGV.flatten! - ARGV.delete_if{|x| x == '-'} - getopts(options) - options = {} - options[:verbose] = true if $OPT["v"] - options[:force] = true if $OPT["f"] - options[:preserve] = true if $OPT["p"] - yield ARGV, options, $OPT + yield argv, opt_hash end ## @@ -56,18 +76,21 @@ end # # ruby -run -e cp -- [OPTION] SOURCE DEST # -# -p preserve file attributes if possible -# -r copy recursively -# -v verbose +# -p preserve file attributes if possible +# -r copy recursively +# -l make hard link instead of copying (implies -r) +# -v verbose # def cp - setup("pr") do |argv, options, opt| + setup("prl") do |argv, options| cmd = "cp" - cmd += "_r" if opt["r"] + 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 + FileUtils.__send__ cmd, argv, dest, **options end end @@ -76,18 +99,19 @@ end # # ruby -run -e ln -- [OPTION] TARGET LINK_NAME # -# -s make symbolic links instead of hard links -# -f remove existing destination files -# -v verbose +# -s make symbolic links instead of hard links +# -f remove existing destination files +# -v verbose # def ln - setup("sf") do |argv, options, opt| + setup("sf") do |argv, options| cmd = "ln" - cmd += "_s" if opt["s"] + 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 + FileUtils.__send__ cmd, argv, dest, **options end end @@ -96,14 +120,14 @@ end # # ruby -run -e mv -- [OPTION] SOURCE DEST # -# -v verbose +# -v verbose # def mv setup do |argv, options| dest = argv.pop argv = argv[0] if argv.size == 1 - FileUtils.mv argv, dest, options + FileUtils.mv argv, dest, **options end end @@ -112,16 +136,17 @@ end # # ruby -run -e rm -- [OPTION] FILE # -# -f ignore nonexistent files -# -r remove the contents of directories recursively -# -v verbose +# -f ignore nonexistent files +# -r remove the contents of directories recursively +# -v verbose # def rm - setup("fr") do |argv, options, opt| + setup("fr") do |argv, options| cmd = "rm" - cmd += "_r" if opt["r"] - FileUtils.send cmd, argv, options + cmd += "_r" if options.delete :r + options[:force] = true if options.delete :f + FileUtils.__send__ cmd, argv, **options end end @@ -130,15 +155,15 @@ end # # ruby -run -e mkdir -- [OPTION] DIR # -# -p no error if existing, make parent directories as needed -# -v verbose +# -p no error if existing, make parent directories as needed +# -v verbose # def mkdir - setup("p") do |argv, options, opt| + setup("p") do |argv, options| cmd = "mkdir" - cmd += "_p" if options.delete :preserve - FileUtils.send cmd, argv, options + cmd += "_p" if options.delete :p + FileUtils.__send__ cmd, argv, **options end end @@ -147,12 +172,14 @@ end # # ruby -run -e rmdir -- [OPTION] DIR # -# -v verbose +# -p remove DIRECTORY and its ancestors. +# -v verbose # def rmdir - setup do |argv, options| - FileUtils.rmdir argv, options + setup("p") do |argv, options| + options[:parents] = true if options.delete :p + FileUtils.rmdir argv, **options end end @@ -161,18 +188,23 @@ end # # 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 -# -v verbose +# -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:") do |argv, options, opt| - options[:mode] = opt["m"] ? opt["m"].oct : 0755 + 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 + FileUtils.install argv, dest, **options end end @@ -181,13 +213,14 @@ end # # ruby -run -e chmod -- [OPTION] OCTAL-MODE FILE # -# -v verbose +# -v verbose # def chmod setup do |argv, options| - mode = argv.shift.oct - FileUtils.chmod mode, argv, options + mode = argv.shift + mode = /\A\d/ =~ mode ? mode.oct : mode + FileUtils.chmod mode, argv, **options end end @@ -196,12 +229,172 @@ end # # ruby -run -e touch -- [OPTION] FILE # -# -v verbose +# -v verbose # def touch setup do |argv, options| - FileUtils.touch 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 + File.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 @@ -213,15 +406,36 @@ end def help setup do |argv,| + UN.help(argv) + end +end + +module UN # :nodoc: + + VERSION = "0.3.0" + + module_function + def help(argv, output: $stdout) all = argv.empty? - open(__FILE__) do |me| + cmd = nil + if all + store = proc {|msg| output << msg} + else + messages = {} + store = proc {|msg| messages[cmd] = msg} + end + File.open(__FILE__) do |me| while me.gets("##\n") - if help = me.gets("\n\n") - if all or argv.delete help[/-e \w+/].sub(/-e /, "") - print help.gsub(/^# ?/, "") - end - end + 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 |
