1 # frozen_string_literal: false
5 # Copyright (c) 2003 WATANABE Hirofumi <eban@ruby-lang.org>
7 # This program is free software.
8 # You can distribute/modify this program under the same terms of Ruby.
10 # == Utilities to replace common UNIX commands in Makefiles etc
14 # ruby -run -e cp -- [OPTION] SOURCE DEST
15 # ruby -run -e ln -- [OPTION] TARGET LINK_NAME
16 # ruby -run -e mv -- [OPTION] SOURCE DEST
17 # ruby -run -e rm -- [OPTION] FILE
18 # ruby -run -e mkdir -- [OPTION] DIRS
19 # ruby -run -e rmdir -- [OPTION] DIRS
20 # ruby -run -e install -- [OPTION] SOURCE DEST
21 # ruby -run -e chmod -- [OPTION] OCTAL-MODE FILE
22 # ruby -run -e touch -- [OPTION] FILE
23 # ruby -run -e wait_writable -- [OPTION] FILE
24 # ruby -run -e mkmf -- [OPTION] EXTNAME [OPTION]
25 # ruby -run -e httpd -- [OPTION] [DocumentRoot]
26 # ruby -run -e colorize -- [FILE]
27 # ruby -run -e help [COMMAND]
33 @fileutils_output = $stdout
37 def setup(options = "", *long_options)
38 caller = caller_locations(1, 1)[0].label
41 OptionParser.new do |o|
42 options.scan(/.:?/) do |s|
43 opt_name = s.delete(":").intern
44 o.on("-" + s.tr(":", " ")) do |val|
45 opt_hash[opt_name] = val
48 long_options.each do |s|
49 opt_name, arg_name = s.split(/(?=[\s=])/, 2)
50 opt_name.delete_prefix!('--')
51 s = "--#{opt_name.gsub(/([A-Z]+|[a-z])([A-Z])/, '\1-\2').downcase}#{arg_name}"
52 puts "#{opt_name}=>#{s}" if $DEBUG
53 opt_name = opt_name.intern
55 opt_hash[opt_name] = val
58 o.on("-v") do opt_hash[:verbose] = true end
75 # Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY
77 # ruby -run -e cp -- [OPTION] SOURCE DEST
79 # -p preserve file attributes if possible
81 # -l make hard link instead of copying (implies -r)
86 setup("prl") do |argv, options|
88 cmd += "_r" if options.delete :r
89 cmd = "cp_lr" if options.delete :l
90 options[:preserve] = true if options.delete :p
92 argv = argv[0] if argv.size == 1
93 FileUtils.__send__ cmd, argv, dest, **options
98 # Create a link to the specified TARGET with LINK_NAME.
100 # ruby -run -e ln -- [OPTION] TARGET LINK_NAME
102 # -s make symbolic links instead of hard links
103 # -f remove existing destination files
108 setup("sf") do |argv, options|
110 cmd += "_s" if options.delete :s
111 options[:force] = true if options.delete :f
113 argv = argv[0] if argv.size == 1
114 FileUtils.__send__ cmd, argv, dest, **options
119 # Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.
121 # ruby -run -e mv -- [OPTION] SOURCE DEST
127 setup do |argv, options|
129 argv = argv[0] if argv.size == 1
130 FileUtils.mv argv, dest, **options
137 # ruby -run -e rm -- [OPTION] FILE
139 # -f ignore nonexistent files
140 # -r remove the contents of directories recursively
145 setup("fr") do |argv, options|
147 cmd += "_r" if options.delete :r
148 options[:force] = true if options.delete :f
149 FileUtils.__send__ cmd, argv, **options
154 # Create the DIR, if they do not already exist.
156 # ruby -run -e mkdir -- [OPTION] DIR
158 # -p no error if existing, make parent directories as needed
163 setup("p") do |argv, options|
165 cmd += "_p" if options.delete :p
166 FileUtils.__send__ cmd, argv, **options
173 # ruby -run -e rmdir -- [OPTION] DIR
175 # -p remove DIRECTORY and its ancestors.
180 setup("p") do |argv, options|
181 options[:parents] = true if options.delete :p
182 FileUtils.rmdir argv, **options
187 # Copy SOURCE to DEST.
189 # ruby -run -e install -- [OPTION] SOURCE DEST
191 # -p apply access/modification times of SOURCE files to
192 # corresponding destination files
193 # -m set permission mode (as in chmod), instead of 0755
194 # -o set owner user id, instead of the current owner
195 # -g set owner group id, instead of the current group
200 setup("pm:o:g:") do |argv, options|
201 (mode = options.delete :m) and options[:mode] = /\A\d/ =~ mode ? mode.oct : mode
202 options[:preserve] = true if options.delete :p
203 (owner = options.delete :o) and options[:owner] = owner
204 (group = options.delete :g) and options[:group] = group
206 argv = argv[0] if argv.size == 1
207 FileUtils.install argv, dest, **options
212 # Change the mode of each FILE to OCTAL-MODE.
214 # ruby -run -e chmod -- [OPTION] OCTAL-MODE FILE
220 setup do |argv, options|
222 mode = /\A\d/ =~ mode ? mode.oct : mode
223 FileUtils.chmod mode, argv, **options
228 # Update the access and modification times of each FILE to the current time.
230 # ruby -run -e touch -- [OPTION] FILE
236 setup do |argv, options|
237 FileUtils.touch argv, **options
242 # Wait until the file becomes writable.
244 # ruby -run -e wait_writable -- [OPTION] FILE
246 # -n RETRY count to retry
247 # -w SEC each wait time in seconds
252 setup("n:w:v") do |argv, options|
253 verbose = options[:verbose]
254 n = options[:n] and n = Integer(n)
255 wait = (wait = options[:w]) ? Float(wait) : 0.2
258 File.open(file, "r+b") {}
261 rescue Errno::EACCES => e
262 raise if n and (n -= 1) <= 0
275 # Create makefile using mkmf.
277 # ruby -run -e mkmf -- [OPTION] EXTNAME [OPTION]
279 # -d ARGS run dir_config
280 # -h ARGS run have_header
281 # -l ARGS run have_library
282 # -f ARGS run have_func
283 # -v ARGS run have_var
284 # -t ARGS run have_type
285 # -m ARGS run have_macro
286 # -c ARGS run have_const
287 # --vendor install to vendor_ruby
291 setup("d:h:l:f:v:t:m:c:", "vendor") do |argv, options|
293 opt = options[:d] and opt.split(/:/).each {|n| dir_config(*n.split(/,/))}
294 opt = options[:h] and opt.split(/:/).each {|n| have_header(*n.split(/,/))}
295 opt = options[:l] and opt.split(/:/).each {|n| have_library(*n.split(/,/))}
296 opt = options[:f] and opt.split(/:/).each {|n| have_func(*n.split(/,/))}
297 opt = options[:v] and opt.split(/:/).each {|n| have_var(*n.split(/,/))}
298 opt = options[:t] and opt.split(/:/).each {|n| have_type(*n.split(/,/))}
299 opt = options[:m] and opt.split(/:/).each {|n| have_macro(*n.split(/,/))}
300 opt = options[:c] and opt.split(/:/).each {|n| have_const(*n.split(/,/))}
301 $configure_args["--vendor"] = true if options[:vendor]
302 create_makefile(*argv)
307 # Run WEBrick HTTP server.
309 # ruby -run -e httpd -- [OPTION] [DocumentRoot]
311 # --bind-address=ADDR address to bind
312 # --port=NUM listening port number
313 # --max-clients=MAX max number of simultaneous clients
314 # --temp-dir=DIR temporary directory
315 # --do-not-reverse-lookup disable reverse lookup
316 # --request-timeout=SECOND request timeout in seconds
317 # --http-version=VERSION HTTP version
318 # --server-name=NAME name of the server host
319 # --server-software=NAME name and version of the server
320 # --ssl-certificate=CERT The SSL certificate file for the server
321 # --ssl-private-key=KEY The SSL private key file for the server certificate
326 setup("", "BindAddress=ADDR", "Port=PORT", "MaxClients=NUM", "TempDir=DIR",
327 "DoNotReverseLookup", "RequestTimeout=SECOND", "HTTPVersion=VERSION",
328 "ServerName=NAME", "ServerSoftware=NAME",
329 "SSLCertificate=CERT", "SSLPrivateKey=KEY") do
334 abort "webrick is not found. You may need to `gem install webrick` to install webrick."
336 opt = options[:RequestTimeout] and options[:RequestTimeout] = opt.to_i
337 [:Port, :MaxClients].each do |name|
338 opt = options[name] and (options[name] = Integer(opt)) rescue nil
340 if cert = options[:SSLCertificate]
341 key = options[:SSLPrivateKey] or
342 raise "--ssl-private-key option must also be given"
343 require 'webrick/https'
344 options[:SSLEnable] = true
345 options[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.read(cert))
346 options[:SSLPrivateKey] = OpenSSL::PKey.read(File.read(key))
347 options[:Port] ||= 8443 # HTTPS Alternate
349 options[:Port] ||= 8080 # HTTP Alternate
350 options[:DocumentRoot] = argv.shift || '.'
352 options[:StartCallback] = proc {
354 logger.info("To access this server, open this URL in a browser:")
355 s.listeners.each do |listener|
356 if options[:SSLEnable]
358 addr[3] = "127.0.0.1" if addr[3] == "0.0.0.0"
359 addr[3] = "::1" if addr[3] == "::"
360 logger.info(" https://#{Addrinfo.new(addr).inspect_sockaddr}")
362 logger.info(" http://#{listener.connect_address.inspect_sockaddr}")
366 s = WEBrick::HTTPServer.new(options)
367 shut = proc {s.shutdown}
368 siglist = %w"TERM QUIT"
369 siglist.concat(%w"HUP INT") if STDIN.tty?
370 siglist &= Signal.list.keys
371 siglist.each do |sig|
372 Signal.trap(sig, shut)
379 # Colorize ruby code.
381 # ruby -run -e colorize -- [FILE]
388 raise "colorize requires irb 1.1.0 or later"
392 puts IRB::Color.colorize_code STDIN.read
396 puts IRB::Color.colorize_code File.read(file)
402 # Display help message.
404 # ruby -run -e help [COMMAND]
418 def help(argv, output: $stdout)
422 store = proc {|msg| output << msg}
425 store = proc {|msg| messages[cmd] = msg}
427 File.open(__FILE__) do |me|
428 while me.gets("##\n")
429 if help = me.gets("\n\n")
430 if all or argv.include?(cmd = help[/^#\s*ruby\s.*-e\s+(\w+)/, 1])
431 store[help.gsub(/^# ?/, "")]
432 break unless all or argv.size > messages.size
438 argv.each {|arg| output << messages[arg]}