diff --git a/README.md b/README.md index 7706ac5..867a56f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # cryptowallFileFinder This ruby program finds all files infected with cryptowall and puts them in a txt file for you. + +Download the whole repository with the ruby source code. Clicking start.bat will scan all local drives and place a txt in this directory. diff --git a/find_cryptowall_infected_files.rb b/find_cryptowall_infected_files.rb new file mode 100644 index 0000000..48bad9f --- /dev/null +++ b/find_cryptowall_infected_files.rb @@ -0,0 +1,89 @@ +require 'win32ole' +require 'FileUtils' + + +def infectedFiles(decryptFile) + infectedPath = File.dirname("#{decryptFile}") + infectedFiles = Dir["#{infectedPath}/*.*"] + return infectedFiles +end + +def infectedFileExpandedPath(file) + infectedFileExpandedPath = File.expand_path(file.to_s) + return infectedFileExpandedPath +end + +def decryptFiles(path) + decryptFiles = Dir["#{path}/**/DECRYPT_INSTRUCTION.TXT"] + return decryptFiles +end + + +def myFile + myFile = File.new("files.txt","a") + return myFile +end + +def openFile + openFile = File.open("files.txt","a") + return openFile +end + +def writeMyFile(whatToWrite) + if !isDecryptInstructions(whatToWrite) && !isTorInstructions(whatToWrite) + whatToWrite = infectedFileExpandedPath(whatToWrite) + whatToWrite = whatToWrite.gsub("/","\\") + openFile.puts(whatToWrite) + openFile.close + end +end + +def deleteInstructions(file) + if isTorInstructions(file) or isDecryptInstructions(file) + FileUtils.rm(File.expand_path(file.to_s)) + end +end + +def isTorInstructions(file) + if file.include? "TOR" + return true + else + return false + end +end + +def isDecryptInstructions(file) + if file.include? "DECRYPT" + return true + else + return false + end +end + +def findInfectedFiles(path) + decryptFiles(path).each do |f| + infectedFiles(f).each do |returnedFiles| + writeMyFile(returnedFiles) + deleteInstructions(returnedFiles) + end + end + +end + + +file_system = WIN32OLE.new("Scripting.FileSystemObject") +drives = file_system.Drives +drives.each do |drive| + if drive.DriveType == 2 + findInfectedFiles(drive.Path) + end +end + + + + + + + + + diff --git a/ruby/bin/erb b/ruby/bin/erb new file mode 100644 index 0000000..6a7ea7d --- /dev/null +++ b/ruby/bin/erb @@ -0,0 +1,155 @@ +#!/usr/bin/env ruby +# Tiny eRuby --- ERB2 +# Copyright (c) 1999-2000,2002 Masatoshi SEKI +# You can redistribute it and/or modify it under the same terms as Ruby. + +require 'erb' + +class ERB + module Main + def ARGV.switch + return nil if self.empty? + arg = self.shift + return nil if arg == '--' + if arg =~ /^-(.)(.*)/ + if $1 == '-' + arg, @maybe_arg = arg.split(/=/, 2) + return arg + end + raise 'unknown switch "-"' if $2[0] == ?- and $1 != 'T' + if $2.size > 0 + self.unshift "-#{$2}" + @maybe_arg = $2 + else + @maybe_arg = nil + end + "-#{$1}" + else + self.unshift arg + nil + end + end + + def ARGV.req_arg + (@maybe_arg || self.shift || raise('missing argument')).tap { + @maybe_arg = nil + } + end + + def trim_mode_opt(trim_mode, disable_percent) + return trim_mode if disable_percent + case trim_mode + when 0 + return '%' + when 1 + return '%>' + when 2 + return '%<>' + when '-' + return '%-' + end + end + module_function :trim_mode_opt + + def run(factory=ERB) + trim_mode = 0 + disable_percent = false + begin + while switch = ARGV.switch + case switch + when '-x' # ruby source + output = true + when '-n' # line number + number = true + when '-v' # verbose + $VERBOSE = true + when '--version' # version + STDERR.puts factory.version + exit + when '-d', '--debug' # debug + $DEBUG = true + when '-r' # require + require ARGV.req_arg + when '-S' # security level + arg = ARGV.req_arg + raise "invalid safe_level #{arg.dump}" unless arg =~ /^[0-3]$/ + safe_level = arg.to_i + when '-T' # trim mode + arg = ARGV.req_arg + if arg == '-' + trim_mode = arg + next + end + raise "invalid trim mode #{arg.dump}" unless arg =~ /^[0-2]$/ + trim_mode = arg.to_i + when '-E', '--encoding' + arg = ARGV.req_arg + set_encoding(*arg.split(/:/, 2)) + when '-U' + set_encoding(Encoding::UTF_8, Encoding::UTF_8) + when '-P' + disable_percent = true + when '--help' + raise "print this help" + else + raise "unknown switch #{switch.dump}" + end + end + rescue # usage + STDERR.puts $!.to_s + STDERR.puts File.basename($0) + + " [switches] [inputfile]" + STDERR.puts <= 1.8.7" + +unless required_version.satisfied_by? Gem.ruby_version then + abort "Expected Ruby Version #{required_version}, is #{Gem.ruby_version}" +end + +args = ARGV.clone + +begin + Gem::GemRunner.new.run args +rescue Gem::SystemExitException => e + exit e.exit_code +end + diff --git a/ruby/bin/gem.bat b/ruby/bin/gem.bat new file mode 100644 index 0000000..ed73662 --- /dev/null +++ b/ruby/bin/gem.bat @@ -0,0 +1,6 @@ +@ECHO OFF +IF NOT "%~f0" == "~f0" GOTO :WinNT +ECHO.This version of Ruby has not been built with support for Windows 95/98/Me. +GOTO :EOF +:WinNT +@"%~dp0ruby.exe" "%~dpn0" %* diff --git a/ruby/bin/irb b/ruby/bin/irb new file mode 100644 index 0000000..1718d07 --- /dev/null +++ b/ruby/bin/irb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +# +# irb.rb - interactive ruby +# $Release Version: 0.9.6 $ +# $Revision: 40560 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# + +require "irb" + +IRB.start(__FILE__) diff --git a/ruby/bin/irb.bat b/ruby/bin/irb.bat new file mode 100644 index 0000000..ed73662 --- /dev/null +++ b/ruby/bin/irb.bat @@ -0,0 +1,6 @@ +@ECHO OFF +IF NOT "%~f0" == "~f0" GOTO :WinNT +ECHO.This version of Ruby has not been built with support for Windows 95/98/Me. +GOTO :EOF +:WinNT +@"%~dp0ruby.exe" "%~dpn0" %* diff --git a/ruby/bin/libeay32.dll b/ruby/bin/libeay32.dll new file mode 100644 index 0000000..1afaa2c Binary files /dev/null and b/ruby/bin/libeay32.dll differ diff --git a/ruby/bin/libffi-6.dll b/ruby/bin/libffi-6.dll new file mode 100644 index 0000000..40d9ce6 Binary files /dev/null and b/ruby/bin/libffi-6.dll differ diff --git a/ruby/bin/libgdbm-3.dll b/ruby/bin/libgdbm-3.dll new file mode 100644 index 0000000..cd2260d Binary files /dev/null and b/ruby/bin/libgdbm-3.dll differ diff --git a/ruby/bin/libgdbm_compat-3.dll b/ruby/bin/libgdbm_compat-3.dll new file mode 100644 index 0000000..8236707 Binary files /dev/null and b/ruby/bin/libgdbm_compat-3.dll differ diff --git a/ruby/bin/libiconv-2.dll b/ruby/bin/libiconv-2.dll new file mode 100644 index 0000000..620806c Binary files /dev/null and b/ruby/bin/libiconv-2.dll differ diff --git a/ruby/bin/libyaml-0-2.dll b/ruby/bin/libyaml-0-2.dll new file mode 100644 index 0000000..c6fbcf1 Binary files /dev/null and b/ruby/bin/libyaml-0-2.dll differ diff --git a/ruby/bin/msvcrt-ruby210.dll b/ruby/bin/msvcrt-ruby210.dll new file mode 100644 index 0000000..b90dbee Binary files /dev/null and b/ruby/bin/msvcrt-ruby210.dll differ diff --git a/ruby/bin/rake b/ruby/bin/rake new file mode 100644 index 0000000..4e0bbb7 --- /dev/null +++ b/ruby/bin/rake @@ -0,0 +1,33 @@ +#!/usr/bin/env ruby + +#-- +# Copyright (c) 2003, 2004, 2005, 2006, 2007 Jim Weirich +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +#++ + +begin + require 'rubygems' + gem 'rake' +rescue LoadError +end + +require 'rake' + +Rake.application.run diff --git a/ruby/bin/rake.bat b/ruby/bin/rake.bat new file mode 100644 index 0000000..ed73662 --- /dev/null +++ b/ruby/bin/rake.bat @@ -0,0 +1,6 @@ +@ECHO OFF +IF NOT "%~f0" == "~f0" GOTO :WinNT +ECHO.This version of Ruby has not been built with support for Windows 95/98/Me. +GOTO :EOF +:WinNT +@"%~dp0ruby.exe" "%~dpn0" %* diff --git a/ruby/bin/rdoc b/ruby/bin/rdoc new file mode 100644 index 0000000..aaa2329 --- /dev/null +++ b/ruby/bin/rdoc @@ -0,0 +1,44 @@ +#!/usr/bin/env ruby +# +# RDoc: Documentation tool for source code +# (see lib/rdoc/rdoc.rb for more information) +# +# Copyright (c) 2003 Dave Thomas +# Released under the same terms as Ruby + +begin + gem 'rdoc' +rescue NameError => e # --disable-gems + raise unless e.name == :gem +rescue Gem::LoadError +end + +require 'rdoc/rdoc' + +begin + r = RDoc::RDoc.new + r.document ARGV +rescue Errno::ENOSPC + $stderr.puts 'Ran out of space creating documentation' + $stderr.puts + $stderr.puts 'Please free up some space and try again' +rescue SystemExit + raise +rescue Exception => e + if $DEBUG_RDOC then + $stderr.puts e.message + $stderr.puts "#{e.backtrace.join "\n\t"}" + $stderr.puts + elsif Interrupt === e then + $stderr.puts + $stderr.puts 'Interrupted' + else + $stderr.puts "uh-oh! RDoc had a problem:" + $stderr.puts e.message + $stderr.puts + $stderr.puts "run with --debug for full backtrace" + end + + exit 1 +end + diff --git a/ruby/bin/rdoc.bat b/ruby/bin/rdoc.bat new file mode 100644 index 0000000..ed73662 --- /dev/null +++ b/ruby/bin/rdoc.bat @@ -0,0 +1,6 @@ +@ECHO OFF +IF NOT "%~f0" == "~f0" GOTO :WinNT +ECHO.This version of Ruby has not been built with support for Windows 95/98/Me. +GOTO :EOF +:WinNT +@"%~dp0ruby.exe" "%~dpn0" %* diff --git a/ruby/bin/ri b/ruby/bin/ri new file mode 100644 index 0000000..7fbed0c --- /dev/null +++ b/ruby/bin/ri @@ -0,0 +1,12 @@ +#!/usr/bin/env ruby + +begin + gem 'rdoc' +rescue NameError => e # --disable-gems + raise unless e.name == :gem +rescue Gem::LoadError +end + +require 'rdoc/ri/driver' + +RDoc::RI::Driver.run ARGV diff --git a/ruby/bin/ri.bat b/ruby/bin/ri.bat new file mode 100644 index 0000000..ed73662 --- /dev/null +++ b/ruby/bin/ri.bat @@ -0,0 +1,6 @@ +@ECHO OFF +IF NOT "%~f0" == "~f0" GOTO :WinNT +ECHO.This version of Ruby has not been built with support for Windows 95/98/Me. +GOTO :EOF +:WinNT +@"%~dp0ruby.exe" "%~dpn0" %* diff --git a/ruby/bin/ruby.exe b/ruby/bin/ruby.exe new file mode 100644 index 0000000..571753a Binary files /dev/null and b/ruby/bin/ruby.exe differ diff --git a/ruby/bin/rubyw.exe b/ruby/bin/rubyw.exe new file mode 100644 index 0000000..63bc279 Binary files /dev/null and b/ruby/bin/rubyw.exe differ diff --git a/ruby/bin/ssleay32.dll b/ruby/bin/ssleay32.dll new file mode 100644 index 0000000..28a7f81 Binary files /dev/null and b/ruby/bin/ssleay32.dll differ diff --git a/ruby/bin/tcl85.dll b/ruby/bin/tcl85.dll new file mode 100644 index 0000000..55ddb78 Binary files /dev/null and b/ruby/bin/tcl85.dll differ diff --git a/ruby/bin/tclpip85.dll b/ruby/bin/tclpip85.dll new file mode 100644 index 0000000..6a74d02 Binary files /dev/null and b/ruby/bin/tclpip85.dll differ diff --git a/ruby/bin/testrb b/ruby/bin/testrb new file mode 100644 index 0000000..23a00b4 --- /dev/null +++ b/ruby/bin/testrb @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require 'test/unit' +exit Test::Unit::AutoRunner.run(true) diff --git a/ruby/bin/testrb.bat b/ruby/bin/testrb.bat new file mode 100644 index 0000000..ed73662 --- /dev/null +++ b/ruby/bin/testrb.bat @@ -0,0 +1,6 @@ +@ECHO OFF +IF NOT "%~f0" == "~f0" GOTO :WinNT +ECHO.This version of Ruby has not been built with support for Windows 95/98/Me. +GOTO :EOF +:WinNT +@"%~dp0ruby.exe" "%~dpn0" %* diff --git a/ruby/bin/tk85.dll b/ruby/bin/tk85.dll new file mode 100644 index 0000000..49a4e94 Binary files /dev/null and b/ruby/bin/tk85.dll differ diff --git a/ruby/bin/zlib1.dll b/ruby/bin/zlib1.dll new file mode 100644 index 0000000..cd7c762 Binary files /dev/null and b/ruby/bin/zlib1.dll differ diff --git a/ruby/include/ruby-2.1.0/i386-mingw32/ruby/config.h b/ruby/include/ruby-2.1.0/i386-mingw32/ruby/config.h new file mode 100644 index 0000000..38577b4 --- /dev/null +++ b/ruby/include/ruby-2.1.0/i386-mingw32/ruby/config.h @@ -0,0 +1,232 @@ +#ifndef INCLUDE_RUBY_CONFIG_H +#define INCLUDE_RUBY_CONFIG_H 1 +/* confdefs.h */ +#define CANONICALIZATION_FOR_MATHN 1 +#define RUBY_MSVCRT_VERSION 60 +#define STDC_HEADERS 1 +#define HAVE_SYS_TYPES_H 1 +#define HAVE_SYS_STAT_H 1 +#define HAVE_STDLIB_H 1 +#define HAVE_STRING_H 1 +#define HAVE_MEMORY_H 1 +#define HAVE_STRINGS_H 1 +#define HAVE_INTTYPES_H 1 +#define HAVE_STDINT_H 1 +#define HAVE_UNISTD_H 1 +#define __EXTENSIONS__ 1 +#define _ALL_SOURCE 1 +#define _GNU_SOURCE 1 +#define _POSIX_PTHREAD_SEMANTICS 1 +#define _TANDEM_SOURCE 1 +#define RUBY_SYMBOL_EXPORT_BEGIN _Pragma("GCC visibility push(default)") +#define RUBY_SYMBOL_EXPORT_END _Pragma("GCC visibility pop") +#define HAVE_TYPE_NET_LUID 1 +#define HAVE__GMTIME64_S 1 +#define HAVE_DIRENT_H 1 +#define HAVE__BOOL 1 +#define HAVE_STDBOOL_H 1 +#define HAVE_LIMITS_H 1 +#define HAVE_SYS_FILE_H 1 +#define HAVE_FCNTL_H 1 +#define HAVE_SYS_FCNTL_H 1 +#define HAVE_DIRECT_H 1 +#define HAVE_SYS_UTIME_H 1 +#define HAVE_FLOAT_H 1 +#define HAVE_LOCALE_H 1 +#define HAVE_TIME_H 1 +#define HAVE_PROCESS_H 1 +#define HAVE_MALLOC_H 1 +#define HAVE_SETJMPEX_H 1 +#define _FILE_OFFSET_BITS 64 +#define HAVE_LONG_LONG 1 +#define HAVE_OFF_T 1 +#define SIZEOF_INT 4 +#define SIZEOF_SHORT 2 +#define SIZEOF_LONG 4 +#define SIZEOF_LONG_LONG 8 +#define SIZEOF___INT64 8 +#define SIZEOF___INT128 0 +#define SIZEOF_OFF_T 8 +#define SIZEOF_VOIDP 4 +#define SIZEOF_FLOAT 4 +#define SIZEOF_DOUBLE 8 +#define SIZEOF_TIME_T 8 +#define SIZEOF_CLOCK_T 4 +#define PRI_LL_PREFIX "I64" +#define rb_pid_t pid_t +#define SIGNEDNESS_OF_PID_T -1 +#define PIDT2NUM(v) INT2NUM(v) +#define NUM2PIDT(v) NUM2INT(v) +#define PRI_PIDT_PREFIX PRI_INT_PREFIX +#define rb_uid_t int +#define SIGNEDNESS_OF_UID_T -1 +#define UIDT2NUM(v) INT2NUM(v) +#define NUM2UIDT(v) NUM2INT(v) +#define PRI_UIDT_PREFIX PRI_INT_PREFIX +#define rb_gid_t int +#define SIGNEDNESS_OF_GID_T -1 +#define GIDT2NUM(v) INT2NUM(v) +#define NUM2GIDT(v) NUM2INT(v) +#define PRI_GIDT_PREFIX PRI_INT_PREFIX +#define rb_time_t time_t +#define SIGNEDNESS_OF_TIME_T -1 +#define TIMET2NUM(v) LL2NUM(v) +#define NUM2TIMET(v) NUM2LL(v) +#define PRI_TIMET_PREFIX PRI_LL_PREFIX +#define rb_dev_t dev_t +#define SIGNEDNESS_OF_DEV_T +1 +#define DEVT2NUM(v) UINT2NUM(v) +#define NUM2DEVT(v) NUM2UINT(v) +#define PRI_DEVT_PREFIX PRI_INT_PREFIX +#define rb_mode_t mode_t +#define SIGNEDNESS_OF_MODE_T +1 +#define MODET2NUM(v) UINT2NUM(v) +#define NUM2MODET(v) NUM2UINT(v) +#define PRI_MODET_PREFIX PRI_INT_PREFIX +#define rb_rlim_t int long "long long" +#define SIGNEDNESS_OF_RLIM_T -1 +#define RLIM2NUM(v) LONG2NUM(v) +#define NUM2RLIM(v) NUM2LONG(v) +#define PRI_RLIM_PREFIX PRI_LONG_PREFIX +#define rb_off_t off_t +#define SIGNEDNESS_OF_OFF_T -1 +#define OFFT2NUM(v) LL2NUM(v) +#define NUM2OFFT(v) NUM2LL(v) +#define PRI_OFFT_PREFIX PRI_LL_PREFIX +#define rb_clockid_t int +#define SIGNEDNESS_OF_CLOCKID_T -1 +#define CLOCKID2NUM(v) INT2NUM(v) +#define NUM2CLOCKID(v) NUM2INT(v) +#define PRI_CLOCKID_PREFIX PRI_INT_PREFIX +#define HAVE_PROTOTYPES 1 +#define TOKEN_PASTE(x,y) x##y +#define STRINGIZE(expr) STRINGIZE0(expr) +#define HAVE_STDARG_PROTOTYPES 1 +#define HAVE_VA_ARGS_MACRO 1 +#define NORETURN(x) __attribute__ ((noreturn)) x +#define DEPRECATED(x) __attribute__ ((deprecated)) x +#define NOINLINE(x) __attribute__ ((noinline)) x +#define FUNC_STDCALL(x) __attribute__ ((stdcall)) x +#define FUNC_CDECL(x) __attribute__ ((cdecl)) x +#define FUNC_FASTCALL(x) __attribute__ ((fastcall)) x +#define HAVE_ATTRIBUTE_FUNCTION_ALIAS 1 +#define RUBY_ALIAS_FUNCTION_TYPE(type, prot, name, args) type prot __attribute__((alias(#name))); +#define RUBY_ALIAS_FUNCTION_VOID(prot, name, args) RUBY_ALIAS_FUNCTION_TYPE(void, prot, name, args) +#define HAVE_GCC_ATOMIC_BUILTINS 1 +#define HAVE_GCC_SYNC_BUILTINS 1 +#define UNREACHABLE __builtin_unreachable() +#define RUBY_FUNC_EXPORTED __attribute__ ((visibility("default"))) extern +#define RUBY_FUNCTION_NAME_STRING __func__ +#define HAVE_DECL_SYS_NERR 1 +#define HAVE_DECL_GETENV 1 +#define SIZEOF_SIZE_T 4 +#define SIZEOF_PTRDIFF_T 4 +#define HAVE_STRUCT_STAT_ST_RDEV 1 +#define HAVE_ST_RDEV 1 +#define SIZEOF_STRUCT_STAT_ST_SIZE SIZEOF_LONG_LONG +#define HAVE_STRUCT_TIMEVAL 1 +#define SIZEOF_STRUCT_TIMEVAL_TV_SEC SIZEOF_LONG +#define TYPEOF_TIMEVAL_TV_SEC long +#define HAVE_STRUCT_TIMESPEC 1 +#define HAVE_STRUCT_TIMEZONE 1 +#define HAVE_RB_FD_INIT 1 +#define HAVE_INT8_T 1 +#define SIZEOF_INT8_T 1 +#define HAVE_UINT8_T 1 +#define SIZEOF_UINT8_T 1 +#define HAVE_INT16_T 1 +#define SIZEOF_INT16_T 2 +#define HAVE_UINT16_T 1 +#define SIZEOF_UINT16_T 2 +#define HAVE_INT32_T 1 +#define SIZEOF_INT32_T 4 +#define HAVE_UINT32_T 1 +#define SIZEOF_UINT32_T 4 +#define HAVE_INT64_T 1 +#define SIZEOF_INT64_T 8 +#define HAVE_UINT64_T 1 +#define SIZEOF_UINT64_T 8 +#define HAVE_INTPTR_T 1 +#define SIZEOF_INTPTR_T 4 +#define HAVE_UINTPTR_T 1 +#define SIZEOF_UINTPTR_T 4 +#define HAVE_SSIZE_T 1 +#define SIZEOF_SSIZE_T 4 +#define uid_t int +#define gid_t int +#define GETGROUPS_T int +#define RETSIGTYPE void +#define HAVE_ALLOCA 1 +#define HAVE_DUP2 1 +#define HAVE_MEMMOVE 1 +#define HAVE_STRERROR 1 +#define HAVE_STRCHR 1 +#define HAVE_STRSTR 1 +#define HAVE_FLOCK 1 +#define HAVE_ISNAN 1 +#define HAVE_FINITE 1 +#define HAVE_ISINF 1 +#define HAVE_HYPOT 1 +#define HAVE_ACOSH 1 +#define HAVE_ERF 1 +#define HAVE_TGAMMA 1 +#define HAVE_CBRT 1 +#define HAVE_SIGNBIT 1 +#define HAVE__SETJMP 1 +#define HAVE_CHSIZE 1 +#define HAVE_CLOCK_GETTIME 1 +#define HAVE_COSH 1 +#define HAVE_DUP 1 +#define HAVE_FCNTL 1 +#define HAVE_FMOD 1 +#define HAVE_FSYNC 1 +#define HAVE_FTRUNCATE 1 +#define HAVE_FTRUNCATE64 1 +#define HAVE_GETCWD 1 +#define HAVE_GETTIMEOFDAY 1 +#define HAVE_GMTIME_R 1 +#define HAVE_LINK 1 +#define HAVE_LLABS 1 +#define HAVE_LOG2 1 +#define HAVE_MBLEN 1 +#define HAVE_MKTIME 1 +#define HAVE_ROUND 1 +#define HAVE_SEEKDIR 1 +#define HAVE_SINH 1 +#define HAVE_SPAWNV 1 +#define HAVE_TANH 1 +#define HAVE_TELLDIR 1 +#define HAVE_TIMES 1 +#define HAVE_TRUNCATE 1 +#define HAVE_TRUNCATE64 1 +#define HAVE_WAITPID 1 +#define HAVE_BUILTIN___BUILTIN_BSWAP32 1 +#define HAVE_BUILTIN___BUILTIN_BSWAP64 1 +#define HAVE_BUILTIN___BUILTIN_CLZ 1 +#define HAVE_BUILTIN___BUILTIN_CLZL 1 +#define HAVE_BUILTIN___BUILTIN_CLZLL 1 +#define HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR 1 +#define HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P 1 +#define HAVE_CLOCK_GETRES 1 +#define HAVE_DECL_TZNAME 1 +#define HAVE_TZNAME 1 +#define HAVE_DAYLIGHT 1 +#define HAVE_VAR_TIMEZONE 1 +#define TYPEOF_VAR_TIMEZONE long +#define HAVE_TIMEZONE 1 +#define TIMEZONE_VOID 1 +#define RSHIFT(x,y) ((x)>>(int)(y)) +#define FILE_COUNT _cnt +#define FILE_READPTR _ptr +#define SIZEOF_STRUCT_STAT_ST_INO 2 +#define STACK_GROW_DIRECTION -1 +#define DLEXT_MAXLEN 3 +#define DLEXT ".so" +#define LIBDIR_BASENAME "lib" +#define EXECUTABLE_EXTS ".exe",".com",".cmd",".bat" +#define RUBY_SETJMP(env) __builtin_setjmp((env)) +#define RUBY_LONGJMP(env,val) __builtin_longjmp((env),val) +#define RUBY_JMP_BUF jmp_buf +#define LOAD_RELATIVE 1 +#define RUBY_PLATFORM "i386-mingw32" +#endif /* INCLUDE_RUBY_CONFIG_H */ diff --git a/ruby/include/ruby-2.1.0/ruby.h b/ruby/include/ruby-2.1.0/ruby.h new file mode 100644 index 0000000..076b5ce --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby.h @@ -0,0 +1,35 @@ +/********************************************************************** + + ruby.h - + + $Author$ + created at: Sun 10 12:06:15 Jun JST 2007 + + Copyright (C) 2007-2008 Yukihiro Matsumoto + +**********************************************************************/ + +#ifndef RUBY_H +#define RUBY_H 1 + +#define HAVE_RUBY_DEFINES_H 1 +#define HAVE_RUBY_ENCODING_H 1 +#define HAVE_RUBY_INTERN_H 1 +#define HAVE_RUBY_IO_H 1 +#define HAVE_RUBY_MISSING_H 1 +#define HAVE_RUBY_ONIGURUMA_H 1 +#define HAVE_RUBY_RE_H 1 +#define HAVE_RUBY_REGEX_H 1 +#define HAVE_RUBY_RUBY_H 1 +#define HAVE_RUBY_ST_H 1 +#define HAVE_RUBY_THREAD_H 1 +#define HAVE_RUBY_UTIL_H 1 +#define HAVE_RUBY_VERSION_H 1 +#define HAVE_RUBY_VM_H 1 +#ifdef _WIN32 +#define HAVE_RUBY_WIN32_H 1 +#endif + +#include "ruby/ruby.h" + +#endif /* RUBY_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/backward/classext.h b/ruby/include/ruby-2.1.0/ruby/backward/classext.h new file mode 100644 index 0000000..33f3b01 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/backward/classext.h @@ -0,0 +1,18 @@ +#if defined __GNUC__ +#warning use of RClass internals is deprecated +#elif defined _MSC_VER || defined __BORLANDC__ +#pragma message("warning: use of RClass internals is deprecated") +#endif + +#ifndef RUBY_BACKWARD_CLASSEXT_H +#define RUBY_BACKWARD_CLASSEXT_H 1 + +typedef struct rb_deprecated_classext_struct { + VALUE super; +} rb_deprecated_classext_t; + +#undef RCLASS_SUPER(c) +#define RCLASS_EXT(c) ((rb_deprecated_classext_t *)RCLASS(c)->ptr) +#define RCLASS_SUPER(c) (RCLASS(c)->super) + +#endif /* RUBY_BACKWARD_CLASSEXT_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/backward/rubyio.h b/ruby/include/ruby-2.1.0/ruby/backward/rubyio.h new file mode 100644 index 0000000..d5246db --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/backward/rubyio.h @@ -0,0 +1,6 @@ +#if defined __GNUC__ +#warning use "ruby/io.h" instead of "rubyio.h" +#elif defined _MSC_VER || defined __BORLANDC__ +#pragma message("warning: use \"ruby/io.h\" instead of \"rubyio.h\"") +#endif +#include "ruby/io.h" diff --git a/ruby/include/ruby-2.1.0/ruby/backward/rubysig.h b/ruby/include/ruby-2.1.0/ruby/backward/rubysig.h new file mode 100644 index 0000000..bb32a2e --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/backward/rubysig.h @@ -0,0 +1,52 @@ +/********************************************************************** + + rubysig.h - + + $Author: nobu $ + $Date: 2013-04-05 19:29:38 +0900 (Fri, 05 Apr 2013) $ + created at: Wed Aug 16 01:15:38 JST 1995 + + Copyright (C) 1993-2008 Yukihiro Matsumoto + +**********************************************************************/ + +#if defined __GNUC__ +#warning rubysig.h is obsolete +#elif defined _MSC_VER || defined __BORLANDC__ +#pragma message("warning: rubysig.h is obsolete") +#endif + +#ifndef RUBYSIG_H +#define RUBYSIG_H +#include "ruby/ruby.h" + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +struct rb_blocking_region_buffer; +DEPRECATED(RUBY_EXTERN struct rb_blocking_region_buffer *rb_thread_blocking_region_begin(void)); +DEPRECATED(RUBY_EXTERN void rb_thread_blocking_region_end(struct rb_blocking_region_buffer *)); +#define TRAP_BEG do {struct rb_blocking_region_buffer *__region = rb_thread_blocking_region_begin(); +#define TRAP_END rb_thread_blocking_region_end(__region);} while (0) +#define RUBY_CRITICAL(statements) do {statements;} while (0) +#define DEFER_INTS (0) +#define ENABLE_INTS (1) +#define ALLOW_INTS do {CHECK_INTS;} while (0) +#define CHECK_INTS rb_thread_check_ints() + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif diff --git a/ruby/include/ruby-2.1.0/ruby/backward/st.h b/ruby/include/ruby-2.1.0/ruby/backward/st.h new file mode 100644 index 0000000..514128e --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/backward/st.h @@ -0,0 +1,6 @@ +#if defined __GNUC__ +#warning use "ruby/st.h" instead of bare "st.h" +#elif defined _MSC_VER || defined __BORLANDC__ +#pragma message("warning: use \"ruby/st.h\" instead of bare \"st.h\"") +#endif +#include "ruby/st.h" diff --git a/ruby/include/ruby-2.1.0/ruby/backward/util.h b/ruby/include/ruby-2.1.0/ruby/backward/util.h new file mode 100644 index 0000000..6b47940 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/backward/util.h @@ -0,0 +1,6 @@ +#if defined __GNUC__ +#warning use "ruby/util.h" instead of bare "util.h" +#elif defined _MSC_VER || defined __BORLANDC__ +#pragma message("warning: use \"ruby/util.h\" instead of bare \"util.h\"") +#endif +#include "ruby/util.h" diff --git a/ruby/include/ruby-2.1.0/ruby/debug.h b/ruby/include/ruby-2.1.0/ruby/debug.h new file mode 100644 index 0000000..9bfc9b9 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/debug.h @@ -0,0 +1,110 @@ +/********************************************************************** + + ruby/debug.h - + + $Author: ko1 $ + created at: Tue Nov 20 20:35:08 2012 + + Copyright (C) 2012 Yukihiro Matsumoto + +**********************************************************************/ + +#ifndef RB_DEBUG_H +#define RB_DEBUG_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +/* Note: This file contains experimental APIs. */ +/* APIs can be replaced at Ruby 2.0.1 or later */ + + +/* profile frames APIs */ +int rb_profile_frames(int start, int limit, VALUE *buff, int *lines); +VALUE rb_profile_frame_path(VALUE frame); +VALUE rb_profile_frame_absolute_path(VALUE frame); +VALUE rb_profile_frame_label(VALUE frame); +VALUE rb_profile_frame_base_label(VALUE frame); +VALUE rb_profile_frame_full_label(VALUE frame); +VALUE rb_profile_frame_first_lineno(VALUE frame); +VALUE rb_profile_frame_classpath(VALUE frame); +VALUE rb_profile_frame_singleton_method_p(VALUE frame); +VALUE rb_profile_frame_method_name(VALUE frame); +VALUE rb_profile_frame_qualified_method_name(VALUE frame); + +/* debug inspector APIs */ +typedef struct rb_debug_inspector_struct rb_debug_inspector_t; +typedef VALUE (*rb_debug_inspector_func_t)(const rb_debug_inspector_t *, void *); + +VALUE rb_debug_inspector_open(rb_debug_inspector_func_t func, void *data); +VALUE rb_debug_inspector_frame_self_get(const rb_debug_inspector_t *dc, long index); +VALUE rb_debug_inspector_frame_class_get(const rb_debug_inspector_t *dc, long index); +VALUE rb_debug_inspector_frame_binding_get(const rb_debug_inspector_t *dc, long index); +VALUE rb_debug_inspector_frame_iseq_get(const rb_debug_inspector_t *dc, long index); +VALUE rb_debug_inspector_backtrace_locations(const rb_debug_inspector_t *dc); + +/* Old style set_trace_func APIs */ + +/* duplicated def of include/ruby/ruby.h */ +void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); +int rb_remove_event_hook(rb_event_hook_func_t func); + +int rb_remove_event_hook_with_data(rb_event_hook_func_t func, VALUE data); +void rb_thread_add_event_hook(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); +int rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func); +int rb_thread_remove_event_hook_with_data(VALUE thval, rb_event_hook_func_t func, VALUE data); + +/* TracePoint APIs */ + +VALUE rb_tracepoint_new(VALUE target_thread_not_supported_yet, rb_event_flag_t events, void (*func)(VALUE, void *), void *data); +VALUE rb_tracepoint_enable(VALUE tpval); +VALUE rb_tracepoint_disable(VALUE tpval); +VALUE rb_tracepoint_enabled_p(VALUE tpval); + +typedef struct rb_trace_arg_struct rb_trace_arg_t; +rb_trace_arg_t *rb_tracearg_from_tracepoint(VALUE tpval); + +rb_event_flag_t rb_tracearg_event_flag(rb_trace_arg_t *trace_arg); +VALUE rb_tracearg_event(rb_trace_arg_t *trace_arg); +VALUE rb_tracearg_lineno(rb_trace_arg_t *trace_arg); +VALUE rb_tracearg_path(rb_trace_arg_t *trace_arg); +VALUE rb_tracearg_method_id(rb_trace_arg_t *trace_arg); +VALUE rb_tracearg_defined_class(rb_trace_arg_t *trace_arg); +VALUE rb_tracearg_binding(rb_trace_arg_t *trace_arg); +VALUE rb_tracearg_self(rb_trace_arg_t *trace_arg); +VALUE rb_tracearg_return_value(rb_trace_arg_t *trace_arg); +VALUE rb_tracearg_raised_exception(rb_trace_arg_t *trace_arg); +VALUE rb_tracearg_object(rb_trace_arg_t *trace_arg); + +/* Postponed Job API */ +typedef void (*rb_postponed_job_func_t)(void *arg); +int rb_postponed_job_register(unsigned int flags, rb_postponed_job_func_t func, void *data); +int rb_postponed_job_register_one(unsigned int flags, rb_postponed_job_func_t func, void *data); + +/* undocumented advanced tracing APIs */ + +typedef enum { + RUBY_EVENT_HOOK_FLAG_SAFE = 0x01, + RUBY_EVENT_HOOK_FLAG_DELETED = 0x02, + RUBY_EVENT_HOOK_FLAG_RAW_ARG = 0x04 +} rb_event_hook_flag_t; + +void rb_add_event_hook2(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flag); +void rb_thread_add_event_hook2(VALUE thval, rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flag); + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_DEBUG_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/defines.h b/ruby/include/ruby-2.1.0/ruby/defines.h new file mode 100644 index 0000000..0e6b23c --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/defines.h @@ -0,0 +1,324 @@ +/************************************************ + + defines.h - + + $Author: akr $ + created at: Wed May 18 00:21:44 JST 1994 + +************************************************/ + +#ifndef RUBY_DEFINES_H +#define RUBY_DEFINES_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#include "ruby/config.h" +#ifdef RUBY_EXTCONF_H +#include RUBY_EXTCONF_H +#endif + +/* AC_INCLUDES_DEFAULT */ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#ifdef HAVE_SYS_SELECT_H +# include +#endif + +#if defined HAVE_SETJMPEX_H && defined HAVE__SETJMPEX +#include +#endif + +#include "ruby/missing.h" + +#define RUBY + +#ifdef __cplusplus +# ifndef HAVE_PROTOTYPES +# define HAVE_PROTOTYPES 1 +# endif +# ifndef HAVE_STDARG_PROTOTYPES +# define HAVE_STDARG_PROTOTYPES 1 +# endif +#endif + +#undef _ +#ifdef HAVE_PROTOTYPES +# define _(args) args +#else +# define _(args) () +#endif + +#undef __ +#ifdef HAVE_STDARG_PROTOTYPES +# define __(args) args +#else +# define __(args) () +#endif + +#ifdef __cplusplus +#define ANYARGS ... +#else +#define ANYARGS +#endif + +#ifndef RUBY_SYMBOL_EXPORT_BEGIN +# define RUBY_SYMBOL_EXPORT_BEGIN /* begin */ +# define RUBY_SYMBOL_EXPORT_END /* end */ +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +#define xmalloc ruby_xmalloc +#define xmalloc2 ruby_xmalloc2 +#define xcalloc ruby_xcalloc +#define xrealloc ruby_xrealloc +#define xrealloc2 ruby_xrealloc2 +#define xfree ruby_xfree + +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 +# define RUBY_ATTR_ALLOC_SIZE(params) __attribute__ ((__alloc_size__ params)) +#else +# define RUBY_ATTR_ALLOC_SIZE(params) +#endif + +void *xmalloc(size_t) RUBY_ATTR_ALLOC_SIZE((1)); +void *xmalloc2(size_t,size_t) RUBY_ATTR_ALLOC_SIZE((1,2)); +void *xcalloc(size_t,size_t) RUBY_ATTR_ALLOC_SIZE((1,2)); +void *xrealloc(void*,size_t) RUBY_ATTR_ALLOC_SIZE((2)); +void *xrealloc2(void*,size_t,size_t) RUBY_ATTR_ALLOC_SIZE((2,3)); +void xfree(void*); + +#define STRINGIZE(expr) STRINGIZE0(expr) +#ifndef STRINGIZE0 +#define STRINGIZE0(expr) #expr +#endif + +#ifdef HAVE_LONG_LONG +# define HAVE_TRUE_LONG_LONG 1 +#endif + +#if SIZEOF_LONG_LONG > 0 +# define LONG_LONG long long +#elif SIZEOF___INT64 > 0 +# define HAVE_LONG_LONG 1 +# define LONG_LONG __int64 +# undef SIZEOF_LONG_LONG +# define SIZEOF_LONG_LONG SIZEOF___INT64 +#endif + +#ifndef BDIGIT +# if SIZEOF_INT*2 <= SIZEOF_LONG_LONG +# define BDIGIT unsigned int +# define SIZEOF_BDIGITS SIZEOF_INT +# define BDIGIT_DBL unsigned LONG_LONG +# define BDIGIT_DBL_SIGNED LONG_LONG +# define PRI_BDIGIT_PREFIX "" +# define PRI_BDIGIT_DBL_PREFIX PRI_LL_PREFIX +# elif SIZEOF_INT*2 <= SIZEOF_LONG +# define BDIGIT unsigned int +# define SIZEOF_BDIGITS SIZEOF_INT +# define BDIGIT_DBL unsigned long +# define BDIGIT_DBL_SIGNED long +# define PRI_BDIGIT_PREFIX "" +# define PRI_BDIGIT_DBL_PREFIX "l" +# elif SIZEOF_SHORT*2 <= SIZEOF_LONG +# define BDIGIT unsigned short +# define SIZEOF_BDIGITS SIZEOF_SHORT +# define BDIGIT_DBL unsigned long +# define BDIGIT_DBL_SIGNED long +# define PRI_BDIGIT_PREFIX "h" +# define PRI_BDIGIT_DBL_PREFIX "l" +# else +# define BDIGIT unsigned short +# define SIZEOF_BDIGITS (SIZEOF_LONG/2) +# define SIZEOF_ACTUAL_BDIGIT SIZEOF_LONG +# define BDIGIT_DBL unsigned long +# define BDIGIT_DBL_SIGNED long +# define PRI_BDIGIT_PREFIX "h" +# define PRI_BDIGIT_DBL_PREFIX "l" +# endif +#endif +#ifndef SIZEOF_ACTUAL_BDIGIT +# define SIZEOF_ACTUAL_BDIGIT SIZEOF_BDIGITS +#endif + +#ifdef PRI_BDIGIT_PREFIX +# define PRIdBDIGIT PRI_BDIGIT_PREFIX"d" +# define PRIiBDIGIT PRI_BDIGIT_PREFIX"i" +# define PRIoBDIGIT PRI_BDIGIT_PREFIX"o" +# define PRIuBDIGIT PRI_BDIGIT_PREFIX"u" +# define PRIxBDIGIT PRI_BDIGIT_PREFIX"x" +# define PRIXBDIGIT PRI_BDIGIT_PREFIX"X" +#endif + +#ifdef PRI_BDIGIT_DBL_PREFIX +# define PRIdBDIGIT_DBL PRI_BDIGIT_DBL_PREFIX"d" +# define PRIiBDIGIT_DBL PRI_BDIGIT_DBL_PREFIX"i" +# define PRIoBDIGIT_DBL PRI_BDIGIT_DBL_PREFIX"o" +# define PRIuBDIGIT_DBL PRI_BDIGIT_DBL_PREFIX"u" +# define PRIxBDIGIT_DBL PRI_BDIGIT_DBL_PREFIX"x" +# define PRIXBDIGIT_DBL PRI_BDIGIT_DBL_PREFIX"X" +#endif + +#ifdef __CYGWIN__ +#undef _WIN32 +#endif + +#if defined(_WIN32) || defined(__EMX__) +#define DOSISH 1 +# define DOSISH_DRIVE_LETTER +#endif + +#ifdef AC_APPLE_UNIVERSAL_BUILD +#undef WORDS_BIGENDIAN +#ifdef __BIG_ENDIAN__ +#define WORDS_BIGENDIAN +#endif +#endif + +#ifdef _WIN32 +#include "ruby/win32.h" +#endif + +#if defined(__BEOS__) && !defined(__HAIKU__) && !defined(BONE) +#include /* intern.h needs fd_set definition */ +#endif + +#ifdef __SYMBIAN32__ +# define FALSE 0 +# define TRUE 1 +#endif + +#ifdef RUBY_EXPORT +#undef RUBY_EXTERN + +#ifndef FALSE +# define FALSE 0 +#elif FALSE +# error FALSE must be false +#endif +#ifndef TRUE +# define TRUE 1 +#elif !TRUE +# error TRUE must be true +#endif + +#endif + +#ifndef RUBY_FUNC_EXPORTED +#define RUBY_FUNC_EXPORTED +#endif + +#ifndef RUBY_EXTERN +#define RUBY_EXTERN extern +#endif + +#ifndef EXTERN +#define EXTERN RUBY_EXTERN /* deprecated */ +#endif + +#ifndef RUBY_MBCHAR_MAXSIZE +#define RUBY_MBCHAR_MAXSIZE INT_MAX + /* MB_CUR_MAX will not work well in C locale */ +#endif + +#if defined(__sparc) +void rb_sparc_flush_register_windows(void); +# define FLUSH_REGISTER_WINDOWS rb_sparc_flush_register_windows() +#elif defined(__ia64) +void *rb_ia64_bsp(void); +void rb_ia64_flushrs(void); +# define FLUSH_REGISTER_WINDOWS rb_ia64_flushrs() +#else +# define FLUSH_REGISTER_WINDOWS ((void)0) +#endif + +#if defined(DOSISH) +#define PATH_SEP ";" +#else +#define PATH_SEP ":" +#endif +#define PATH_SEP_CHAR PATH_SEP[0] + +#define PATH_ENV "PATH" + +#if defined(DOSISH) && !defined(__EMX__) +#define ENV_IGNORECASE +#endif + +#ifndef CASEFOLD_FILESYSTEM +# if defined DOSISH +# define CASEFOLD_FILESYSTEM 1 +# else +# define CASEFOLD_FILESYSTEM 0 +# endif +#endif + +#ifndef DLEXT_MAXLEN +#define DLEXT_MAXLEN 4 +#endif + +#ifndef RUBY_PLATFORM +#define RUBY_PLATFORM "unknown-unknown" +#endif + +#ifndef RUBY_ALIAS_FUNCTION_TYPE +#define RUBY_ALIAS_FUNCTION_TYPE(type, prot, name, args) \ + type prot {return name args;} +#endif +#ifndef RUBY_ALIAS_FUNCTION_VOID +#define RUBY_ALIAS_FUNCTION_VOID(prot, name, args) \ + void prot {name args;} +#endif +#ifndef RUBY_ALIAS_FUNCTION +#define RUBY_ALIAS_FUNCTION(prot, name, args) \ + RUBY_ALIAS_FUNCTION_TYPE(VALUE, prot, name, args) +#endif + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_DEFINES_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/digest.h b/ruby/include/ruby-2.1.0/ruby/digest.h new file mode 100644 index 0000000..89b26d8 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/digest.h @@ -0,0 +1,32 @@ +/************************************************ + + digest.h - header file for ruby digest modules + + $Author: akr $ + created at: Fri May 25 08:54:56 JST 2001 + + + Copyright (C) 2001-2006 Akinori MUSHA + + $RoughId: digest.h,v 1.3 2001/07/13 15:38:27 knu Exp $ + $Id: digest.h 25189 2009-10-02 12:04:37Z akr $ + +************************************************/ + +#include "ruby.h" + +#define RUBY_DIGEST_API_VERSION 2 + +typedef void (*rb_digest_hash_init_func_t)(void *); +typedef void (*rb_digest_hash_update_func_t)(void *, unsigned char *, size_t); +typedef void (*rb_digest_hash_finish_func_t)(void *, unsigned char *); + +typedef struct { + int api_version; + size_t digest_len; + size_t block_len; + size_t ctx_size; + rb_digest_hash_init_func_t init_func; + rb_digest_hash_update_func_t update_func; + rb_digest_hash_finish_func_t finish_func; +} rb_digest_metadata_t; diff --git a/ruby/include/ruby-2.1.0/ruby/dl.h b/ruby/include/ruby-2.1.0/ruby/dl.h new file mode 100644 index 0000000..f8380a8 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/dl.h @@ -0,0 +1,217 @@ +#ifndef RUBY_DL_H +#define RUBY_DL_H + +#include + +#if !defined(FUNC_CDECL) +# define FUNC_CDECL(x) x +#endif + +#if defined(HAVE_DLFCN_H) +# include +# /* some stranger systems may not define all of these */ +#ifndef RTLD_LAZY +#define RTLD_LAZY 0 +#endif +#ifndef RTLD_GLOBAL +#define RTLD_GLOBAL 0 +#endif +#ifndef RTLD_NOW +#define RTLD_NOW 0 +#endif +#else +# if defined(_WIN32) +# include +# define dlopen(name,flag) ((void)(flag),(void*)LoadLibrary(name)) +# define dlerror() strerror(rb_w32_map_errno(GetLastError())) +# define dlsym(handle,name) ((void*)GetProcAddress((handle),(name))) +# define RTLD_LAZY -1 +# define RTLD_NOW -1 +# define RTLD_GLOBAL -1 +# endif +#endif + +#define MAX_CALLBACK 5 +#define DLSTACK_TYPE SIGNED_VALUE +#define DLSTACK_SIZE (20) +#define DLSTACK_PROTO \ + DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,\ + DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,\ + DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,\ + DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE,DLSTACK_TYPE +#define DLSTACK_ARGS(stack) \ + (stack)[0],(stack)[1],(stack)[2],(stack)[3],(stack)[4],\ + (stack)[5],(stack)[6],(stack)[7],(stack)[8],(stack)[9],\ + (stack)[10],(stack)[11],(stack)[12],(stack)[13],(stack)[14],\ + (stack)[15],(stack)[16],(stack)[17],(stack)[18],(stack)[19] + +#define DLSTACK_PROTO0_ void +#define DLSTACK_PROTO1_ DLSTACK_TYPE +#define DLSTACK_PROTO2_ DLSTACK_PROTO1_, DLSTACK_TYPE +#define DLSTACK_PROTO3_ DLSTACK_PROTO2_, DLSTACK_TYPE +#define DLSTACK_PROTO4_ DLSTACK_PROTO3_, DLSTACK_TYPE +#define DLSTACK_PROTO4_ DLSTACK_PROTO3_, DLSTACK_TYPE +#define DLSTACK_PROTO5_ DLSTACK_PROTO4_, DLSTACK_TYPE +#define DLSTACK_PROTO6_ DLSTACK_PROTO5_, DLSTACK_TYPE +#define DLSTACK_PROTO7_ DLSTACK_PROTO6_, DLSTACK_TYPE +#define DLSTACK_PROTO8_ DLSTACK_PROTO7_, DLSTACK_TYPE +#define DLSTACK_PROTO9_ DLSTACK_PROTO8_, DLSTACK_TYPE +#define DLSTACK_PROTO10_ DLSTACK_PROTO9_, DLSTACK_TYPE +#define DLSTACK_PROTO11_ DLSTACK_PROTO10_, DLSTACK_TYPE +#define DLSTACK_PROTO12_ DLSTACK_PROTO11_, DLSTACK_TYPE +#define DLSTACK_PROTO13_ DLSTACK_PROTO12_, DLSTACK_TYPE +#define DLSTACK_PROTO14_ DLSTACK_PROTO13_, DLSTACK_TYPE +#define DLSTACK_PROTO14_ DLSTACK_PROTO13_, DLSTACK_TYPE +#define DLSTACK_PROTO15_ DLSTACK_PROTO14_, DLSTACK_TYPE +#define DLSTACK_PROTO16_ DLSTACK_PROTO15_, DLSTACK_TYPE +#define DLSTACK_PROTO17_ DLSTACK_PROTO16_, DLSTACK_TYPE +#define DLSTACK_PROTO18_ DLSTACK_PROTO17_, DLSTACK_TYPE +#define DLSTACK_PROTO19_ DLSTACK_PROTO18_, DLSTACK_TYPE +#define DLSTACK_PROTO20_ DLSTACK_PROTO19_, DLSTACK_TYPE + +/* + * Add ",..." as the last argument. + * This is required for variable argument functions such + * as fprintf() on x86_64-linux. + * + * http://refspecs.linuxfoundation.org/elf/x86_64-abi-0.95.pdf + * page 19: + * + * For calls that may call functions that use varargs or stdargs + * (prototype-less calls or calls to functions containing ellipsis + * (...) in the declaration) %al is used as hidden argument to + * specify the number of SSE registers used. + */ +#define DLSTACK_PROTO0 void +#define DLSTACK_PROTO1 DLSTACK_PROTO1_, ... +#define DLSTACK_PROTO2 DLSTACK_PROTO2_, ... +#define DLSTACK_PROTO3 DLSTACK_PROTO3_, ... +#define DLSTACK_PROTO4 DLSTACK_PROTO4_, ... +#define DLSTACK_PROTO4 DLSTACK_PROTO4_, ... +#define DLSTACK_PROTO5 DLSTACK_PROTO5_, ... +#define DLSTACK_PROTO6 DLSTACK_PROTO6_, ... +#define DLSTACK_PROTO7 DLSTACK_PROTO7_, ... +#define DLSTACK_PROTO8 DLSTACK_PROTO8_, ... +#define DLSTACK_PROTO9 DLSTACK_PROTO9_, ... +#define DLSTACK_PROTO10 DLSTACK_PROTO10_, ... +#define DLSTACK_PROTO11 DLSTACK_PROTO11_, ... +#define DLSTACK_PROTO12 DLSTACK_PROTO12_, ... +#define DLSTACK_PROTO13 DLSTACK_PROTO13_, ... +#define DLSTACK_PROTO14 DLSTACK_PROTO14_, ... +#define DLSTACK_PROTO14 DLSTACK_PROTO14_, ... +#define DLSTACK_PROTO15 DLSTACK_PROTO15_, ... +#define DLSTACK_PROTO16 DLSTACK_PROTO16_, ... +#define DLSTACK_PROTO17 DLSTACK_PROTO17_, ... +#define DLSTACK_PROTO18 DLSTACK_PROTO18_, ... +#define DLSTACK_PROTO19 DLSTACK_PROTO19_, ... +#define DLSTACK_PROTO20 DLSTACK_PROTO20_, ... + +#define DLSTACK_ARGS0(stack) +#define DLSTACK_ARGS1(stack) (stack)[0] +#define DLSTACK_ARGS2(stack) DLSTACK_ARGS1(stack), (stack)[1] +#define DLSTACK_ARGS3(stack) DLSTACK_ARGS2(stack), (stack)[2] +#define DLSTACK_ARGS4(stack) DLSTACK_ARGS3(stack), (stack)[3] +#define DLSTACK_ARGS5(stack) DLSTACK_ARGS4(stack), (stack)[4] +#define DLSTACK_ARGS6(stack) DLSTACK_ARGS5(stack), (stack)[5] +#define DLSTACK_ARGS7(stack) DLSTACK_ARGS6(stack), (stack)[6] +#define DLSTACK_ARGS8(stack) DLSTACK_ARGS7(stack), (stack)[7] +#define DLSTACK_ARGS9(stack) DLSTACK_ARGS8(stack), (stack)[8] +#define DLSTACK_ARGS10(stack) DLSTACK_ARGS9(stack), (stack)[9] +#define DLSTACK_ARGS11(stack) DLSTACK_ARGS10(stack), (stack)[10] +#define DLSTACK_ARGS12(stack) DLSTACK_ARGS11(stack), (stack)[11] +#define DLSTACK_ARGS13(stack) DLSTACK_ARGS12(stack), (stack)[12] +#define DLSTACK_ARGS14(stack) DLSTACK_ARGS13(stack), (stack)[13] +#define DLSTACK_ARGS15(stack) DLSTACK_ARGS14(stack), (stack)[14] +#define DLSTACK_ARGS16(stack) DLSTACK_ARGS15(stack), (stack)[15] +#define DLSTACK_ARGS17(stack) DLSTACK_ARGS16(stack), (stack)[16] +#define DLSTACK_ARGS18(stack) DLSTACK_ARGS17(stack), (stack)[17] +#define DLSTACK_ARGS19(stack) DLSTACK_ARGS18(stack), (stack)[18] +#define DLSTACK_ARGS20(stack) DLSTACK_ARGS19(stack), (stack)[19] + +extern VALUE rb_mDL; +extern VALUE rb_cDLHandle; +extern VALUE rb_cDLSymbol; +extern VALUE rb_eDLError; +extern VALUE rb_eDLTypeError; + +#define ALIGN_OF(type) offsetof(struct {char align_c; type align_x;}, align_x) + +#define ALIGN_VOIDP ALIGN_OF(void*) +#define ALIGN_SHORT ALIGN_OF(short) +#define ALIGN_CHAR ALIGN_OF(char) +#define ALIGN_INT ALIGN_OF(int) +#define ALIGN_LONG ALIGN_OF(long) +#if HAVE_LONG_LONG +#define ALIGN_LONG_LONG ALIGN_OF(LONG_LONG) +#endif +#define ALIGN_FLOAT ALIGN_OF(float) +#define ALIGN_DOUBLE ALIGN_OF(double) + +#define DLALIGN(ptr,offset,align) \ + ((offset) += ((align) - ((uintptr_t)((char *)(ptr) + (offset))) % (align)) % (align)) + + +#define DLTYPE_VOID 0 +#define DLTYPE_VOIDP 1 +#define DLTYPE_CHAR 2 +#define DLTYPE_SHORT 3 +#define DLTYPE_INT 4 +#define DLTYPE_LONG 5 +#if HAVE_LONG_LONG +#define DLTYPE_LONG_LONG 6 +#endif +#define DLTYPE_FLOAT 7 +#define DLTYPE_DOUBLE 8 +#define MAX_DLTYPE 9 + +#if SIZEOF_VOIDP == SIZEOF_LONG +# define PTR2NUM(x) (ULONG2NUM((unsigned long)(x))) +# define NUM2PTR(x) ((void*)(NUM2ULONG(x))) +#else +/* # error --->> Ruby/DL2 requires sizeof(void*) == sizeof(long) to be compiled. <<--- */ +# define PTR2NUM(x) (ULL2NUM((unsigned long long)(x))) +# define NUM2PTR(x) ((void*)(NUM2ULL(x))) +#endif + +#define BOOL2INT(x) (((x) == Qtrue)?1:0) +#define INT2BOOL(x) ((x)?Qtrue:Qfalse) + +typedef void (*freefunc_t)(void*); + +struct dl_handle { + void *ptr; + int open; + int enable_close; +}; + + +struct cfunc_data { + void *ptr; + char *name; + int type; + ID calltype; + VALUE wrap; +}; +extern ID rbdl_id_cdecl; +extern ID rbdl_id_stdcall; +#define CFUNC_CDECL (rbdl_id_cdecl) +#define CFUNC_STDCALL (rbdl_id_stdcall) + +struct ptr_data { + void *ptr; + long size; + freefunc_t free; + VALUE wrap[2]; +}; + +#define RDL_HANDLE(obj) ((struct dl_handle *)(DATA_PTR(obj))) +#define RCFUNC_DATA(obj) ((struct cfunc_data *)(DATA_PTR(obj))) +#define RPTR_DATA(obj) ((struct ptr_data *)(DATA_PTR(obj))) + +VALUE rb_dlcfunc_new(void (*func)(), int dltype, const char * name, ID calltype); +int rb_dlcfunc_kind_p(VALUE func); +VALUE rb_dlptr_new(void *ptr, long size, freefunc_t func); +VALUE rb_dlptr_new2(VALUE klass, void *ptr, long size, freefunc_t func); +VALUE rb_dlptr_malloc(long size, freefunc_t func); + +#endif diff --git a/ruby/include/ruby-2.1.0/ruby/encoding.h b/ruby/include/ruby-2.1.0/ruby/encoding.h new file mode 100644 index 0000000..1c5d8da --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/encoding.h @@ -0,0 +1,363 @@ +/********************************************************************** + + encoding.h - + + $Author: matz $ + created at: Thu May 24 11:49:41 JST 2007 + + Copyright (C) 2007 Yukihiro Matsumoto + +**********************************************************************/ + +#ifndef RUBY_ENCODING_H +#define RUBY_ENCODING_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#include +#include "ruby/oniguruma.h" + +RUBY_SYMBOL_EXPORT_BEGIN + +#define ENCODING_INLINE_MAX 127 +#define ENCODING_SHIFT (FL_USHIFT+10) +#define ENCODING_MASK (((VALUE)ENCODING_INLINE_MAX)<flags &= ~ENCODING_MASK;\ + RBASIC(obj)->flags |= (VALUE)(i) << ENCODING_SHIFT;\ +} while (0) +#define ENCODING_SET(obj,i) rb_enc_set_index((obj), (i)) + +#define ENCODING_GET_INLINED(obj) (int)((RBASIC(obj)->flags & ENCODING_MASK)>>ENCODING_SHIFT) +#define ENCODING_GET(obj) \ + (ENCODING_GET_INLINED(obj) != ENCODING_INLINE_MAX ? \ + ENCODING_GET_INLINED(obj) : \ + rb_enc_get_index(obj)) + +#define ENCODING_IS_ASCII8BIT(obj) (ENCODING_GET_INLINED(obj) == 0) + +#define ENCODING_MAXNAMELEN 42 + +#define ENC_CODERANGE_MASK ((int)(FL_USER8|FL_USER9)) +#define ENC_CODERANGE_UNKNOWN 0 +#define ENC_CODERANGE_7BIT ((int)FL_USER8) +#define ENC_CODERANGE_VALID ((int)FL_USER9) +#define ENC_CODERANGE_BROKEN ((int)(FL_USER8|FL_USER9)) +#define ENC_CODERANGE(obj) ((int)RBASIC(obj)->flags & ENC_CODERANGE_MASK) +#define ENC_CODERANGE_ASCIIONLY(obj) (ENC_CODERANGE(obj) == ENC_CODERANGE_7BIT) +#define ENC_CODERANGE_SET(obj,cr) (RBASIC(obj)->flags = \ + (RBASIC(obj)->flags & ~ENC_CODERANGE_MASK) | (cr)) +#define ENC_CODERANGE_CLEAR(obj) ENC_CODERANGE_SET((obj),0) + +/* assumed ASCII compatibility */ +#define ENC_CODERANGE_AND(a, b) \ + ((a) == ENC_CODERANGE_7BIT ? (b) : \ + (a) == ENC_CODERANGE_VALID ? ((b) == ENC_CODERANGE_7BIT ? ENC_CODERANGE_VALID : (b)) : \ + ENC_CODERANGE_UNKNOWN) + +#define ENCODING_CODERANGE_SET(obj, encindex, cr) \ + do { \ + VALUE rb_encoding_coderange_obj = (obj); \ + ENCODING_SET(rb_encoding_coderange_obj, (encindex)); \ + ENC_CODERANGE_SET(rb_encoding_coderange_obj, (cr)); \ + } while (0) + +typedef OnigEncodingType rb_encoding; + +int rb_char_to_option_kcode(int c, int *option, int *kcode); + +int rb_enc_replicate(const char *, rb_encoding *); +int rb_define_dummy_encoding(const char *); +#define rb_enc_to_index(enc) ((enc) ? ENC_TO_ENCINDEX(enc) : 0) +int rb_enc_get_index(VALUE obj); +void rb_enc_set_index(VALUE obj, int encindex); +int rb_enc_find_index(const char *name); +int rb_to_encoding_index(VALUE); +rb_encoding* rb_to_encoding(VALUE); +rb_encoding* rb_find_encoding(VALUE); +rb_encoding* rb_enc_get(VALUE); +rb_encoding* rb_enc_compatible(VALUE,VALUE); +rb_encoding* rb_enc_check(VALUE,VALUE); +VALUE rb_enc_associate_index(VALUE, int); +VALUE rb_enc_associate(VALUE, rb_encoding*); +void rb_enc_copy(VALUE dst, VALUE src); + +VALUE rb_enc_str_new(const char*, long, rb_encoding*); +VALUE rb_enc_str_new_cstr(const char*, rb_encoding*); +VALUE rb_enc_reg_new(const char*, long, rb_encoding*, int); +PRINTF_ARGS(VALUE rb_enc_sprintf(rb_encoding *, const char*, ...), 2, 3); +VALUE rb_enc_vsprintf(rb_encoding *, const char*, va_list); +long rb_enc_strlen(const char*, const char*, rb_encoding*); +char* rb_enc_nth(const char*, const char*, long, rb_encoding*); +VALUE rb_obj_encoding(VALUE); +VALUE rb_enc_str_buf_cat(VALUE str, const char *ptr, long len, rb_encoding *enc); +VALUE rb_enc_uint_chr(unsigned int code, rb_encoding *enc); + +VALUE rb_external_str_new_with_enc(const char *ptr, long len, rb_encoding *); +VALUE rb_str_export_to_enc(VALUE, rb_encoding *); +VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to); +VALUE rb_str_conv_enc_opts(VALUE str, rb_encoding *from, rb_encoding *to, int ecflags, VALUE ecopts); + +#if defined(__GNUC__) && !defined(__PCC__) +#define rb_enc_str_new_cstr(str, enc) __extension__ ( \ +{ \ + (__builtin_constant_p(str)) ? \ + rb_enc_str_new((str), (long)strlen(str), (enc)) : \ + rb_enc_str_new_cstr((str), (enc)); \ +}) +#endif + +PRINTF_ARGS(NORETURN(void rb_enc_raise(rb_encoding *, VALUE, const char*, ...)), 3, 4); + +/* index -> rb_encoding */ +rb_encoding* rb_enc_from_index(int idx); + +/* name -> rb_encoding */ +rb_encoding * rb_enc_find(const char *name); + +/* rb_encoding * -> name */ +#define rb_enc_name(enc) (enc)->name + +/* rb_encoding * -> minlen/maxlen */ +#define rb_enc_mbminlen(enc) (enc)->min_enc_len +#define rb_enc_mbmaxlen(enc) (enc)->max_enc_len + +/* -> mbclen (no error notification: 0 < ret <= e-p, no exception) */ +int rb_enc_mbclen(const char *p, const char *e, rb_encoding *enc); + +/* -> mbclen (only for valid encoding) */ +int rb_enc_fast_mbclen(const char *p, const char *e, rb_encoding *enc); + +/* -> chlen, invalid or needmore */ +int rb_enc_precise_mbclen(const char *p, const char *e, rb_encoding *enc); +#define MBCLEN_CHARFOUND_P(ret) ONIGENC_MBCLEN_CHARFOUND_P(ret) +#define MBCLEN_CHARFOUND_LEN(ret) ONIGENC_MBCLEN_CHARFOUND_LEN(ret) +#define MBCLEN_INVALID_P(ret) ONIGENC_MBCLEN_INVALID_P(ret) +#define MBCLEN_NEEDMORE_P(ret) ONIGENC_MBCLEN_NEEDMORE_P(ret) +#define MBCLEN_NEEDMORE_LEN(ret) ONIGENC_MBCLEN_NEEDMORE_LEN(ret) + +/* -> 0x00..0x7f, -1 */ +int rb_enc_ascget(const char *p, const char *e, int *len, rb_encoding *enc); + + +/* -> code (and len) or raise exception */ +unsigned int rb_enc_codepoint_len(const char *p, const char *e, int *len, rb_encoding *enc); + +/* prototype for obsolete function */ +unsigned int rb_enc_codepoint(const char *p, const char *e, rb_encoding *enc); +/* overriding macro */ +#define rb_enc_codepoint(p,e,enc) rb_enc_codepoint_len((p),(e),0,(enc)) +#define rb_enc_mbc_to_codepoint(p, e, enc) ONIGENC_MBC_TO_CODE((enc),(UChar*)(p),(UChar*)(e)) + +/* -> codelen>0 or raise exception */ +int rb_enc_codelen(int code, rb_encoding *enc); +/* -> 0 for invalid codepoint */ +int rb_enc_code_to_mbclen(int code, rb_encoding *enc); +#define rb_enc_code_to_mbclen(c, enc) ONIGENC_CODE_TO_MBCLEN((enc), (c)); + +/* code,ptr,encoding -> write buf */ +#define rb_enc_mbcput(c,buf,enc) ONIGENC_CODE_TO_MBC((enc),(c),(UChar*)(buf)) + +/* start, ptr, end, encoding -> prev_char */ +#define rb_enc_prev_char(s,p,e,enc) ((char *)onigenc_get_prev_char_head((enc),(UChar*)(s),(UChar*)(p),(UChar*)(e))) +/* start, ptr, end, encoding -> next_char */ +#define rb_enc_left_char_head(s,p,e,enc) ((char *)onigenc_get_left_adjust_char_head((enc),(UChar*)(s),(UChar*)(p),(UChar*)(e))) +#define rb_enc_right_char_head(s,p,e,enc) ((char *)onigenc_get_right_adjust_char_head((enc),(UChar*)(s),(UChar*)(p),(UChar*)(e))) +#define rb_enc_step_back(s,p,e,n,enc) ((char *)onigenc_step_back((enc),(UChar*)(s),(UChar*)(p),(UChar*)(e),(int)(n))) + +/* ptr, ptr, encoding -> newline_or_not */ +#define rb_enc_is_newline(p,end,enc) ONIGENC_IS_MBC_NEWLINE((enc),(UChar*)(p),(UChar*)(end)) + +#define rb_enc_isctype(c,t,enc) ONIGENC_IS_CODE_CTYPE((enc),(c),(t)) +#define rb_enc_isascii(c,enc) ONIGENC_IS_CODE_ASCII(c) +#define rb_enc_isalpha(c,enc) ONIGENC_IS_CODE_ALPHA((enc),(c)) +#define rb_enc_islower(c,enc) ONIGENC_IS_CODE_LOWER((enc),(c)) +#define rb_enc_isupper(c,enc) ONIGENC_IS_CODE_UPPER((enc),(c)) +#define rb_enc_ispunct(c,enc) ONIGENC_IS_CODE_PUNCT((enc),(c)) +#define rb_enc_isalnum(c,enc) ONIGENC_IS_CODE_ALNUM((enc),(c)) +#define rb_enc_isprint(c,enc) ONIGENC_IS_CODE_PRINT((enc),(c)) +#define rb_enc_isspace(c,enc) ONIGENC_IS_CODE_SPACE((enc),(c)) +#define rb_enc_isdigit(c,enc) ONIGENC_IS_CODE_DIGIT((enc),(c)) + +#define rb_enc_asciicompat(enc) (rb_enc_mbminlen(enc)==1 && !rb_enc_dummy_p(enc)) + +int rb_enc_casefold(char *to, const char *p, const char *e, rb_encoding *enc); +int rb_enc_toupper(int c, rb_encoding *enc); +int rb_enc_tolower(int c, rb_encoding *enc); +ID rb_intern3(const char*, long, rb_encoding*); +ID rb_interned_id_p(const char *, long, rb_encoding *); +int rb_enc_symname_p(const char*, rb_encoding*); +int rb_enc_symname2_p(const char*, long, rb_encoding*); +int rb_enc_str_coderange(VALUE); +long rb_str_coderange_scan_restartable(const char*, const char*, rb_encoding*, int*); +int rb_enc_str_asciionly_p(VALUE); +#define rb_enc_str_asciicompat_p(str) rb_enc_asciicompat(rb_enc_get(str)) +VALUE rb_enc_from_encoding(rb_encoding *enc); +int rb_enc_unicode_p(rb_encoding *enc); +rb_encoding *rb_ascii8bit_encoding(void); +rb_encoding *rb_utf8_encoding(void); +rb_encoding *rb_usascii_encoding(void); +rb_encoding *rb_locale_encoding(void); +rb_encoding *rb_filesystem_encoding(void); +rb_encoding *rb_default_external_encoding(void); +rb_encoding *rb_default_internal_encoding(void); +#ifndef rb_ascii8bit_encindex +int rb_ascii8bit_encindex(void); +#endif +#ifndef rb_utf8_encindex +int rb_utf8_encindex(void); +#endif +#ifndef rb_usascii_encindex +int rb_usascii_encindex(void); +#endif +int rb_locale_encindex(void); +int rb_filesystem_encindex(void); +VALUE rb_enc_default_external(void); +VALUE rb_enc_default_internal(void); +void rb_enc_set_default_external(VALUE encoding); +void rb_enc_set_default_internal(VALUE encoding); +VALUE rb_locale_charmap(VALUE klass); +long rb_memsearch(const void*,long,const void*,long,rb_encoding*); +char *rb_enc_path_next(const char *,const char *,rb_encoding*); +char *rb_enc_path_skip_prefix(const char *,const char *,rb_encoding*); +char *rb_enc_path_last_separator(const char *,const char *,rb_encoding*); +char *rb_enc_path_end(const char *,const char *,rb_encoding*); +const char *ruby_enc_find_basename(const char *name, long *baselen, long *alllen, rb_encoding *enc); +const char *ruby_enc_find_extname(const char *name, long *len, rb_encoding *enc); +ID rb_check_id_cstr(const char *ptr, long len, rb_encoding *enc); + +RUBY_EXTERN VALUE rb_cEncoding; +#define ENC_DUMMY_FLAG (1<<24) +#define ENC_INDEX_MASK (~(~0U<<24)) + +#define ENC_TO_ENCINDEX(enc) (int)((enc)->ruby_encoding_index & ENC_INDEX_MASK) + +#define ENC_DUMMY_P(enc) ((enc)->ruby_encoding_index & ENC_DUMMY_FLAG) +#define ENC_SET_DUMMY(enc) ((enc)->ruby_encoding_index |= ENC_DUMMY_FLAG) + +static inline int +rb_enc_dummy_p(rb_encoding *enc) +{ + return ENC_DUMMY_P(enc) != 0; +} + +/* econv stuff */ + +typedef enum { + econv_invalid_byte_sequence, + econv_undefined_conversion, + econv_destination_buffer_full, + econv_source_buffer_empty, + econv_finished, + econv_after_output, + econv_incomplete_input +} rb_econv_result_t; + +typedef struct rb_econv_t rb_econv_t; + +VALUE rb_str_encode(VALUE str, VALUE to, int ecflags, VALUE ecopts); +int rb_econv_has_convpath_p(const char* from_encoding, const char* to_encoding); + +int rb_econv_prepare_options(VALUE opthash, VALUE *ecopts, int ecflags); +int rb_econv_prepare_opts(VALUE opthash, VALUE *ecopts); + +rb_econv_t *rb_econv_open(const char *source_encoding, const char *destination_encoding, int ecflags); +rb_econv_t *rb_econv_open_opts(const char *source_encoding, const char *destination_encoding, int ecflags, VALUE ecopts); + +rb_econv_result_t rb_econv_convert(rb_econv_t *ec, + const unsigned char **source_buffer_ptr, const unsigned char *source_buffer_end, + unsigned char **destination_buffer_ptr, unsigned char *destination_buffer_end, + int flags); +void rb_econv_close(rb_econv_t *ec); + +/* result: 0:success -1:failure */ +int rb_econv_set_replacement(rb_econv_t *ec, const unsigned char *str, size_t len, const char *encname); + +/* result: 0:success -1:failure */ +int rb_econv_decorate_at_first(rb_econv_t *ec, const char *decorator_name); +int rb_econv_decorate_at_last(rb_econv_t *ec, const char *decorator_name); + +VALUE rb_econv_open_exc(const char *senc, const char *denc, int ecflags); + +/* result: 0:success -1:failure */ +int rb_econv_insert_output(rb_econv_t *ec, + const unsigned char *str, size_t len, const char *str_encoding); + +/* encoding that rb_econv_insert_output doesn't need conversion */ +const char *rb_econv_encoding_to_insert_output(rb_econv_t *ec); + +/* raise an error if the last rb_econv_convert is error */ +void rb_econv_check_error(rb_econv_t *ec); + +/* returns an exception object or nil */ +VALUE rb_econv_make_exception(rb_econv_t *ec); + +int rb_econv_putbackable(rb_econv_t *ec); +void rb_econv_putback(rb_econv_t *ec, unsigned char *p, int n); + +/* returns the corresponding ASCII compatible encoding for encname, + * or NULL if encname is not ASCII incompatible encoding. */ +const char *rb_econv_asciicompat_encoding(const char *encname); + +VALUE rb_econv_str_convert(rb_econv_t *ec, VALUE src, int flags); +VALUE rb_econv_substr_convert(rb_econv_t *ec, VALUE src, long byteoff, long bytesize, int flags); +VALUE rb_econv_str_append(rb_econv_t *ec, VALUE src, VALUE dst, int flags); +VALUE rb_econv_substr_append(rb_econv_t *ec, VALUE src, long byteoff, long bytesize, VALUE dst, int flags); +VALUE rb_econv_append(rb_econv_t *ec, const char *bytesrc, long bytesize, VALUE dst, int flags); + +void rb_econv_binmode(rb_econv_t *ec); + +/* flags for rb_econv_open */ + +#define ECONV_ERROR_HANDLER_MASK 0x000000ff + +#define ECONV_INVALID_MASK 0x0000000f +#define ECONV_INVALID_REPLACE 0x00000002 + +#define ECONV_UNDEF_MASK 0x000000f0 +#define ECONV_UNDEF_REPLACE 0x00000020 +#define ECONV_UNDEF_HEX_CHARREF 0x00000030 + +#define ECONV_DECORATOR_MASK 0x0000ff00 +#define ECONV_NEWLINE_DECORATOR_MASK 0x00003f00 +#define ECONV_NEWLINE_DECORATOR_READ_MASK 0x00000f00 +#define ECONV_NEWLINE_DECORATOR_WRITE_MASK 0x00003000 + +#define ECONV_UNIVERSAL_NEWLINE_DECORATOR 0x00000100 +#define ECONV_CRLF_NEWLINE_DECORATOR 0x00001000 +#define ECONV_CR_NEWLINE_DECORATOR 0x00002000 +#define ECONV_XML_TEXT_DECORATOR 0x00004000 +#define ECONV_XML_ATTR_CONTENT_DECORATOR 0x00008000 + +#define ECONV_STATEFUL_DECORATOR_MASK 0x00f00000 +#define ECONV_XML_ATTR_QUOTE_DECORATOR 0x00100000 + +#if defined(RUBY_TEST_CRLF_ENVIRONMENT) || defined(_WIN32) +#define ECONV_DEFAULT_NEWLINE_DECORATOR ECONV_CRLF_NEWLINE_DECORATOR +#else +#define ECONV_DEFAULT_NEWLINE_DECORATOR 0 +#endif + +/* end of flags for rb_econv_open */ + +/* flags for rb_econv_convert */ +#define ECONV_PARTIAL_INPUT 0x00010000 +#define ECONV_AFTER_OUTPUT 0x00020000 +/* end of flags for rb_econv_convert */ + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_ENCODING_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/intern.h b/ruby/include/ruby-2.1.0/ruby/intern.h new file mode 100644 index 0000000..cf79f42 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/intern.h @@ -0,0 +1,959 @@ +/********************************************************************** + + intern.h - + + $Author: nagachika $ + created at: Thu Jun 10 14:22:17 JST 1993 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + Copyright (C) 2000 Network Applied Communication Laboratory, Inc. + Copyright (C) 2000 Information-technology Promotion Agency, Japan + +**********************************************************************/ + +#ifndef RUBY_INTERN_H +#define RUBY_INTERN_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#include "ruby/defines.h" +#ifdef RUBY_EXTCONF_H +#include RUBY_EXTCONF_H +#endif + +#ifdef HAVE_STDARG_PROTOTYPES +# include +#else +# include +#endif + +#include "ruby/st.h" + +RUBY_SYMBOL_EXPORT_BEGIN + +/* + * Functions and variables that are used by more than one source file of + * the kernel. + */ + +#define UNLIMITED_ARGUMENTS (-1) + +/* array.c */ +void rb_mem_clear(register VALUE*, register long); +VALUE rb_assoc_new(VALUE, VALUE); +VALUE rb_check_array_type(VALUE); +VALUE rb_ary_new(void); +VALUE rb_ary_new_capa(long capa); +VALUE rb_ary_new_from_args(long n, ...); +VALUE rb_ary_new_from_values(long n, const VALUE *elts); +VALUE rb_ary_tmp_new(long); +void rb_ary_free(VALUE); +void rb_ary_modify(VALUE); +VALUE rb_ary_freeze(VALUE); +VALUE rb_ary_shared_with_p(VALUE, VALUE); +VALUE rb_ary_aref(int, VALUE*, VALUE); +VALUE rb_ary_subseq(VALUE, long, long); +void rb_ary_store(VALUE, long, VALUE); +VALUE rb_ary_dup(VALUE); +VALUE rb_ary_resurrect(VALUE ary); +VALUE rb_ary_to_ary(VALUE); +VALUE rb_ary_to_s(VALUE); +VALUE rb_ary_cat(VALUE, const VALUE *, long); +VALUE rb_ary_push(VALUE, VALUE); +VALUE rb_ary_pop(VALUE); +VALUE rb_ary_shift(VALUE); +VALUE rb_ary_unshift(VALUE, VALUE); +VALUE rb_ary_entry(VALUE, long); +VALUE rb_ary_each(VALUE); +VALUE rb_ary_join(VALUE, VALUE); +VALUE rb_ary_reverse(VALUE); +VALUE rb_ary_rotate(VALUE, long); +VALUE rb_ary_sort(VALUE); +VALUE rb_ary_sort_bang(VALUE); +VALUE rb_ary_delete(VALUE, VALUE); +VALUE rb_ary_delete_at(VALUE, long); +VALUE rb_ary_clear(VALUE); +VALUE rb_ary_plus(VALUE, VALUE); +VALUE rb_ary_concat(VALUE, VALUE); +VALUE rb_ary_assoc(VALUE, VALUE); +VALUE rb_ary_rassoc(VALUE, VALUE); +VALUE rb_ary_includes(VALUE, VALUE); +VALUE rb_ary_cmp(VALUE, VALUE); +VALUE rb_ary_replace(VALUE copy, VALUE orig); +VALUE rb_get_values_at(VALUE, long, int, VALUE*, VALUE(*)(VALUE,long)); +VALUE rb_ary_resize(VALUE ary, long len); +#define rb_ary_new2 rb_ary_new_capa +#define rb_ary_new3 rb_ary_new_from_args +#define rb_ary_new4 rb_ary_new_from_values +/* bignum.c */ +VALUE rb_big_new(long, int); +int rb_bigzero_p(VALUE x); +VALUE rb_big_clone(VALUE); +void rb_big_2comp(VALUE); +VALUE rb_big_norm(VALUE); +void rb_big_resize(VALUE big, long len); +VALUE rb_cstr_to_inum(const char*, int, int); +VALUE rb_str_to_inum(VALUE, int, int); +VALUE rb_cstr2inum(const char*, int); +VALUE rb_str2inum(VALUE, int); +VALUE rb_big2str(VALUE, int); +DEPRECATED(VALUE rb_big2str0(VALUE, int, int)); +SIGNED_VALUE rb_big2long(VALUE); +#define rb_big2int(x) rb_big2long(x) +VALUE rb_big2ulong(VALUE); +#define rb_big2uint(x) rb_big2ulong(x) +DEPRECATED(VALUE rb_big2ulong_pack(VALUE x)); +#if HAVE_LONG_LONG +LONG_LONG rb_big2ll(VALUE); +unsigned LONG_LONG rb_big2ull(VALUE); +#endif /* HAVE_LONG_LONG */ +DEPRECATED(void rb_quad_pack(char*,VALUE)); +DEPRECATED(VALUE rb_quad_unpack(const char*,int)); +void rb_big_pack(VALUE val, unsigned long *buf, long num_longs); +VALUE rb_big_unpack(unsigned long *buf, long num_longs); +int rb_uv_to_utf8(char[6],unsigned long); +VALUE rb_dbl2big(double); +double rb_big2dbl(VALUE); +VALUE rb_big_cmp(VALUE, VALUE); +VALUE rb_big_eq(VALUE, VALUE); +VALUE rb_big_eql(VALUE, VALUE); +VALUE rb_big_plus(VALUE, VALUE); +VALUE rb_big_minus(VALUE, VALUE); +VALUE rb_big_mul(VALUE, VALUE); +VALUE rb_big_div(VALUE, VALUE); +VALUE rb_big_idiv(VALUE, VALUE); +VALUE rb_big_modulo(VALUE, VALUE); +VALUE rb_big_divmod(VALUE, VALUE); +VALUE rb_big_pow(VALUE, VALUE); +VALUE rb_big_and(VALUE, VALUE); +VALUE rb_big_or(VALUE, VALUE); +VALUE rb_big_xor(VALUE, VALUE); +VALUE rb_big_lshift(VALUE, VALUE); +VALUE rb_big_rshift(VALUE, VALUE); + +/* For rb_integer_pack and rb_integer_unpack: */ +/* "MS" in MSWORD and MSBYTE means "most significant" */ +/* "LS" in LSWORD and LSBYTE means "least significant" */ +#define INTEGER_PACK_MSWORD_FIRST 0x01 +#define INTEGER_PACK_LSWORD_FIRST 0x02 +#define INTEGER_PACK_MSBYTE_FIRST 0x10 +#define INTEGER_PACK_LSBYTE_FIRST 0x20 +#define INTEGER_PACK_NATIVE_BYTE_ORDER 0x40 +#define INTEGER_PACK_2COMP 0x80 +#define INTEGER_PACK_FORCE_GENERIC_IMPLEMENTATION 0x400 +/* For rb_integer_unpack: */ +#define INTEGER_PACK_FORCE_BIGNUM 0x100 +#define INTEGER_PACK_NEGATIVE 0x200 +/* Combinations: */ +#define INTEGER_PACK_LITTLE_ENDIAN \ + (INTEGER_PACK_LSWORD_FIRST | \ + INTEGER_PACK_LSBYTE_FIRST) +#define INTEGER_PACK_BIG_ENDIAN \ + (INTEGER_PACK_MSWORD_FIRST | \ + INTEGER_PACK_MSBYTE_FIRST) +int rb_integer_pack(VALUE val, void *words, size_t numwords, size_t wordsize, size_t nails, int flags); +VALUE rb_integer_unpack(const void *words, size_t numwords, size_t wordsize, size_t nails, int flags); +size_t rb_absint_size(VALUE val, int *nlz_bits_ret); +size_t rb_absint_numwords(VALUE val, size_t word_numbits, size_t *nlz_bits_ret); +int rb_absint_singlebit_p(VALUE val); + +/* rational.c */ +VALUE rb_rational_raw(VALUE, VALUE); +#define rb_rational_raw1(x) rb_rational_raw((x), INT2FIX(1)) +#define rb_rational_raw2(x,y) rb_rational_raw((x), (y)) +VALUE rb_rational_new(VALUE, VALUE); +#define rb_rational_new1(x) rb_rational_new((x), INT2FIX(1)) +#define rb_rational_new2(x,y) rb_rational_new((x), (y)) +VALUE rb_Rational(VALUE, VALUE); +#define rb_Rational1(x) rb_Rational((x), INT2FIX(1)) +#define rb_Rational2(x,y) rb_Rational((x), (y)) +VALUE rb_flt_rationalize_with_prec(VALUE, VALUE); +VALUE rb_flt_rationalize(VALUE); +/* complex.c */ +VALUE rb_complex_raw(VALUE, VALUE); +#define rb_complex_raw1(x) rb_complex_raw((x), INT2FIX(0)) +#define rb_complex_raw2(x,y) rb_complex_raw((x), (y)) +VALUE rb_complex_new(VALUE, VALUE); +#define rb_complex_new1(x) rb_complex_new((x), INT2FIX(0)) +#define rb_complex_new2(x,y) rb_complex_new((x), (y)) +VALUE rb_complex_polar(VALUE, VALUE); +VALUE rb_Complex(VALUE, VALUE); +#define rb_Complex1(x) rb_Complex((x), INT2FIX(0)) +#define rb_Complex2(x,y) rb_Complex((x), (y)) +/* class.c */ +VALUE rb_class_boot(VALUE); +VALUE rb_class_new(VALUE); +VALUE rb_mod_init_copy(VALUE, VALUE); +VALUE rb_singleton_class_clone(VALUE); +void rb_singleton_class_attached(VALUE,VALUE); +VALUE rb_make_metaclass(VALUE, VALUE); +void rb_check_inheritable(VALUE); +VALUE rb_class_inherited(VALUE, VALUE); +VALUE rb_define_class_id(ID, VALUE); +VALUE rb_define_class_id_under(VALUE, ID, VALUE); +VALUE rb_module_new(void); +VALUE rb_define_module_id(ID); +VALUE rb_define_module_id_under(VALUE, ID); +VALUE rb_include_class_new(VALUE, VALUE); +VALUE rb_mod_included_modules(VALUE); +VALUE rb_mod_include_p(VALUE, VALUE); +VALUE rb_mod_ancestors(VALUE); +VALUE rb_class_instance_methods(int, VALUE*, VALUE); +VALUE rb_class_public_instance_methods(int, VALUE*, VALUE); +VALUE rb_class_protected_instance_methods(int, VALUE*, VALUE); +VALUE rb_class_private_instance_methods(int, VALUE*, VALUE); +VALUE rb_obj_singleton_methods(int, VALUE*, VALUE); +void rb_define_method_id(VALUE, ID, VALUE (*)(ANYARGS), int); +void rb_frozen_class_p(VALUE); +void rb_undef(VALUE, ID); +void rb_define_protected_method(VALUE, const char*, VALUE (*)(ANYARGS), int); +void rb_define_private_method(VALUE, const char*, VALUE (*)(ANYARGS), int); +void rb_define_singleton_method(VALUE, const char*, VALUE(*)(ANYARGS), int); +VALUE rb_singleton_class(VALUE); +/* compar.c */ +int rb_cmpint(VALUE, VALUE, VALUE); +NORETURN(void rb_cmperr(VALUE, VALUE)); +/* cont.c */ +VALUE rb_fiber_new(VALUE (*)(ANYARGS), VALUE); +VALUE rb_fiber_resume(VALUE fib, int argc, VALUE *args); +VALUE rb_fiber_yield(int argc, VALUE *args); +VALUE rb_fiber_current(void); +VALUE rb_fiber_alive_p(VALUE); +/* enum.c */ +VALUE rb_enum_values_pack(int, const VALUE*); +/* enumerator.c */ +VALUE rb_enumeratorize(VALUE, VALUE, int, VALUE *); +typedef VALUE rb_enumerator_size_func(VALUE, VALUE, VALUE); +VALUE rb_enumeratorize_with_size(VALUE, VALUE, int, VALUE *, rb_enumerator_size_func *); +#ifndef RUBY_EXPORT +#define rb_enumeratorize_with_size(obj, id, argc, argv, size_fn) \ + rb_enumeratorize_with_size(obj, id, argc, argv, (rb_enumerator_size_func *)(size_fn)) +#endif +#define RETURN_SIZED_ENUMERATOR(obj, argc, argv, size_fn) do { \ + if (!rb_block_given_p()) \ + return rb_enumeratorize_with_size((obj), ID2SYM(rb_frame_this_func()),\ + (argc), (argv), (size_fn)); \ + } while (0) +#define RETURN_ENUMERATOR(obj, argc, argv) RETURN_SIZED_ENUMERATOR(obj, argc, argv, 0) +/* error.c */ +VALUE rb_exc_new(VALUE, const char*, long); +VALUE rb_exc_new_cstr(VALUE, const char*); +VALUE rb_exc_new_str(VALUE, VALUE); +#define rb_exc_new2 rb_exc_new_cstr +#define rb_exc_new3 rb_exc_new_str +PRINTF_ARGS(NORETURN(void rb_loaderror(const char*, ...)), 1, 2); +PRINTF_ARGS(NORETURN(void rb_loaderror_with_path(VALUE path, const char*, ...)), 2, 3); +PRINTF_ARGS(NORETURN(void rb_name_error(ID, const char*, ...)), 2, 3); +PRINTF_ARGS(NORETURN(void rb_name_error_str(VALUE, const char*, ...)), 2, 3); +NORETURN(void rb_invalid_str(const char*, const char*)); +PRINTF_ARGS(void rb_compile_error(const char*, int, const char*, ...), 3, 4); +PRINTF_ARGS(void rb_compile_error_with_enc(const char*, int, void *, const char*, ...), 4, 5); +PRINTF_ARGS(void rb_compile_error_append(const char*, ...), 1, 2); +NORETURN(void rb_error_frozen(const char*)); +void rb_error_untrusted(VALUE); +void rb_check_frozen(VALUE); +void rb_check_trusted(VALUE); +#define rb_check_frozen_internal(obj) do { \ + VALUE frozen_obj = (obj); \ + if (OBJ_FROZEN(frozen_obj)) { \ + rb_error_frozen(rb_obj_classname(frozen_obj)); \ + } \ + } while (0) +#define rb_check_trusted_internal(obj) ((void) 0) +#ifdef __GNUC__ +#define rb_check_frozen(obj) __extension__({rb_check_frozen_internal(obj);}) +#define rb_check_trusted(obj) __extension__({rb_check_trusted_internal(obj);}) +#else +static inline void +rb_check_frozen_inline(VALUE obj) +{ + rb_check_frozen_internal(obj); +} +#define rb_check_frozen(obj) rb_check_frozen_inline(obj) +static inline void +rb_check_trusted_inline(VALUE obj) +{ + rb_check_trusted_internal(obj); +} +#define rb_check_trusted(obj) rb_check_trusted_inline(obj) +#endif +void rb_check_copyable(VALUE obj, VALUE orig); + +#define OBJ_INIT_COPY(obj, orig) \ + ((obj) != (orig) && (rb_obj_init_copy((obj), (orig)), 1)) + +/* eval.c */ +int rb_sourceline(void); +const char *rb_sourcefile(void); +VALUE rb_check_funcall(VALUE, ID, int, const VALUE*); + +NORETURN(void rb_error_arity(int, int, int)); +#define rb_check_arity rb_check_arity /* for ifdef */ +static inline void +rb_check_arity(int argc, int min, int max) +{ + if ((argc < min) || (max != UNLIMITED_ARGUMENTS && argc > max)) + rb_error_arity(argc, min, max); +} + +#if defined(NFDBITS) && defined(HAVE_RB_FD_INIT) +typedef struct { + int maxfd; + fd_set *fdset; +} rb_fdset_t; + +void rb_fd_init(rb_fdset_t *); +void rb_fd_term(rb_fdset_t *); +void rb_fd_zero(rb_fdset_t *); +void rb_fd_set(int, rb_fdset_t *); +void rb_fd_clr(int, rb_fdset_t *); +int rb_fd_isset(int, const rb_fdset_t *); +void rb_fd_copy(rb_fdset_t *, const fd_set *, int); +void rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src); +int rb_fd_select(int, rb_fdset_t *, rb_fdset_t *, rb_fdset_t *, struct timeval *); + +#define rb_fd_ptr(f) ((f)->fdset) +#define rb_fd_max(f) ((f)->maxfd) + +#elif defined(_WIN32) + +typedef struct { + int capa; + fd_set *fdset; +} rb_fdset_t; + +void rb_fd_init(rb_fdset_t *); +void rb_fd_term(rb_fdset_t *); +#define rb_fd_zero(f) ((f)->fdset->fd_count = 0) +void rb_fd_set(int, rb_fdset_t *); +#define rb_fd_clr(n, f) rb_w32_fdclr((n), (f)->fdset) +#define rb_fd_isset(n, f) rb_w32_fdisset((n), (f)->fdset) +#define rb_fd_copy(d, s, n) rb_w32_fd_copy((d), (s), (n)) +void rb_w32_fd_copy(rb_fdset_t *, const fd_set *, int); +#define rb_fd_dup(d, s) rb_w32_fd_dup((d), (s)) +void rb_w32_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src); +#define rb_fd_select(n, rfds, wfds, efds, timeout) rb_w32_select((n), (rfds) ? ((rb_fdset_t*)(rfds))->fdset : NULL, (wfds) ? ((rb_fdset_t*)(wfds))->fdset : NULL, (efds) ? ((rb_fdset_t*)(efds))->fdset: NULL, (timeout)) +#define rb_fd_resize(n, f) ((void)(f)) + +#define rb_fd_ptr(f) ((f)->fdset) +#define rb_fd_max(f) ((f)->fdset->fd_count) + +#else + +typedef fd_set rb_fdset_t; +#define rb_fd_zero(f) FD_ZERO(f) +#define rb_fd_set(n, f) FD_SET((n), (f)) +#define rb_fd_clr(n, f) FD_CLR((n), (f)) +#define rb_fd_isset(n, f) FD_ISSET((n), (f)) +#define rb_fd_copy(d, s, n) (*(d) = *(s)) +#define rb_fd_dup(d, s) (*(d) = *(s)) +#define rb_fd_resize(n, f) ((void)(f)) +#define rb_fd_ptr(f) (f) +#define rb_fd_init(f) FD_ZERO(f) +#define rb_fd_init_copy(d, s) (*(d) = *(s)) +#define rb_fd_term(f) ((void)(f)) +#define rb_fd_max(f) FD_SETSIZE +#define rb_fd_select(n, rfds, wfds, efds, timeout) select((n), (rfds), (wfds), (efds), (timeout)) + +#endif + +NORETURN(void rb_exc_raise(VALUE)); +NORETURN(void rb_exc_fatal(VALUE)); +VALUE rb_f_exit(int,VALUE*); +VALUE rb_f_abort(int,VALUE*); +void rb_remove_method(VALUE, const char*); +void rb_remove_method_id(VALUE, ID); +#define rb_disable_super(klass, name) ((void)0) +#define rb_enable_super(klass, name) ((void)0) +#define HAVE_RB_DEFINE_ALLOC_FUNC 1 +typedef VALUE (*rb_alloc_func_t)(VALUE); +void rb_define_alloc_func(VALUE, rb_alloc_func_t); +void rb_undef_alloc_func(VALUE); +rb_alloc_func_t rb_get_alloc_func(VALUE); +void rb_clear_cache(void); +void rb_clear_constant_cache(void); +void rb_clear_method_cache_by_class(VALUE); +void rb_alias(VALUE, ID, ID); +void rb_attr(VALUE,ID,int,int,int); +int rb_method_boundp(VALUE, ID, int); +int rb_method_basic_definition_p(VALUE, ID); +VALUE rb_eval_cmd(VALUE, VALUE, int); +int rb_obj_respond_to(VALUE, ID, int); +int rb_respond_to(VALUE, ID); +VALUE rb_f_notimplement(int argc, VALUE *argv, VALUE obj); +void rb_interrupt(void); +VALUE rb_apply(VALUE, ID, VALUE); +void rb_backtrace(void); +ID rb_frame_this_func(void); +VALUE rb_obj_instance_eval(int, VALUE*, VALUE); +VALUE rb_obj_instance_exec(int, VALUE*, VALUE); +VALUE rb_mod_module_eval(int, VALUE*, VALUE); +VALUE rb_mod_module_exec(int, VALUE*, VALUE); +void rb_load(VALUE, int); +void rb_load_protect(VALUE, int, int*); +NORETURN(void rb_jump_tag(int)); +int rb_provided(const char*); +int rb_feature_provided(const char *, const char **); +void rb_provide(const char*); +VALUE rb_f_require(VALUE, VALUE); +VALUE rb_require_safe(VALUE, int); +void rb_obj_call_init(VALUE, int, VALUE*); +VALUE rb_class_new_instance(int, VALUE*, VALUE); +VALUE rb_block_proc(void); +VALUE rb_block_lambda(void); +VALUE rb_proc_new(VALUE (*)(ANYARGS/* VALUE yieldarg[, VALUE procarg] */), VALUE); +VALUE rb_obj_is_proc(VALUE); +VALUE rb_proc_call(VALUE, VALUE); +VALUE rb_proc_call_with_block(VALUE, int argc, const VALUE *argv, VALUE); +int rb_proc_arity(VALUE); +VALUE rb_proc_lambda_p(VALUE); +VALUE rb_binding_new(void); +VALUE rb_obj_method(VALUE, VALUE); +VALUE rb_obj_is_method(VALUE); +VALUE rb_method_call(int, VALUE*, VALUE); +VALUE rb_method_call_with_block(int, VALUE *, VALUE, VALUE); +int rb_mod_method_arity(VALUE, ID); +int rb_obj_method_arity(VALUE, ID); +VALUE rb_protect(VALUE (*)(VALUE), VALUE, int*); +void rb_set_end_proc(void (*)(VALUE), VALUE); +void rb_exec_end_proc(void); +void rb_thread_schedule(void); +void rb_thread_wait_fd(int); +int rb_thread_fd_writable(int); +void rb_thread_fd_close(int); +int rb_thread_alone(void); +DEPRECATED(void rb_thread_polling(void)); +void rb_thread_sleep(int); +void rb_thread_sleep_forever(void); +void rb_thread_sleep_deadly(void); +VALUE rb_thread_stop(void); +VALUE rb_thread_wakeup(VALUE); +VALUE rb_thread_wakeup_alive(VALUE); +VALUE rb_thread_run(VALUE); +VALUE rb_thread_kill(VALUE); +VALUE rb_thread_create(VALUE (*)(ANYARGS), void*); +DEPRECATED(int rb_thread_select(int, fd_set *, fd_set *, fd_set *, struct timeval *)); +int rb_thread_fd_select(int, rb_fdset_t *, rb_fdset_t *, rb_fdset_t *, struct timeval *); +void rb_thread_wait_for(struct timeval); +VALUE rb_thread_current(void); +VALUE rb_thread_main(void); +VALUE rb_thread_local_aref(VALUE, ID); +VALUE rb_thread_local_aset(VALUE, ID, VALUE); +void rb_thread_atfork(void); +void rb_thread_atfork_before_exec(void); +VALUE rb_exec_recursive(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE); +VALUE rb_exec_recursive_paired(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE,VALUE); +VALUE rb_exec_recursive_outer(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE); +VALUE rb_exec_recursive_paired_outer(VALUE(*)(VALUE, VALUE, int),VALUE,VALUE,VALUE); +/* dir.c */ +VALUE rb_dir_getwd(void); +/* file.c */ +VALUE rb_file_s_expand_path(int, VALUE *); +VALUE rb_file_expand_path(VALUE, VALUE); +VALUE rb_file_s_absolute_path(int, VALUE *); +VALUE rb_file_absolute_path(VALUE, VALUE); +VALUE rb_file_dirname(VALUE fname); +int rb_find_file_ext_safe(VALUE*, const char* const*, int); +VALUE rb_find_file_safe(VALUE, int); +int rb_find_file_ext(VALUE*, const char* const*); +VALUE rb_find_file(VALUE); +VALUE rb_file_directory_p(VALUE,VALUE); +VALUE rb_str_encode_ospath(VALUE); +int rb_is_absolute_path(const char *); +/* gc.c */ +NORETURN(void rb_memerror(void)); +int rb_during_gc(void); +void rb_gc_mark_locations(VALUE*, VALUE*); +void rb_mark_tbl(struct st_table*); +void rb_mark_set(struct st_table*); +void rb_mark_hash(struct st_table*); +void rb_gc_mark_maybe(VALUE); +void rb_gc_mark(VALUE); +void rb_gc_force_recycle(VALUE); +void rb_gc(void); +void rb_gc_copy_finalizer(VALUE,VALUE); +void rb_gc_finalize_deferred(void); +void rb_gc_call_finalizer_at_exit(void); +VALUE rb_gc_enable(void); +VALUE rb_gc_disable(void); +VALUE rb_gc_start(void); +DEPRECATED(void rb_gc_set_params(void)); +VALUE rb_define_finalizer(VALUE, VALUE); +VALUE rb_undefine_finalizer(VALUE); +size_t rb_gc_count(void); +size_t rb_gc_stat(VALUE); +VALUE rb_gc_latest_gc_info(VALUE); +/* hash.c */ +void st_foreach_safe(struct st_table *, int (*)(ANYARGS), st_data_t); +VALUE rb_check_hash_type(VALUE); +void rb_hash_foreach(VALUE, int (*)(ANYARGS), VALUE); +VALUE rb_hash(VALUE); +VALUE rb_hash_new(void); +VALUE rb_hash_dup(VALUE); +VALUE rb_hash_freeze(VALUE); +VALUE rb_hash_aref(VALUE, VALUE); +VALUE rb_hash_lookup(VALUE, VALUE); +VALUE rb_hash_lookup2(VALUE, VALUE, VALUE); +VALUE rb_hash_fetch(VALUE, VALUE); +VALUE rb_hash_aset(VALUE, VALUE, VALUE); +VALUE rb_hash_clear(VALUE); +VALUE rb_hash_delete_if(VALUE); +VALUE rb_hash_delete(VALUE,VALUE); +VALUE rb_hash_set_ifnone(VALUE hash, VALUE ifnone); +typedef VALUE rb_hash_update_func(VALUE newkey, VALUE oldkey, VALUE value); +VALUE rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func); +struct st_table *rb_hash_tbl(VALUE); +int rb_path_check(const char*); +int rb_env_path_tainted(void); +VALUE rb_env_clear(void); +/* io.c */ +#define rb_defout rb_stdout +RUBY_EXTERN VALUE rb_fs; +RUBY_EXTERN VALUE rb_output_fs; +RUBY_EXTERN VALUE rb_rs; +RUBY_EXTERN VALUE rb_default_rs; +RUBY_EXTERN VALUE rb_output_rs; +VALUE rb_io_write(VALUE, VALUE); +VALUE rb_io_gets(VALUE); +VALUE rb_io_getbyte(VALUE); +VALUE rb_io_ungetc(VALUE, VALUE); +VALUE rb_io_ungetbyte(VALUE, VALUE); +VALUE rb_io_close(VALUE); +VALUE rb_io_flush(VALUE); +VALUE rb_io_eof(VALUE); +VALUE rb_io_binmode(VALUE); +VALUE rb_io_ascii8bit_binmode(VALUE); +VALUE rb_io_addstr(VALUE, VALUE); +VALUE rb_io_printf(int, VALUE*, VALUE); +VALUE rb_io_print(int, VALUE*, VALUE); +VALUE rb_io_puts(int, VALUE*, VALUE); +VALUE rb_io_fdopen(int, int, const char*); +VALUE rb_io_get_io(VALUE); +VALUE rb_file_open(const char*, const char*); +VALUE rb_file_open_str(VALUE, const char*); +VALUE rb_gets(void); +void rb_write_error(const char*); +void rb_write_error2(const char*, long); +void rb_close_before_exec(int lowfd, int maxhint, VALUE noclose_fds); +int rb_pipe(int *pipes); +int rb_reserved_fd_p(int fd); +int rb_cloexec_open(const char *pathname, int flags, mode_t mode); +int rb_cloexec_dup(int oldfd); +int rb_cloexec_dup2(int oldfd, int newfd); +int rb_cloexec_pipe(int fildes[2]); +int rb_cloexec_fcntl_dupfd(int fd, int minfd); +#define RB_RESERVED_FD_P(fd) rb_reserved_fd_p(fd) +void rb_update_max_fd(int fd); +void rb_fd_fix_cloexec(int fd); +/* marshal.c */ +VALUE rb_marshal_dump(VALUE, VALUE); +VALUE rb_marshal_load(VALUE); +void rb_marshal_define_compat(VALUE newclass, VALUE oldclass, VALUE (*dumper)(VALUE), VALUE (*loader)(VALUE, VALUE)); +/* numeric.c */ +NORETURN(void rb_num_zerodiv(void)); +#define RB_NUM_COERCE_FUNCS_NEED_OPID 1 +VALUE rb_num_coerce_bin(VALUE, VALUE, ID); +VALUE rb_num_coerce_cmp(VALUE, VALUE, ID); +VALUE rb_num_coerce_relop(VALUE, VALUE, ID); +VALUE rb_num_coerce_bit(VALUE, VALUE, ID); +VALUE rb_num2fix(VALUE); +VALUE rb_fix2str(VALUE, int); +VALUE rb_dbl_cmp(double, double); +/* object.c */ +int rb_eql(VALUE, VALUE); +VALUE rb_any_to_s(VALUE); +VALUE rb_inspect(VALUE); +VALUE rb_obj_is_instance_of(VALUE, VALUE); +VALUE rb_obj_is_kind_of(VALUE, VALUE); +VALUE rb_obj_alloc(VALUE); +VALUE rb_obj_clone(VALUE); +VALUE rb_obj_dup(VALUE); +VALUE rb_obj_init_copy(VALUE,VALUE); +VALUE rb_obj_taint(VALUE); +VALUE rb_obj_tainted(VALUE); +VALUE rb_obj_untaint(VALUE); +VALUE rb_obj_untrust(VALUE); +VALUE rb_obj_untrusted(VALUE); +VALUE rb_obj_trust(VALUE); +VALUE rb_obj_freeze(VALUE); +VALUE rb_obj_frozen_p(VALUE); +VALUE rb_obj_id(VALUE); +VALUE rb_obj_class(VALUE); +VALUE rb_class_real(VALUE); +VALUE rb_class_inherited_p(VALUE, VALUE); +VALUE rb_class_superclass(VALUE); +VALUE rb_class_get_superclass(VALUE); +VALUE rb_convert_type(VALUE,int,const char*,const char*); +VALUE rb_check_convert_type(VALUE,int,const char*,const char*); +VALUE rb_check_to_integer(VALUE, const char *); +VALUE rb_check_to_float(VALUE); +VALUE rb_to_int(VALUE); +VALUE rb_check_to_int(VALUE); +VALUE rb_Integer(VALUE); +VALUE rb_to_float(VALUE); +VALUE rb_Float(VALUE); +VALUE rb_String(VALUE); +VALUE rb_Array(VALUE); +VALUE rb_Hash(VALUE); +double rb_cstr_to_dbl(const char*, int); +double rb_str_to_dbl(VALUE, int); +/* parse.y */ +RUBY_EXTERN int ruby_sourceline; +RUBY_EXTERN char *ruby_sourcefile; +ID rb_id_attrset(ID); +int rb_is_const_id(ID); +int rb_is_global_id(ID); +int rb_is_instance_id(ID); +int rb_is_attrset_id(ID); +int rb_is_class_id(ID); +int rb_is_local_id(ID); +int rb_is_junk_id(ID); +int rb_symname_p(const char*); +int rb_sym_interned_p(VALUE); +VALUE rb_backref_get(void); +void rb_backref_set(VALUE); +VALUE rb_lastline_get(void); +void rb_lastline_set(VALUE); +VALUE rb_sym_all_symbols(void); +/* process.c */ +void rb_last_status_set(int status, rb_pid_t pid); +VALUE rb_last_status_get(void); +struct rb_exec_arg { + VALUE execarg_obj; +}; +DEPRECATED(int rb_proc_exec_n(int, VALUE*, const char*)); +int rb_proc_exec(const char*); +DEPRECATED(VALUE rb_exec_arg_init(int argc, VALUE *argv, int accept_shell, struct rb_exec_arg *e)); +DEPRECATED(int rb_exec_arg_addopt(struct rb_exec_arg *e, VALUE key, VALUE val)); +DEPRECATED(void rb_exec_arg_fixup(struct rb_exec_arg *e)); +DEPRECATED(int rb_run_exec_options(const struct rb_exec_arg *e, struct rb_exec_arg *s)); +DEPRECATED(int rb_run_exec_options_err(const struct rb_exec_arg *e, struct rb_exec_arg *s, char*, size_t)); +DEPRECATED(int rb_exec(const struct rb_exec_arg*)); +DEPRECATED(int rb_exec_err(const struct rb_exec_arg*, char*, size_t)); +DEPRECATED(rb_pid_t rb_fork(int*, int (*)(void*), void*, VALUE)); +DEPRECATED(rb_pid_t rb_fork_err(int*, int (*)(void*, char*, size_t), void*, VALUE, char*, size_t)); +VALUE rb_f_exec(int,VALUE*); +rb_pid_t rb_waitpid(rb_pid_t pid, int *status, int flags); +void rb_syswait(rb_pid_t pid); +rb_pid_t rb_spawn(int, VALUE*); +rb_pid_t rb_spawn_err(int, VALUE*, char*, size_t); +VALUE rb_proc_times(VALUE); +VALUE rb_detach_process(rb_pid_t pid); +/* range.c */ +VALUE rb_range_new(VALUE, VALUE, int); +VALUE rb_range_beg_len(VALUE, long*, long*, long, int); +int rb_range_values(VALUE range, VALUE *begp, VALUE *endp, int *exclp); +/* random.c */ +unsigned int rb_genrand_int32(void); +double rb_genrand_real(void); +void rb_reset_random_seed(void); +VALUE rb_random_bytes(VALUE rnd, long n); +VALUE rb_random_int(VALUE rnd, VALUE max); +unsigned int rb_random_int32(VALUE rnd); +double rb_random_real(VALUE rnd); +unsigned long rb_random_ulong_limited(VALUE rnd, unsigned long limit); +unsigned long rb_genrand_ulong_limited(unsigned long i); +/* re.c */ +#define rb_memcmp memcmp +int rb_memcicmp(const void*,const void*,long); +void rb_match_busy(VALUE); +VALUE rb_reg_nth_defined(int, VALUE); +VALUE rb_reg_nth_match(int, VALUE); +int rb_reg_backref_number(VALUE match, VALUE backref); +VALUE rb_reg_last_match(VALUE); +VALUE rb_reg_match_pre(VALUE); +VALUE rb_reg_match_post(VALUE); +VALUE rb_reg_match_last(VALUE); +#define HAVE_RB_REG_NEW_STR 1 +VALUE rb_reg_new_str(VALUE, int); +VALUE rb_reg_new(const char *, long, int); +VALUE rb_reg_alloc(void); +VALUE rb_reg_init_str(VALUE re, VALUE s, int options); +VALUE rb_reg_match(VALUE, VALUE); +VALUE rb_reg_match2(VALUE); +int rb_reg_options(VALUE); +/* ruby.c */ +#define rb_argv rb_get_argv() +RUBY_EXTERN VALUE rb_argv0; +VALUE rb_get_argv(void); +void *rb_load_file(const char*); +void *rb_load_file_str(VALUE); +/* signal.c */ +VALUE rb_f_kill(int, VALUE*); +#ifdef POSIX_SIGNAL +#define posix_signal ruby_posix_signal +RETSIGTYPE (*posix_signal(int, RETSIGTYPE (*)(int)))(int); +#endif +void rb_trap_exit(void); +void rb_trap_exec(void); +const char *ruby_signal_name(int); +void ruby_default_signal(int); +/* sprintf.c */ +VALUE rb_f_sprintf(int, const VALUE*); +PRINTF_ARGS(VALUE rb_sprintf(const char*, ...), 1, 2); +VALUE rb_vsprintf(const char*, va_list); +PRINTF_ARGS(VALUE rb_str_catf(VALUE, const char*, ...), 2, 3); +VALUE rb_str_vcatf(VALUE, const char*, va_list); +VALUE rb_str_format(int, const VALUE *, VALUE); +/* string.c */ +VALUE rb_str_new(const char*, long); +VALUE rb_str_new_cstr(const char*); +VALUE rb_str_new_shared(VALUE); +VALUE rb_str_new_frozen(VALUE); +VALUE rb_str_new_with_class(VALUE, const char*, long); +VALUE rb_tainted_str_new_cstr(const char*); +VALUE rb_tainted_str_new(const char*, long); +VALUE rb_external_str_new(const char*, long); +VALUE rb_external_str_new_cstr(const char*); +VALUE rb_locale_str_new(const char*, long); +VALUE rb_locale_str_new_cstr(const char*); +VALUE rb_filesystem_str_new(const char*, long); +VALUE rb_filesystem_str_new_cstr(const char*); +VALUE rb_str_buf_new(long); +VALUE rb_str_buf_new_cstr(const char*); +VALUE rb_str_buf_new2(const char*); +VALUE rb_str_tmp_new(long); +VALUE rb_usascii_str_new(const char*, long); +VALUE rb_usascii_str_new_cstr(const char*); +void rb_str_free(VALUE); +void rb_str_shared_replace(VALUE, VALUE); +VALUE rb_str_buf_append(VALUE, VALUE); +VALUE rb_str_buf_cat(VALUE, const char*, long); +VALUE rb_str_buf_cat2(VALUE, const char*); +VALUE rb_str_buf_cat_ascii(VALUE, const char*); +VALUE rb_obj_as_string(VALUE); +VALUE rb_check_string_type(VALUE); +void rb_must_asciicompat(VALUE); +VALUE rb_str_dup(VALUE); +VALUE rb_str_resurrect(VALUE str); +VALUE rb_str_locktmp(VALUE); +VALUE rb_str_unlocktmp(VALUE); +VALUE rb_str_dup_frozen(VALUE); +#define rb_str_dup_frozen rb_str_new_frozen +VALUE rb_str_plus(VALUE, VALUE); +VALUE rb_str_times(VALUE, VALUE); +long rb_str_sublen(VALUE, long); +VALUE rb_str_substr(VALUE, long, long); +VALUE rb_str_subseq(VALUE, long, long); +char *rb_str_subpos(VALUE, long, long*); +void rb_str_modify(VALUE); +void rb_str_modify_expand(VALUE, long); +VALUE rb_str_freeze(VALUE); +void rb_str_set_len(VALUE, long); +VALUE rb_str_resize(VALUE, long); +VALUE rb_str_cat(VALUE, const char*, long); +VALUE rb_str_cat2(VALUE, const char*); +VALUE rb_str_append(VALUE, VALUE); +VALUE rb_str_concat(VALUE, VALUE); +st_index_t rb_memhash(const void *ptr, long len); +st_index_t rb_hash_start(st_index_t); +st_index_t rb_hash_uint32(st_index_t, uint32_t); +st_index_t rb_hash_uint(st_index_t, st_index_t); +st_index_t rb_hash_end(st_index_t); +#define rb_hash_uint32(h, i) st_hash_uint32((h), (i)) +#define rb_hash_uint(h, i) st_hash_uint((h), (i)) +#define rb_hash_end(h) st_hash_end(h) +st_index_t rb_str_hash(VALUE); +int rb_str_hash_cmp(VALUE,VALUE); +int rb_str_comparable(VALUE, VALUE); +int rb_str_cmp(VALUE, VALUE); +VALUE rb_str_equal(VALUE str1, VALUE str2); +VALUE rb_str_drop_bytes(VALUE, long); +void rb_str_update(VALUE, long, long, VALUE); +VALUE rb_str_replace(VALUE, VALUE); +VALUE rb_str_inspect(VALUE); +VALUE rb_str_dump(VALUE); +VALUE rb_str_split(VALUE, const char*); +void rb_str_associate(VALUE, VALUE); +VALUE rb_str_associated(VALUE); +void rb_str_setter(VALUE, ID, VALUE*); +VALUE rb_str_intern(VALUE); +VALUE rb_sym_to_s(VALUE); +long rb_str_strlen(VALUE); +VALUE rb_str_length(VALUE); +long rb_str_offset(VALUE, long); +size_t rb_str_capacity(VALUE); +VALUE rb_str_ellipsize(VALUE, long); +VALUE rb_str_scrub(VALUE, VALUE); +#if defined(__GNUC__) && !defined(__PCC__) +#define rb_str_new_cstr(str) __extension__ ( \ +{ \ + (__builtin_constant_p(str)) ? \ + rb_str_new((str), (long)strlen(str)) : \ + rb_str_new_cstr(str); \ +}) +#define rb_tainted_str_new_cstr(str) __extension__ ( \ +{ \ + (__builtin_constant_p(str)) ? \ + rb_tainted_str_new((str), (long)strlen(str)) : \ + rb_tainted_str_new_cstr(str); \ +}) +#define rb_usascii_str_new_cstr(str) __extension__ ( \ +{ \ + (__builtin_constant_p(str)) ? \ + rb_usascii_str_new((str), (long)strlen(str)) : \ + rb_usascii_str_new_cstr(str); \ +}) +#define rb_external_str_new_cstr(str) __extension__ ( \ +{ \ + (__builtin_constant_p(str)) ? \ + rb_external_str_new((str), (long)strlen(str)) : \ + rb_external_str_new_cstr(str); \ +}) +#define rb_locale_str_new_cstr(str) __extension__ ( \ +{ \ + (__builtin_constant_p(str)) ? \ + rb_locale_str_new((str), (long)strlen(str)) : \ + rb_locale_str_new_cstr(str); \ +}) +#define rb_str_buf_new_cstr(str) __extension__ ( \ +{ \ + (__builtin_constant_p(str)) ? \ + rb_str_buf_cat(rb_str_buf_new((long)strlen(str)), \ + (str), (long)strlen(str)) : \ + rb_str_buf_new_cstr(str); \ +}) +#define rb_str_buf_cat2(str, ptr) __extension__ ( \ +{ \ + (__builtin_constant_p(ptr)) ? \ + rb_str_buf_cat((str), (ptr), (long)strlen(ptr)) : \ + rb_str_buf_cat2((str), (ptr)); \ +}) +#define rb_str_cat2(str, ptr) __extension__ ( \ +{ \ + (__builtin_constant_p(ptr)) ? \ + rb_str_cat((str), (ptr), (long)strlen(ptr)) : \ + rb_str_cat2((str), (ptr)); \ +}) +#define rb_exc_new_cstr(klass, ptr) __extension__ ( \ +{ \ + (__builtin_constant_p(ptr)) ? \ + rb_exc_new((klass), (ptr), (long)strlen(ptr)) : \ + rb_exc_new_cstr((klass), (ptr)); \ +}) +#endif +#define rb_str_new2 rb_str_new_cstr +#define rb_str_new3 rb_str_new_shared +#define rb_str_new4 rb_str_new_frozen +#define rb_str_new5 rb_str_new_with_class +#define rb_tainted_str_new2 rb_tainted_str_new_cstr +#define rb_str_buf_new2 rb_str_buf_new_cstr +#define rb_usascii_str_new2 rb_usascii_str_new_cstr +/* struct.c */ +VALUE rb_struct_new(VALUE, ...); +VALUE rb_struct_define(const char*, ...); +VALUE rb_struct_define_under(VALUE, const char*, ...); +VALUE rb_struct_alloc(VALUE, VALUE); +VALUE rb_struct_initialize(VALUE, VALUE); +VALUE rb_struct_aref(VALUE, VALUE); +VALUE rb_struct_aset(VALUE, VALUE, VALUE); +VALUE rb_struct_getmember(VALUE, ID); +DEPRECATED(VALUE rb_struct_iv_get(VALUE, const char*)); +VALUE rb_struct_s_members(VALUE); +VALUE rb_struct_members(VALUE); +VALUE rb_struct_alloc_noinit(VALUE); +VALUE rb_struct_define_without_accessor(const char *, VALUE, rb_alloc_func_t, ...); +VALUE rb_struct_define_without_accessor_under(VALUE outer, const char *class_name, VALUE super, rb_alloc_func_t alloc, ...); + +/* thread.c */ +typedef void rb_unblock_function_t(void *); +typedef VALUE rb_blocking_function_t(void *); +void rb_thread_check_ints(void); +int rb_thread_interrupted(VALUE thval); + +/* Use rb_thread_call_without_gvl family instead. */ +DEPRECATED(VALUE rb_thread_blocking_region(rb_blocking_function_t *func, void *data1, + rb_unblock_function_t *ubf, void *data2)); +#define RUBY_UBF_IO ((rb_unblock_function_t *)-1) +#define RUBY_UBF_PROCESS ((rb_unblock_function_t *)-1) +VALUE rb_mutex_new(void); +VALUE rb_mutex_locked_p(VALUE mutex); +VALUE rb_mutex_trylock(VALUE mutex); +VALUE rb_mutex_lock(VALUE mutex); +VALUE rb_mutex_unlock(VALUE mutex); +VALUE rb_mutex_sleep(VALUE self, VALUE timeout); +VALUE rb_mutex_synchronize(VALUE mutex, VALUE (*func)(VALUE arg), VALUE arg); +/* time.c */ +VALUE rb_time_new(time_t, long); +VALUE rb_time_nano_new(time_t, long); +VALUE rb_time_num_new(VALUE, VALUE); +struct timeval rb_time_interval(VALUE num); +struct timeval rb_time_timeval(VALUE time); +struct timespec rb_time_timespec(VALUE time); +/* variable.c */ +VALUE rb_mod_name(VALUE); +VALUE rb_class_path(VALUE); +VALUE rb_class_path_cached(VALUE); +void rb_set_class_path(VALUE, VALUE, const char*); +void rb_set_class_path_string(VALUE, VALUE, VALUE); +VALUE rb_path_to_class(VALUE); +VALUE rb_path2class(const char*); +void rb_name_class(VALUE, ID); +VALUE rb_class_name(VALUE); +void rb_autoload(VALUE, ID, const char*); +VALUE rb_autoload_load(VALUE, ID); +VALUE rb_autoload_p(VALUE, ID); +VALUE rb_f_trace_var(int, VALUE*); +VALUE rb_f_untrace_var(int, VALUE*); +VALUE rb_f_global_variables(void); +void rb_alias_variable(ID, ID); +struct st_table* rb_generic_ivar_table(VALUE); +void rb_copy_generic_ivar(VALUE,VALUE); +void rb_free_generic_ivar(VALUE); +VALUE rb_ivar_get(VALUE, ID); +VALUE rb_ivar_set(VALUE, ID, VALUE); +VALUE rb_ivar_defined(VALUE, ID); +void rb_ivar_foreach(VALUE, int (*)(ANYARGS), st_data_t); +st_index_t rb_ivar_count(VALUE); +VALUE rb_attr_get(VALUE, ID); +VALUE rb_obj_instance_variables(VALUE); +VALUE rb_obj_remove_instance_variable(VALUE, VALUE); +void *rb_mod_const_at(VALUE, void*); +void *rb_mod_const_of(VALUE, void*); +VALUE rb_const_list(void*); +VALUE rb_mod_constants(int, VALUE *, VALUE); +VALUE rb_mod_remove_const(VALUE, VALUE); +int rb_const_defined(VALUE, ID); +int rb_const_defined_at(VALUE, ID); +int rb_const_defined_from(VALUE, ID); +VALUE rb_const_get(VALUE, ID); +VALUE rb_const_get_at(VALUE, ID); +VALUE rb_const_get_from(VALUE, ID); +void rb_const_set(VALUE, ID, VALUE); +VALUE rb_const_remove(VALUE, ID); +VALUE rb_mod_const_missing(VALUE,VALUE); +VALUE rb_cvar_defined(VALUE, ID); +void rb_cvar_set(VALUE, ID, VALUE); +VALUE rb_cvar_get(VALUE, ID); +void rb_cv_set(VALUE, const char*, VALUE); +VALUE rb_cv_get(VALUE, const char*); +void rb_define_class_variable(VALUE, const char*, VALUE); +VALUE rb_mod_class_variables(int, VALUE*, VALUE); +VALUE rb_mod_remove_cvar(VALUE, VALUE); + +ID rb_frame_callee(void); +VALUE rb_str_succ(VALUE); +VALUE rb_time_succ(VALUE); +int rb_frame_method_id_and_class(ID *idp, VALUE *klassp); +VALUE rb_make_backtrace(void); +VALUE rb_make_exception(int, VALUE*); + +/* deprecated */ +DEPRECATED(void rb_frame_pop(void)); + + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_INTERN_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/io.h b/ruby/include/ruby-2.1.0/ruby/io.h new file mode 100644 index 0000000..1034938 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/io.h @@ -0,0 +1,214 @@ +/********************************************************************** + + rubyio.h - + + $Author: nobu $ + created at: Fri Nov 12 16:47:09 JST 1993 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +**********************************************************************/ + +#ifndef RUBY_IO_H +#define RUBY_IO_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#include +#include +#include "ruby/encoding.h" + +#if defined(HAVE_STDIO_EXT_H) +#include +#endif + +#include "ruby/config.h" +#if defined(HAVE_POLL) +# ifdef _AIX +# define reqevents events +# define rtnevents revents +# endif +# include +# ifdef _AIX +# undef reqevents +# undef rtnevents +# undef events +# undef revents +# endif +# define RB_WAITFD_IN POLLIN +# define RB_WAITFD_PRI POLLPRI +# define RB_WAITFD_OUT POLLOUT +#else +# define RB_WAITFD_IN 0x001 +# define RB_WAITFD_PRI 0x002 +# define RB_WAITFD_OUT 0x004 +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +typedef struct { + char *ptr; /* off + len <= capa */ + int off; + int len; + int capa; +} rb_io_buffer_t; + +typedef struct rb_io_t { + int fd; /* file descriptor */ + FILE *stdio_file; /* stdio ptr for read/write if available */ + int mode; /* mode flags: FMODE_XXXs */ + rb_pid_t pid; /* child's pid (for pipes) */ + int lineno; /* number of lines read */ + VALUE pathv; /* pathname for file */ + void (*finalize)(struct rb_io_t*,int); /* finalize proc */ + + rb_io_buffer_t wbuf, rbuf; + + VALUE tied_io_for_writing; + + /* + * enc enc2 read action write action + * NULL NULL force_encoding(default_external) write the byte sequence of str + * e1 NULL force_encoding(e1) convert str.encoding to e1 + * e1 e2 convert from e2 to e1 convert str.encoding to e2 + */ + struct rb_io_enc_t { + rb_encoding *enc; + rb_encoding *enc2; + int ecflags; + VALUE ecopts; + } encs; + + rb_econv_t *readconv; + rb_io_buffer_t cbuf; + + rb_econv_t *writeconv; + VALUE writeconv_asciicompat; + int writeconv_pre_ecflags; + VALUE writeconv_pre_ecopts; + int writeconv_initialized; + + VALUE write_lock; +} rb_io_t; + +#define HAVE_RB_IO_T 1 + +#define FMODE_READABLE 0x00000001 +#define FMODE_WRITABLE 0x00000002 +#define FMODE_READWRITE (FMODE_READABLE|FMODE_WRITABLE) +#define FMODE_BINMODE 0x00000004 +#define FMODE_SYNC 0x00000008 +#define FMODE_TTY 0x00000010 +#define FMODE_DUPLEX 0x00000020 +#define FMODE_APPEND 0x00000040 +#define FMODE_CREATE 0x00000080 +/* #define FMODE_NOREVLOOKUP 0x00000100 */ +#define FMODE_WSPLIT 0x00000200 +#define FMODE_WSPLIT_INITIALIZED 0x00000400 +#define FMODE_TRUNC 0x00000800 +#define FMODE_TEXTMODE 0x00001000 +/* #define FMODE_PREP 0x00010000 */ +#define FMODE_SETENC_BY_BOM 0x00100000 + +#define GetOpenFile(obj,fp) rb_io_check_closed((fp) = RFILE(rb_io_taint_check(obj))->fptr) + +#define RB_IO_BUFFER_INIT(buf) do {\ + (buf).ptr = NULL;\ + (buf).off = 0;\ + (buf).len = 0;\ + (buf).capa = 0;\ +} while (0) + +#define MakeOpenFile(obj, fp) do {\ + if (RFILE(obj)->fptr) {\ + rb_io_close(obj);\ + rb_io_fptr_finalize(RFILE(obj)->fptr);\ + RFILE(obj)->fptr = 0;\ + }\ + (fp) = 0;\ + RB_IO_FPTR_NEW(fp);\ + RFILE(obj)->fptr = (fp);\ +} while (0) + +#define RB_IO_FPTR_NEW(fp) do {\ + (fp) = ALLOC(rb_io_t);\ + (fp)->fd = -1;\ + (fp)->stdio_file = NULL;\ + (fp)->mode = 0;\ + (fp)->pid = 0;\ + (fp)->lineno = 0;\ + (fp)->pathv = Qnil;\ + (fp)->finalize = 0;\ + RB_IO_BUFFER_INIT((fp)->wbuf);\ + RB_IO_BUFFER_INIT((fp)->rbuf);\ + RB_IO_BUFFER_INIT((fp)->cbuf);\ + (fp)->readconv = NULL;\ + (fp)->writeconv = NULL;\ + (fp)->writeconv_asciicompat = Qnil;\ + (fp)->writeconv_pre_ecflags = 0;\ + (fp)->writeconv_pre_ecopts = Qnil;\ + (fp)->writeconv_initialized = 0;\ + (fp)->tied_io_for_writing = 0;\ + (fp)->encs.enc = NULL;\ + (fp)->encs.enc2 = NULL;\ + (fp)->encs.ecflags = 0;\ + (fp)->encs.ecopts = Qnil;\ + (fp)->write_lock = 0;\ +} while (0) + +FILE *rb_io_stdio_file(rb_io_t *fptr); + +FILE *rb_fdopen(int, const char*); +int rb_io_modestr_fmode(const char *modestr); +int rb_io_modestr_oflags(const char *modestr); +int rb_io_oflags_fmode(int oflags); +void rb_io_check_writable(rb_io_t*); +void rb_io_check_readable(rb_io_t*); +void rb_io_check_char_readable(rb_io_t *fptr); +void rb_io_check_byte_readable(rb_io_t *fptr); +int rb_io_fptr_finalize(rb_io_t*); +void rb_io_synchronized(rb_io_t*); +void rb_io_check_initialized(rb_io_t*); +void rb_io_check_closed(rb_io_t*); +VALUE rb_io_get_io(VALUE io); +VALUE rb_io_check_io(VALUE io); +VALUE rb_io_get_write_io(VALUE io); +VALUE rb_io_set_write_io(VALUE io, VALUE w); +int rb_io_wait_readable(int); +int rb_io_wait_writable(int); +int rb_wait_for_single_fd(int fd, int events, struct timeval *tv); +void rb_io_set_nonblock(rb_io_t *fptr); +int rb_io_extract_encoding_option(VALUE opt, rb_encoding **enc_p, rb_encoding **enc2_p, int *fmode_p); +ssize_t rb_io_bufwrite(VALUE io, const void *buf, size_t size); + +/* compatibility for ruby 1.8 and older */ +#define rb_io_mode_flags(modestr) rb_io_modestr_fmode(modestr) +#define rb_io_modenum_flags(oflags) rb_io_oflags_fmode(oflags) + +VALUE rb_io_taint_check(VALUE); +NORETURN(void rb_eof_error(void)); + +void rb_io_read_check(rb_io_t*); +int rb_io_read_pending(rb_io_t*); +DEPRECATED(void rb_read_check(FILE*)); + +struct stat; +VALUE rb_stat_new(const struct stat *); + +/* gc.c */ + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_IO_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/missing.h b/ruby/include/ruby-2.1.0/ruby/missing.h new file mode 100644 index 0000000..abd4b99 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/missing.h @@ -0,0 +1,244 @@ +/************************************************ + + missing.h - prototype for *.c in ./missing, and + for missing timeval struct + + $Author: nobu $ + created at: Sat May 11 23:46:03 JST 2002 + +************************************************/ + +#ifndef RUBY_MISSING_H +#define RUBY_MISSING_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#include "ruby/config.h" +#include +#include /* for INFINITY and NAN */ +#ifdef RUBY_EXTCONF_H +#include RUBY_EXTCONF_H +#endif + +#if !defined(HAVE_STRUCT_TIMEVAL) || !defined(HAVE_STRUCT_TIMESPEC) +#if defined(HAVE_TIME_H) +# include +#endif +#if defined(HAVE_SYS_TIME_H) +# include +#endif +#endif + +#ifndef RUBY_SYMBOL_EXPORT_BEGIN +# define RUBY_SYMBOL_EXPORT_BEGIN /* begin */ +# define RUBY_SYMBOL_EXPORT_END /* end */ +#endif + +#if !defined(HAVE_STRUCT_TIMEVAL) +struct timeval { + time_t tv_sec; /* seconds */ + long tv_usec; /* microseconds */ +}; +#endif /* HAVE_STRUCT_TIMEVAL */ + +#if !defined(HAVE_STRUCT_TIMESPEC) +struct timespec { + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; +#endif + +#if !defined(HAVE_STRUCT_TIMEZONE) +struct timezone { + int tz_minuteswest; + int tz_dsttime; +}; +#endif + +#ifdef RUBY_EXPORT +#undef RUBY_EXTERN +#endif +#ifndef RUBY_EXTERN +#define RUBY_EXTERN extern +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +#ifndef HAVE_ACOSH +RUBY_EXTERN double acosh(double); +RUBY_EXTERN double asinh(double); +RUBY_EXTERN double atanh(double); +#endif + +#ifndef HAVE_CRYPT +RUBY_EXTERN char *crypt(const char *, const char *); +#endif + +#ifndef HAVE_DUP2 +RUBY_EXTERN int dup2(int, int); +#endif + +#ifndef HAVE_EACCESS +RUBY_EXTERN int eaccess(const char*, int); +#endif + +#ifndef HAVE_ROUND +RUBY_EXTERN double round(double); /* numeric.c */ +#endif + +#ifndef HAVE_FINITE +RUBY_EXTERN int finite(double); +#endif + +#ifndef HAVE_FLOCK +RUBY_EXTERN int flock(int, int); +#endif + +/* +#ifndef HAVE_FREXP +RUBY_EXTERN double frexp(double, int *); +#endif +*/ + +#ifndef HAVE_HYPOT +RUBY_EXTERN double hypot(double, double); +#endif + +#ifndef HAVE_ERF +RUBY_EXTERN double erf(double); +RUBY_EXTERN double erfc(double); +#endif + +#ifndef HAVE_TGAMMA +RUBY_EXTERN double tgamma(double); +#endif + +#ifndef HAVE_LGAMMA_R +RUBY_EXTERN double lgamma_r(double, int *); +#endif + +#ifndef HAVE_CBRT +RUBY_EXTERN double cbrt(double); +#endif + +#if !defined(INFINITY) || !defined(NAN) +union bytesequence4_or_float { + unsigned char bytesequence[4]; + float float_value; +}; +#endif + +#ifdef INFINITY +# define HAVE_INFINITY +#else +/** @internal */ +RUBY_EXTERN const union bytesequence4_or_float rb_infinity; +# define INFINITY (rb_infinity.float_value) +#endif + +#ifdef NAN +# define HAVE_NAN +#else +/** @internal */ +RUBY_EXTERN const union bytesequence4_or_float rb_nan; +# define NAN (rb_nan.float_value) +#endif + +#ifndef isinf +# ifndef HAVE_ISINF +# if defined(HAVE_FINITE) && defined(HAVE_ISNAN) +# ifdef HAVE_IEEEFP_H +# include +# endif +# define isinf(x) (!finite(x) && !isnan(x)) +# else +RUBY_EXTERN int isinf(double); +# endif +# endif +#endif + +#ifndef isnan +# ifndef HAVE_ISNAN +RUBY_EXTERN int isnan(double); +# endif +#endif + +/* +#ifndef HAVE_MEMCMP +RUBY_EXTERN int memcmp(const void *, const void *, size_t); +#endif +*/ + +#ifndef HAVE_MEMMOVE +RUBY_EXTERN void *memmove(void *, const void *, size_t); +#endif + +/* +#ifndef HAVE_MODF +RUBY_EXTERN double modf(double, double *); +#endif +*/ + +#ifndef HAVE_STRCHR +RUBY_EXTERN char *strchr(const char *, int); +RUBY_EXTERN char *strrchr(const char *, int); +#endif + +#ifndef HAVE_STRERROR +RUBY_EXTERN char *strerror(int); +#endif + +#ifndef HAVE_STRSTR +RUBY_EXTERN char *strstr(const char *, const char *); +#endif + +/* +#ifndef HAVE_STRTOL +RUBY_EXTERN long strtol(const char *, char **, int); +#endif +*/ + +#ifndef HAVE_STRLCPY +RUBY_EXTERN size_t strlcpy(char *, const char*, size_t); +#endif + +#ifndef HAVE_STRLCAT +RUBY_EXTERN size_t strlcat(char *, const char*, size_t); +#endif + +#ifndef HAVE_SIGNBIT +RUBY_EXTERN int signbit(double x); +#endif + +#ifndef HAVE_FFS +RUBY_EXTERN int ffs(int); +#endif + +#ifdef BROKEN_CLOSE +#include +#include +RUBY_EXTERN int ruby_getpeername(int, struct sockaddr *, socklen_t *); +RUBY_EXTERN int ruby_getsockname(int, struct sockaddr *, socklen_t *); +RUBY_EXTERN int ruby_shutdown(int, int); +RUBY_EXTERN int ruby_close(int); +#endif + +#ifndef HAVE_SETPROCTITLE +RUBY_EXTERN void setproctitle(const char *fmt, ...); +#endif + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_MISSING_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/oniguruma.h b/ruby/include/ruby-2.1.0/ruby/oniguruma.h new file mode 100644 index 0000000..d533a05 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/oniguruma.h @@ -0,0 +1,837 @@ +#ifndef ONIGURUMA_H +#define ONIGURUMA_H +/********************************************************************** + oniguruma.h - Onigmo (Oniguruma-mod) (regular expression library) +**********************************************************************/ +/*- + * Copyright (c) 2002-2009 K.Kosako + * Copyright (c) 2011-2013 K.Takata + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#define ONIGURUMA +#define ONIGURUMA_VERSION_MAJOR 5 +#define ONIGURUMA_VERSION_MINOR 13 +#define ONIGURUMA_VERSION_TEENY 5 + +#ifdef __cplusplus +# ifndef HAVE_PROTOTYPES +# define HAVE_PROTOTYPES 1 +# endif +# ifndef HAVE_STDARG_PROTOTYPES +# define HAVE_STDARG_PROTOTYPES 1 +# endif +#endif + +/* escape Mac OS X/Xcode 2.4/gcc 4.0.1 problem */ +#if defined(__APPLE__) && defined(__GNUC__) && __GNUC__ >= 4 +# ifndef HAVE_STDARG_PROTOTYPES +# define HAVE_STDARG_PROTOTYPES 1 +# endif +#endif + +#ifdef HAVE_STDARG_H +# ifndef HAVE_STDARG_PROTOTYPES +# define HAVE_STDARG_PROTOTYPES 1 +# endif +#endif + +#ifndef P_ +#if defined(__STDC__) || defined(_WIN32) +# define P_(args) args +#else +# define P_(args) () +#endif +#endif + +#ifndef PV_ +#ifdef HAVE_STDARG_PROTOTYPES +# define PV_(args) args +#else +# define PV_(args) () +#endif +#endif + +#ifndef ONIG_EXTERN +#ifdef RUBY_EXTERN +#define ONIG_EXTERN RUBY_EXTERN +#else +#if defined(_WIN32) && !defined(__GNUC__) +#if defined(EXPORT) || defined(RUBY_EXPORT) +#define ONIG_EXTERN extern __declspec(dllexport) +#else +#define ONIG_EXTERN extern __declspec(dllimport) +#endif +#endif +#endif +#endif + +#ifndef ONIG_EXTERN +#define ONIG_EXTERN extern +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +#include /* for size_t */ + +/* PART: character encoding */ + +#ifndef ONIG_ESCAPE_UCHAR_COLLISION +#define UChar OnigUChar +#endif + +typedef unsigned char OnigUChar; +typedef unsigned int OnigCodePoint; +typedef unsigned int OnigCtype; +typedef size_t OnigDistance; +typedef ptrdiff_t OnigPosition; + +#define ONIG_INFINITE_DISTANCE ~((OnigDistance )0) + +typedef unsigned int OnigCaseFoldType; /* case fold flag */ + +ONIG_EXTERN OnigCaseFoldType OnigDefaultCaseFoldFlag; + +/* #define ONIGENC_CASE_FOLD_HIRAGANA_KATAKANA (1<<1) */ +/* #define ONIGENC_CASE_FOLD_KATAKANA_WIDTH (1<<2) */ +#define ONIGENC_CASE_FOLD_TURKISH_AZERI (1<<20) +#define INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR (1<<30) + +#define ONIGENC_CASE_FOLD_MIN INTERNAL_ONIGENC_CASE_FOLD_MULTI_CHAR +#define ONIGENC_CASE_FOLD_DEFAULT OnigDefaultCaseFoldFlag + + +#define ONIGENC_MAX_COMP_CASE_FOLD_CODE_LEN 3 +#define ONIGENC_GET_CASE_FOLD_CODES_MAX_NUM 13 +/* 13 => Unicode:0x1ffc */ + +/* code range */ +#define ONIGENC_CODE_RANGE_NUM(range) ((int )range[0]) +#define ONIGENC_CODE_RANGE_FROM(range,i) range[((i)*2) + 1] +#define ONIGENC_CODE_RANGE_TO(range,i) range[((i)*2) + 2] + +typedef struct { + int byte_len; /* argument(original) character(s) byte length */ + int code_len; /* number of code */ + OnigCodePoint code[ONIGENC_MAX_COMP_CASE_FOLD_CODE_LEN]; +} OnigCaseFoldCodeItem; + +typedef struct { + OnigCodePoint esc; + OnigCodePoint anychar; + OnigCodePoint anytime; + OnigCodePoint zero_or_one_time; + OnigCodePoint one_or_more_time; + OnigCodePoint anychar_anytime; +} OnigMetaCharTableType; + +typedef int (*OnigApplyAllCaseFoldFunc)(OnigCodePoint from, OnigCodePoint* to, int to_len, void* arg); + +typedef struct OnigEncodingTypeST { + int (*precise_mbc_enc_len)(const OnigUChar* p,const OnigUChar* e, struct OnigEncodingTypeST* enc); + const char* name; + int max_enc_len; + int min_enc_len; + int (*is_mbc_newline)(const OnigUChar* p, const OnigUChar* end, struct OnigEncodingTypeST* enc); + OnigCodePoint (*mbc_to_code)(const OnigUChar* p, const OnigUChar* end, struct OnigEncodingTypeST* enc); + int (*code_to_mbclen)(OnigCodePoint code, struct OnigEncodingTypeST* enc); + int (*code_to_mbc)(OnigCodePoint code, OnigUChar *buf, struct OnigEncodingTypeST* enc); + int (*mbc_case_fold)(OnigCaseFoldType flag, const OnigUChar** pp, const OnigUChar* end, OnigUChar* to, struct OnigEncodingTypeST* enc); + int (*apply_all_case_fold)(OnigCaseFoldType flag, OnigApplyAllCaseFoldFunc f, void* arg, struct OnigEncodingTypeST* enc); + int (*get_case_fold_codes_by_str)(OnigCaseFoldType flag, const OnigUChar* p, const OnigUChar* end, OnigCaseFoldCodeItem acs[], struct OnigEncodingTypeST* enc); + int (*property_name_to_ctype)(struct OnigEncodingTypeST* enc, OnigUChar* p, OnigUChar* end); + int (*is_code_ctype)(OnigCodePoint code, OnigCtype ctype, struct OnigEncodingTypeST* enc); + int (*get_ctype_code_range)(OnigCtype ctype, OnigCodePoint* sb_out, const OnigCodePoint* ranges[], struct OnigEncodingTypeST* enc); + OnigUChar* (*left_adjust_char_head)(const OnigUChar* start, const OnigUChar* p, const OnigUChar* end, struct OnigEncodingTypeST* enc); + int (*is_allowed_reverse_match)(const OnigUChar* p, const OnigUChar* end, struct OnigEncodingTypeST* enc); + int ruby_encoding_index; + unsigned int flags; +} OnigEncodingType; + +typedef OnigEncodingType* OnigEncoding; + +ONIG_EXTERN OnigEncodingType OnigEncodingASCII; + +#define ONIG_ENCODING_ASCII (&OnigEncodingASCII) + +#define ONIG_ENCODING_UNDEF ((OnigEncoding )0) + + +/* work size */ +#define ONIGENC_CODE_TO_MBC_MAXLEN 7 +#define ONIGENC_MBC_CASE_FOLD_MAXLEN 18 +/* 18: 6(max-byte) * 3(case-fold chars) */ + +/* character types */ +#define ONIGENC_CTYPE_NEWLINE 0 +#define ONIGENC_CTYPE_ALPHA 1 +#define ONIGENC_CTYPE_BLANK 2 +#define ONIGENC_CTYPE_CNTRL 3 +#define ONIGENC_CTYPE_DIGIT 4 +#define ONIGENC_CTYPE_GRAPH 5 +#define ONIGENC_CTYPE_LOWER 6 +#define ONIGENC_CTYPE_PRINT 7 +#define ONIGENC_CTYPE_PUNCT 8 +#define ONIGENC_CTYPE_SPACE 9 +#define ONIGENC_CTYPE_UPPER 10 +#define ONIGENC_CTYPE_XDIGIT 11 +#define ONIGENC_CTYPE_WORD 12 +#define ONIGENC_CTYPE_ALNUM 13 /* alpha || digit */ +#define ONIGENC_CTYPE_ASCII 14 +#define ONIGENC_MAX_STD_CTYPE ONIGENC_CTYPE_ASCII + +/* flags */ +#define ONIGENC_FLAG_NONE 0U +#define ONIGENC_FLAG_UNICODE 1U + +#define onig_enc_len(enc,p,e) ONIGENC_MBC_ENC_LEN(enc, p, e) + +#define ONIGENC_IS_UNDEF(enc) ((enc) == ONIG_ENCODING_UNDEF) +#define ONIGENC_IS_SINGLEBYTE(enc) (ONIGENC_MBC_MAXLEN(enc) == 1) +#define ONIGENC_IS_MBC_HEAD(enc,p,e) (ONIGENC_MBC_ENC_LEN(enc,p,e) != 1) +#define ONIGENC_IS_MBC_ASCII(p) (*(p) < 128) +#define ONIGENC_IS_CODE_ASCII(code) ((code) < 128) +#define ONIGENC_IS_MBC_WORD(enc,s,end) \ + ONIGENC_IS_CODE_WORD(enc,ONIGENC_MBC_TO_CODE(enc,s,end)) +#define ONIGENC_IS_MBC_ASCII_WORD(enc,s,end) \ + onigenc_ascii_is_code_ctype( \ + ONIGENC_MBC_TO_CODE(enc,s,end),ONIGENC_CTYPE_WORD,enc) +#define ONIGENC_IS_UNICODE(enc) ((enc)->flags & ONIGENC_FLAG_UNICODE) + + +#define ONIGENC_NAME(enc) ((enc)->name) + +#define ONIGENC_MBC_CASE_FOLD(enc,flag,pp,end,buf) \ + (enc)->mbc_case_fold(flag,(const OnigUChar** )pp,end,buf,enc) +#define ONIGENC_IS_ALLOWED_REVERSE_MATCH(enc,s,end) \ + (enc)->is_allowed_reverse_match(s,end,enc) +#define ONIGENC_LEFT_ADJUST_CHAR_HEAD(enc,start,s,end) \ + (enc)->left_adjust_char_head(start, s, end, enc) +#define ONIGENC_APPLY_ALL_CASE_FOLD(enc,case_fold_flag,f,arg) \ + (enc)->apply_all_case_fold(case_fold_flag,f,arg,enc) +#define ONIGENC_GET_CASE_FOLD_CODES_BY_STR(enc,case_fold_flag,p,end,acs) \ + (enc)->get_case_fold_codes_by_str(case_fold_flag,p,end,acs,enc) +#define ONIGENC_STEP_BACK(enc,start,s,end,n) \ + onigenc_step_back((enc),(start),(s),(end),(n)) + +#define ONIGENC_CONSTRUCT_MBCLEN_CHARFOUND(n) (n) +#define ONIGENC_MBCLEN_CHARFOUND_P(r) (0 < (r)) +#define ONIGENC_MBCLEN_CHARFOUND_LEN(r) (r) + +#define ONIGENC_CONSTRUCT_MBCLEN_INVALID() (-1) +#define ONIGENC_MBCLEN_INVALID_P(r) ((r) == -1) + +#define ONIGENC_CONSTRUCT_MBCLEN_NEEDMORE(n) (-1-(n)) +#define ONIGENC_MBCLEN_NEEDMORE_P(r) ((r) < -1) +#define ONIGENC_MBCLEN_NEEDMORE_LEN(r) (-1-(r)) + +#define ONIGENC_PRECISE_MBC_ENC_LEN(enc,p,e) (enc)->precise_mbc_enc_len(p,e,enc) + +ONIG_EXTERN +int onigenc_mbclen_approximate P_((const OnigUChar* p,const OnigUChar* e, struct OnigEncodingTypeST* enc)); + +#define ONIGENC_MBC_ENC_LEN(enc,p,e) onigenc_mbclen_approximate(p,e,enc) +#define ONIGENC_MBC_MAXLEN(enc) ((enc)->max_enc_len) +#define ONIGENC_MBC_MAXLEN_DIST(enc) ONIGENC_MBC_MAXLEN(enc) +#define ONIGENC_MBC_MINLEN(enc) ((enc)->min_enc_len) +#define ONIGENC_IS_MBC_NEWLINE(enc,p,end) (enc)->is_mbc_newline((p),(end),enc) +#define ONIGENC_MBC_TO_CODE(enc,p,end) (enc)->mbc_to_code((p),(end),enc) +#define ONIGENC_CODE_TO_MBCLEN(enc,code) (enc)->code_to_mbclen(code,enc) +#define ONIGENC_CODE_TO_MBC(enc,code,buf) (enc)->code_to_mbc(code,buf,enc) +#define ONIGENC_PROPERTY_NAME_TO_CTYPE(enc,p,end) \ + (enc)->property_name_to_ctype(enc,p,end) + +#define ONIGENC_IS_CODE_CTYPE(enc,code,ctype) (enc)->is_code_ctype(code,ctype,enc) + +#define ONIGENC_IS_CODE_NEWLINE(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_NEWLINE) +#define ONIGENC_IS_CODE_GRAPH(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_GRAPH) +#define ONIGENC_IS_CODE_PRINT(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_PRINT) +#define ONIGENC_IS_CODE_ALNUM(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_ALNUM) +#define ONIGENC_IS_CODE_ALPHA(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_ALPHA) +#define ONIGENC_IS_CODE_LOWER(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_LOWER) +#define ONIGENC_IS_CODE_UPPER(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_UPPER) +#define ONIGENC_IS_CODE_CNTRL(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_CNTRL) +#define ONIGENC_IS_CODE_PUNCT(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_PUNCT) +#define ONIGENC_IS_CODE_SPACE(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_SPACE) +#define ONIGENC_IS_CODE_BLANK(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_BLANK) +#define ONIGENC_IS_CODE_DIGIT(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_DIGIT) +#define ONIGENC_IS_CODE_XDIGIT(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_XDIGIT) +#define ONIGENC_IS_CODE_WORD(enc,code) \ + ONIGENC_IS_CODE_CTYPE(enc,code,ONIGENC_CTYPE_WORD) + +#define ONIGENC_GET_CTYPE_CODE_RANGE(enc,ctype,sbout,ranges) \ + (enc)->get_ctype_code_range(ctype,sbout,ranges,enc) + +ONIG_EXTERN +OnigUChar* onigenc_step_back P_((OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end, int n)); + + +/* encoding API */ +ONIG_EXTERN +int onigenc_init P_((void)); +ONIG_EXTERN +int onigenc_set_default_encoding P_((OnigEncoding enc)); +ONIG_EXTERN +OnigEncoding onigenc_get_default_encoding P_((void)); +ONIG_EXTERN +void onigenc_set_default_caseconv_table P_((const OnigUChar* table)); +ONIG_EXTERN +OnigUChar* onigenc_get_right_adjust_char_head_with_prev P_((OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end, const OnigUChar** prev)); +ONIG_EXTERN +OnigUChar* onigenc_get_prev_char_head P_((OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end)); +ONIG_EXTERN +OnigUChar* onigenc_get_left_adjust_char_head P_((OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end)); +ONIG_EXTERN +OnigUChar* onigenc_get_right_adjust_char_head P_((OnigEncoding enc, const OnigUChar* start, const OnigUChar* s, const OnigUChar* end)); +ONIG_EXTERN +int onigenc_strlen P_((OnigEncoding enc, const OnigUChar* p, const OnigUChar* end)); +ONIG_EXTERN +int onigenc_strlen_null P_((OnigEncoding enc, const OnigUChar* p)); +ONIG_EXTERN +int onigenc_str_bytelen_null P_((OnigEncoding enc, const OnigUChar* p)); + + + +/* PART: regular expression */ + +/* config parameters */ +#define ONIG_NREGION 10 +#define ONIG_MAX_BACKREF_NUM 1000 +#define ONIG_MAX_CAPTURE_GROUP_NUM 32767 +#define ONIG_MAX_REPEAT_NUM 100000 +#define ONIG_MAX_MULTI_BYTE_RANGES_NUM 10000 +/* constants */ +#define ONIG_MAX_ERROR_MESSAGE_LEN 90 + +typedef unsigned int OnigOptionType; + +#define ONIG_OPTION_DEFAULT ONIG_OPTION_NONE + +/* options */ +#define ONIG_OPTION_NONE 0U +#define ONIG_OPTION_IGNORECASE 1U +#define ONIG_OPTION_EXTEND (ONIG_OPTION_IGNORECASE << 1) +#define ONIG_OPTION_MULTILINE (ONIG_OPTION_EXTEND << 1) +#define ONIG_OPTION_DOTALL ONIG_OPTION_MULTILINE +#define ONIG_OPTION_SINGLELINE (ONIG_OPTION_MULTILINE << 1) +#define ONIG_OPTION_FIND_LONGEST (ONIG_OPTION_SINGLELINE << 1) +#define ONIG_OPTION_FIND_NOT_EMPTY (ONIG_OPTION_FIND_LONGEST << 1) +#define ONIG_OPTION_NEGATE_SINGLELINE (ONIG_OPTION_FIND_NOT_EMPTY << 1) +#define ONIG_OPTION_DONT_CAPTURE_GROUP (ONIG_OPTION_NEGATE_SINGLELINE << 1) +#define ONIG_OPTION_CAPTURE_GROUP (ONIG_OPTION_DONT_CAPTURE_GROUP << 1) +/* options (search time) */ +#define ONIG_OPTION_NOTBOL (ONIG_OPTION_CAPTURE_GROUP << 1) +#define ONIG_OPTION_NOTEOL (ONIG_OPTION_NOTBOL << 1) +#define ONIG_OPTION_POSIX_REGION (ONIG_OPTION_NOTEOL << 1) +/* options (ctype range) */ +#define ONIG_OPTION_ASCII_RANGE (ONIG_OPTION_POSIX_REGION << 1) +#define ONIG_OPTION_POSIX_BRACKET_ALL_RANGE (ONIG_OPTION_ASCII_RANGE << 1) +#define ONIG_OPTION_WORD_BOUND_ALL_RANGE (ONIG_OPTION_POSIX_BRACKET_ALL_RANGE << 1) +/* options (newline) */ +#define ONIG_OPTION_NEWLINE_CRLF (ONIG_OPTION_WORD_BOUND_ALL_RANGE << 1) +#define ONIG_OPTION_MAXBIT ONIG_OPTION_NEWLINE_CRLF /* limit */ + +#define ONIG_OPTION_ON(options,regopt) ((options) |= (regopt)) +#define ONIG_OPTION_OFF(options,regopt) ((options) &= ~(regopt)) +#define ONIG_IS_OPTION_ON(options,option) ((options) & (option)) + +/* syntax */ +typedef struct { + unsigned int op; + unsigned int op2; + unsigned int behavior; + OnigOptionType options; /* default option */ + OnigMetaCharTableType meta_char_table; +} OnigSyntaxType; + +ONIG_EXTERN const OnigSyntaxType OnigSyntaxASIS; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxPosixBasic; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxPosixExtended; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxEmacs; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxGrep; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxGnuRegex; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxJava; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl58; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl58_NG; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxPerl; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxRuby; +ONIG_EXTERN const OnigSyntaxType OnigSyntaxPython; + +/* predefined syntaxes (see regsyntax.c) */ +#define ONIG_SYNTAX_ASIS (&OnigSyntaxASIS) +#define ONIG_SYNTAX_POSIX_BASIC (&OnigSyntaxPosixBasic) +#define ONIG_SYNTAX_POSIX_EXTENDED (&OnigSyntaxPosixExtended) +#define ONIG_SYNTAX_EMACS (&OnigSyntaxEmacs) +#define ONIG_SYNTAX_GREP (&OnigSyntaxGrep) +#define ONIG_SYNTAX_GNU_REGEX (&OnigSyntaxGnuRegex) +#define ONIG_SYNTAX_JAVA (&OnigSyntaxJava) +#define ONIG_SYNTAX_PERL58 (&OnigSyntaxPerl58) +#define ONIG_SYNTAX_PERL58_NG (&OnigSyntaxPerl58_NG) +#define ONIG_SYNTAX_PERL (&OnigSyntaxPerl) +#define ONIG_SYNTAX_RUBY (&OnigSyntaxRuby) +#define ONIG_SYNTAX_PYTHON (&OnigSyntaxPython) + +/* default syntax */ +ONIG_EXTERN const OnigSyntaxType* OnigDefaultSyntax; +#define ONIG_SYNTAX_DEFAULT OnigDefaultSyntax + +/* syntax (operators) */ +#define ONIG_SYN_OP_VARIABLE_META_CHARACTERS (1U<<0) +#define ONIG_SYN_OP_DOT_ANYCHAR (1U<<1) /* . */ +#define ONIG_SYN_OP_ASTERISK_ZERO_INF (1U<<2) /* * */ +#define ONIG_SYN_OP_ESC_ASTERISK_ZERO_INF (1U<<3) +#define ONIG_SYN_OP_PLUS_ONE_INF (1U<<4) /* + */ +#define ONIG_SYN_OP_ESC_PLUS_ONE_INF (1U<<5) +#define ONIG_SYN_OP_QMARK_ZERO_ONE (1U<<6) /* ? */ +#define ONIG_SYN_OP_ESC_QMARK_ZERO_ONE (1U<<7) +#define ONIG_SYN_OP_BRACE_INTERVAL (1U<<8) /* {lower,upper} */ +#define ONIG_SYN_OP_ESC_BRACE_INTERVAL (1U<<9) /* \{lower,upper\} */ +#define ONIG_SYN_OP_VBAR_ALT (1U<<10) /* | */ +#define ONIG_SYN_OP_ESC_VBAR_ALT (1U<<11) /* \| */ +#define ONIG_SYN_OP_LPAREN_SUBEXP (1U<<12) /* (...) */ +#define ONIG_SYN_OP_ESC_LPAREN_SUBEXP (1U<<13) /* \(...\) */ +#define ONIG_SYN_OP_ESC_AZ_BUF_ANCHOR (1U<<14) /* \A, \Z, \z */ +#define ONIG_SYN_OP_ESC_CAPITAL_G_BEGIN_ANCHOR (1U<<15) /* \G */ +#define ONIG_SYN_OP_DECIMAL_BACKREF (1U<<16) /* \num */ +#define ONIG_SYN_OP_BRACKET_CC (1U<<17) /* [...] */ +#define ONIG_SYN_OP_ESC_W_WORD (1U<<18) /* \w, \W */ +#define ONIG_SYN_OP_ESC_LTGT_WORD_BEGIN_END (1U<<19) /* \<. \> */ +#define ONIG_SYN_OP_ESC_B_WORD_BOUND (1U<<20) /* \b, \B */ +#define ONIG_SYN_OP_ESC_S_WHITE_SPACE (1U<<21) /* \s, \S */ +#define ONIG_SYN_OP_ESC_D_DIGIT (1U<<22) /* \d, \D */ +#define ONIG_SYN_OP_LINE_ANCHOR (1U<<23) /* ^, $ */ +#define ONIG_SYN_OP_POSIX_BRACKET (1U<<24) /* [:xxxx:] */ +#define ONIG_SYN_OP_QMARK_NON_GREEDY (1U<<25) /* ??,*?,+?,{n,m}? */ +#define ONIG_SYN_OP_ESC_CONTROL_CHARS (1U<<26) /* \n,\r,\t,\a ... */ +#define ONIG_SYN_OP_ESC_C_CONTROL (1U<<27) /* \cx */ +#define ONIG_SYN_OP_ESC_OCTAL3 (1U<<28) /* \OOO */ +#define ONIG_SYN_OP_ESC_X_HEX2 (1U<<29) /* \xHH */ +#define ONIG_SYN_OP_ESC_X_BRACE_HEX8 (1U<<30) /* \x{7HHHHHHH} */ +#define ONIG_SYN_OP_ESC_O_BRACE_OCTAL (1U<<31) /* \o{OOO} */ /* NOTIMPL */ + +#define ONIG_SYN_OP2_ESC_CAPITAL_Q_QUOTE (1U<<0) /* \Q...\E */ +#define ONIG_SYN_OP2_QMARK_GROUP_EFFECT (1U<<1) /* (?...) */ +#define ONIG_SYN_OP2_OPTION_PERL (1U<<2) /* (?imsxadlu), (?-imsx), (?^imsxalu) */ +#define ONIG_SYN_OP2_OPTION_RUBY (1U<<3) /* (?imxadu), (?-imx) */ +#define ONIG_SYN_OP2_PLUS_POSSESSIVE_REPEAT (1U<<4) /* ?+,*+,++ */ +#define ONIG_SYN_OP2_PLUS_POSSESSIVE_INTERVAL (1U<<5) /* {n,m}+ */ +#define ONIG_SYN_OP2_CCLASS_SET_OP (1U<<6) /* [...&&..[..]..] */ +#define ONIG_SYN_OP2_QMARK_LT_NAMED_GROUP (1U<<7) /* (?...) */ +#define ONIG_SYN_OP2_ESC_K_NAMED_BACKREF (1U<<8) /* \k */ +#define ONIG_SYN_OP2_ESC_G_SUBEXP_CALL (1U<<9) /* \g, \g */ +#define ONIG_SYN_OP2_ATMARK_CAPTURE_HISTORY (1U<<10) /* (?@..),(?@..) */ +#define ONIG_SYN_OP2_ESC_CAPITAL_C_BAR_CONTROL (1U<<11) /* \C-x */ +#define ONIG_SYN_OP2_ESC_CAPITAL_M_BAR_META (1U<<12) /* \M-x */ +#define ONIG_SYN_OP2_ESC_V_VTAB (1U<<13) /* \v as VTAB */ +#define ONIG_SYN_OP2_ESC_U_HEX4 (1U<<14) /* \uHHHH */ +#define ONIG_SYN_OP2_ESC_GNU_BUF_ANCHOR (1U<<15) /* \`, \' */ +#define ONIG_SYN_OP2_ESC_P_BRACE_CHAR_PROPERTY (1U<<16) /* \p{...}, \P{...} */ +#define ONIG_SYN_OP2_ESC_P_BRACE_CIRCUMFLEX_NOT (1U<<17) /* \p{^..}, \P{^..} */ +/* #define ONIG_SYN_OP2_CHAR_PROPERTY_PREFIX_IS (1U<<18) */ +#define ONIG_SYN_OP2_ESC_H_XDIGIT (1U<<19) /* \h, \H */ +#define ONIG_SYN_OP2_INEFFECTIVE_ESCAPE (1U<<20) /* \ */ +#define ONIG_SYN_OP2_ESC_CAPITAL_R_LINEBREAK (1U<<21) /* \R as (?>\x0D\x0A|[\x0A-\x0D\x{85}\x{2028}\x{2029}]) */ +#define ONIG_SYN_OP2_ESC_CAPITAL_X_EXTENDED_GRAPHEME_CLUSTER (1U<<22) /* \X as (?>\P{M}\p{M}*) */ +#define ONIG_SYN_OP2_ESC_V_VERTICAL_WHITESPACE (1U<<23) /* \v, \V -- Perl */ /* NOTIMPL */ +#define ONIG_SYN_OP2_ESC_H_HORIZONTAL_WHITESPACE (1U<<24) /* \h, \H -- Perl */ /* NOTIMPL */ +#define ONIG_SYN_OP2_ESC_CAPITAL_K_KEEP (1U<<25) /* \K */ +#define ONIG_SYN_OP2_ESC_G_BRACE_BACKREF (1U<<26) /* \g{name}, \g{n} */ +#define ONIG_SYN_OP2_QMARK_SUBEXP_CALL (1U<<27) /* (?&name), (?n), (?R), (?0) */ +#define ONIG_SYN_OP2_QMARK_VBAR_BRANCH_RESET (1U<<28) /* (?|...) */ /* NOTIMPL */ +#define ONIG_SYN_OP2_QMARK_LPAREN_CONDITION (1U<<29) /* (?(cond)yes...|no...) */ +#define ONIG_SYN_OP2_QMARK_CAPITAL_P_NAMED_GROUP (1U<<30) /* (?P...), (?P=name), (?P>name) -- Python/PCRE */ +#define ONIG_SYN_OP2_OPTION_JAVA (1U<<31) /* (?idmsux), (?-idmsux) */ /* NOTIMPL */ + +/* syntax (behavior) */ +#define ONIG_SYN_CONTEXT_INDEP_ANCHORS (1U<<31) /* not implemented */ +#define ONIG_SYN_CONTEXT_INDEP_REPEAT_OPS (1U<<0) /* ?, *, +, {n,m} */ +#define ONIG_SYN_CONTEXT_INVALID_REPEAT_OPS (1U<<1) /* error or ignore */ +#define ONIG_SYN_ALLOW_UNMATCHED_CLOSE_SUBEXP (1U<<2) /* ...)... */ +#define ONIG_SYN_ALLOW_INVALID_INTERVAL (1U<<3) /* {??? */ +#define ONIG_SYN_ALLOW_INTERVAL_LOW_ABBREV (1U<<4) /* {,n} => {0,n} */ +#define ONIG_SYN_STRICT_CHECK_BACKREF (1U<<5) /* /(\1)/,/\1()/ ..*/ +#define ONIG_SYN_DIFFERENT_LEN_ALT_LOOK_BEHIND (1U<<6) /* (?<=a|bc) */ +#define ONIG_SYN_CAPTURE_ONLY_NAMED_GROUP (1U<<7) /* see doc/RE */ +#define ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME (1U<<8) /* (?)(?) */ +#define ONIG_SYN_FIXED_INTERVAL_IS_GREEDY_ONLY (1U<<9) /* a{n}?=(?:a{n})? */ +#define ONIG_SYN_ALLOW_MULTIPLEX_DEFINITION_NAME_CALL (1U<<10) /* (?)(?)(?&x) */ + +/* syntax (behavior) in char class [...] */ +#define ONIG_SYN_NOT_NEWLINE_IN_NEGATIVE_CC (1U<<20) /* [^...] */ +#define ONIG_SYN_BACKSLASH_ESCAPE_IN_CC (1U<<21) /* [..\w..] etc.. */ +#define ONIG_SYN_ALLOW_EMPTY_RANGE_IN_CC (1U<<22) +#define ONIG_SYN_ALLOW_DOUBLE_RANGE_OP_IN_CC (1U<<23) /* [0-9-a]=[0-9\-a] */ +/* syntax (behavior) warning */ +#define ONIG_SYN_WARN_CC_OP_NOT_ESCAPED (1U<<24) /* [,-,] */ +#define ONIG_SYN_WARN_REDUNDANT_NESTED_REPEAT (1U<<25) /* (?:a*)+ */ +#define ONIG_SYN_WARN_CC_DUP (1U<<26) /* [aa] */ + +/* meta character specifiers (onig_set_meta_char()) */ +#define ONIG_META_CHAR_ESCAPE 0 +#define ONIG_META_CHAR_ANYCHAR 1 +#define ONIG_META_CHAR_ANYTIME 2 +#define ONIG_META_CHAR_ZERO_OR_ONE_TIME 3 +#define ONIG_META_CHAR_ONE_OR_MORE_TIME 4 +#define ONIG_META_CHAR_ANYCHAR_ANYTIME 5 + +#define ONIG_INEFFECTIVE_META_CHAR 0 + +/* error codes */ +#define ONIG_IS_PATTERN_ERROR(ecode) ((ecode) <= -100 && (ecode) > -1000) +/* normal return */ +#define ONIG_NORMAL 0 +#define ONIG_MISMATCH -1 +#define ONIG_NO_SUPPORT_CONFIG -2 + +/* internal error */ +#define ONIGERR_MEMORY -5 +#define ONIGERR_TYPE_BUG -6 +#define ONIGERR_PARSER_BUG -11 +#define ONIGERR_STACK_BUG -12 +#define ONIGERR_UNDEFINED_BYTECODE -13 +#define ONIGERR_UNEXPECTED_BYTECODE -14 +#define ONIGERR_MATCH_STACK_LIMIT_OVER -15 +#define ONIGERR_DEFAULT_ENCODING_IS_NOT_SET -21 +#define ONIGERR_SPECIFIED_ENCODING_CANT_CONVERT_TO_WIDE_CHAR -22 +/* general error */ +#define ONIGERR_INVALID_ARGUMENT -30 +/* syntax error */ +#define ONIGERR_END_PATTERN_AT_LEFT_BRACE -100 +#define ONIGERR_END_PATTERN_AT_LEFT_BRACKET -101 +#define ONIGERR_EMPTY_CHAR_CLASS -102 +#define ONIGERR_PREMATURE_END_OF_CHAR_CLASS -103 +#define ONIGERR_END_PATTERN_AT_ESCAPE -104 +#define ONIGERR_END_PATTERN_AT_META -105 +#define ONIGERR_END_PATTERN_AT_CONTROL -106 +#define ONIGERR_META_CODE_SYNTAX -108 +#define ONIGERR_CONTROL_CODE_SYNTAX -109 +#define ONIGERR_CHAR_CLASS_VALUE_AT_END_OF_RANGE -110 +#define ONIGERR_CHAR_CLASS_VALUE_AT_START_OF_RANGE -111 +#define ONIGERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS -112 +#define ONIGERR_TARGET_OF_REPEAT_OPERATOR_NOT_SPECIFIED -113 +#define ONIGERR_TARGET_OF_REPEAT_OPERATOR_INVALID -114 +#define ONIGERR_NESTED_REPEAT_OPERATOR -115 +#define ONIGERR_UNMATCHED_CLOSE_PARENTHESIS -116 +#define ONIGERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS -117 +#define ONIGERR_END_PATTERN_IN_GROUP -118 +#define ONIGERR_UNDEFINED_GROUP_OPTION -119 +#define ONIGERR_INVALID_POSIX_BRACKET_TYPE -121 +#define ONIGERR_INVALID_LOOK_BEHIND_PATTERN -122 +#define ONIGERR_INVALID_REPEAT_RANGE_PATTERN -123 +#define ONIGERR_INVALID_CONDITION_PATTERN -124 +/* values error (syntax error) */ +#define ONIGERR_TOO_BIG_NUMBER -200 +#define ONIGERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE -201 +#define ONIGERR_UPPER_SMALLER_THAN_LOWER_IN_REPEAT_RANGE -202 +#define ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS -203 +#define ONIGERR_MISMATCH_CODE_LENGTH_IN_CLASS_RANGE -204 +#define ONIGERR_TOO_MANY_MULTI_BYTE_RANGES -205 +#define ONIGERR_TOO_SHORT_MULTI_BYTE_STRING -206 +#define ONIGERR_TOO_BIG_BACKREF_NUMBER -207 +#define ONIGERR_INVALID_BACKREF -208 +#define ONIGERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED -209 +#define ONIGERR_TOO_SHORT_DIGITS -210 +#define ONIGERR_TOO_LONG_WIDE_CHAR_VALUE -212 +#define ONIGERR_EMPTY_GROUP_NAME -214 +#define ONIGERR_INVALID_GROUP_NAME -215 +#define ONIGERR_INVALID_CHAR_IN_GROUP_NAME -216 +#define ONIGERR_UNDEFINED_NAME_REFERENCE -217 +#define ONIGERR_UNDEFINED_GROUP_REFERENCE -218 +#define ONIGERR_MULTIPLEX_DEFINED_NAME -219 +#define ONIGERR_MULTIPLEX_DEFINITION_NAME_CALL -220 +#define ONIGERR_NEVER_ENDING_RECURSION -221 +#define ONIGERR_GROUP_NUMBER_OVER_FOR_CAPTURE_HISTORY -222 +#define ONIGERR_INVALID_CHAR_PROPERTY_NAME -223 +#define ONIGERR_TOO_MANY_CAPTURE_GROUPS -224 +#define ONIGERR_INVALID_CODE_POINT_VALUE -400 +#define ONIGERR_INVALID_WIDE_CHAR_VALUE -400 +#define ONIGERR_TOO_BIG_WIDE_CHAR_VALUE -401 +#define ONIGERR_NOT_SUPPORTED_ENCODING_COMBINATION -402 +#define ONIGERR_INVALID_COMBINATION_OF_OPTIONS -403 + +/* errors related to thread */ +#define ONIGERR_OVER_THREAD_PASS_LIMIT_COUNT -1001 + + +/* must be smaller than BIT_STATUS_BITS_NUM (unsigned int * 8) */ +#define ONIG_MAX_CAPTURE_HISTORY_GROUP 31 +#define ONIG_IS_CAPTURE_HISTORY_GROUP(r, i) \ + ((i) <= ONIG_MAX_CAPTURE_HISTORY_GROUP && (r)->list && (r)->list[i]) + +typedef struct OnigCaptureTreeNodeStruct { + int group; /* group number */ + OnigPosition beg; + OnigPosition end; + int allocated; + int num_childs; + struct OnigCaptureTreeNodeStruct** childs; +} OnigCaptureTreeNode; + +/* match result region type */ +struct re_registers { + int allocated; + int num_regs; + OnigPosition* beg; + OnigPosition* end; + /* extended */ + OnigCaptureTreeNode* history_root; /* capture history tree root */ +}; + +/* capture tree traverse */ +#define ONIG_TRAVERSE_CALLBACK_AT_FIRST 1 +#define ONIG_TRAVERSE_CALLBACK_AT_LAST 2 +#define ONIG_TRAVERSE_CALLBACK_AT_BOTH \ + ( ONIG_TRAVERSE_CALLBACK_AT_FIRST | ONIG_TRAVERSE_CALLBACK_AT_LAST ) + + +#define ONIG_REGION_NOTPOS -1 + +typedef struct re_registers OnigRegion; + +typedef struct { + OnigEncoding enc; + OnigUChar* par; + OnigUChar* par_end; +} OnigErrorInfo; + +typedef struct { + int lower; + int upper; +} OnigRepeatRange; + +typedef void (*OnigWarnFunc) P_((const char* s)); +extern void onig_null_warn P_((const char* s)); +#define ONIG_NULL_WARN onig_null_warn + +#define ONIG_CHAR_TABLE_SIZE 256 + +/* regex_t state */ +#define ONIG_STATE_NORMAL 0 +#define ONIG_STATE_SEARCHING 1 +#define ONIG_STATE_COMPILING -1 +#define ONIG_STATE_MODIFY -2 + +#define ONIG_STATE(reg) \ + ((reg)->state > 0 ? ONIG_STATE_SEARCHING : (reg)->state) + +typedef struct re_pattern_buffer { + /* common members of BBuf(bytes-buffer) */ + unsigned char* p; /* compiled pattern */ + unsigned int used; /* used space for p */ + unsigned int alloc; /* allocated space for p */ + + int state; /* normal, searching, compiling */ + int num_mem; /* used memory(...) num counted from 1 */ + int num_repeat; /* OP_REPEAT/OP_REPEAT_NG id-counter */ + int num_null_check; /* OP_NULL_CHECK_START/END id counter */ + int num_comb_exp_check; /* combination explosion check */ + int num_call; /* number of subexp call */ + unsigned int capture_history; /* (?@...) flag (1-31) */ + unsigned int bt_mem_start; /* need backtrack flag */ + unsigned int bt_mem_end; /* need backtrack flag */ + int stack_pop_level; + int repeat_range_alloc; + OnigRepeatRange* repeat_range; + + OnigEncoding enc; + OnigOptionType options; + const OnigSyntaxType* syntax; + OnigCaseFoldType case_fold_flag; + void* name_table; + + /* optimization info (string search, char-map and anchors) */ + int optimize; /* optimize flag */ + int threshold_len; /* search str-length for apply optimize */ + int anchor; /* BEGIN_BUF, BEGIN_POS, (SEMI_)END_BUF */ + OnigDistance anchor_dmin; /* (SEMI_)END_BUF anchor distance */ + OnigDistance anchor_dmax; /* (SEMI_)END_BUF anchor distance */ + int sub_anchor; /* start-anchor for exact or map */ + unsigned char *exact; + unsigned char *exact_end; + unsigned char map[ONIG_CHAR_TABLE_SIZE]; /* used as BM skip or char-map */ + int *int_map; /* BM skip for exact_len > 255 */ + int *int_map_backward; /* BM skip for backward search */ + OnigDistance dmin; /* min-distance of exact or map */ + OnigDistance dmax; /* max-distance of exact or map */ + + /* regex_t link chain */ + struct re_pattern_buffer* chain; /* escape compile-conflict */ +} OnigRegexType; + +typedef OnigRegexType* OnigRegex; + +#ifndef ONIG_ESCAPE_REGEX_T_COLLISION + typedef OnigRegexType regex_t; +#endif + + +typedef struct { + int num_of_elements; + OnigEncoding pattern_enc; + OnigEncoding target_enc; + OnigSyntaxType* syntax; + OnigOptionType option; + OnigCaseFoldType case_fold_flag; +} OnigCompileInfo; + +/* Oniguruma Native API */ +ONIG_EXTERN +int onig_init P_((void)); +ONIG_EXTERN +int onig_error_code_to_str PV_((OnigUChar* s, OnigPosition err_code, ...)); +ONIG_EXTERN +void onig_set_warn_func P_((OnigWarnFunc f)); +ONIG_EXTERN +void onig_set_verb_warn_func P_((OnigWarnFunc f)); +ONIG_EXTERN +int onig_new P_((OnigRegex*, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigOptionType option, OnigEncoding enc, const OnigSyntaxType* syntax, OnigErrorInfo* einfo)); +ONIG_EXTERN +int onig_reg_init P_((OnigRegex reg, OnigOptionType option, OnigCaseFoldType case_fold_flag, OnigEncoding enc, const OnigSyntaxType* syntax)); +ONIG_EXTERN +int onig_new_without_alloc P_((OnigRegex, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigOptionType option, OnigEncoding enc, OnigSyntaxType* syntax, OnigErrorInfo* einfo)); +ONIG_EXTERN +int onig_new_deluxe P_((OnigRegex* reg, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigCompileInfo* ci, OnigErrorInfo* einfo)); +ONIG_EXTERN +void onig_free P_((OnigRegex)); +ONIG_EXTERN +void onig_free_body P_((OnigRegex)); +ONIG_EXTERN +int onig_recompile P_((OnigRegex, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigOptionType option, OnigEncoding enc, OnigSyntaxType* syntax, OnigErrorInfo* einfo)); +ONIG_EXTERN +int onig_recompile_deluxe P_((OnigRegex reg, const OnigUChar* pattern, const OnigUChar* pattern_end, OnigCompileInfo* ci, OnigErrorInfo* einfo)); +ONIG_EXTERN +OnigPosition onig_search P_((OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* start, const OnigUChar* range, OnigRegion* region, OnigOptionType option)); +ONIG_EXTERN +OnigPosition onig_search_gpos P_((OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* global_pos, const OnigUChar* start, const OnigUChar* range, OnigRegion* region, OnigOptionType option)); +ONIG_EXTERN +OnigPosition onig_match P_((OnigRegex, const OnigUChar* str, const OnigUChar* end, const OnigUChar* at, OnigRegion* region, OnigOptionType option)); +ONIG_EXTERN +OnigRegion* onig_region_new P_((void)); +ONIG_EXTERN +void onig_region_init P_((OnigRegion* region)); +ONIG_EXTERN +void onig_region_free P_((OnigRegion* region, int free_self)); +ONIG_EXTERN +void onig_region_copy P_((OnigRegion* to, OnigRegion* from)); +ONIG_EXTERN +void onig_region_clear P_((OnigRegion* region)); +ONIG_EXTERN +int onig_region_resize P_((OnigRegion* region, int n)); +ONIG_EXTERN +int onig_region_set P_((OnigRegion* region, int at, int beg, int end)); +ONIG_EXTERN +int onig_name_to_group_numbers P_((OnigRegex reg, const OnigUChar* name, const OnigUChar* name_end, int** nums)); +ONIG_EXTERN +int onig_name_to_backref_number P_((OnigRegex reg, const OnigUChar* name, const OnigUChar* name_end, OnigRegion *region)); +ONIG_EXTERN +int onig_foreach_name P_((OnigRegex reg, int (*func)(const OnigUChar*, const OnigUChar*,int,int*,OnigRegex,void*), void* arg)); +ONIG_EXTERN +int onig_number_of_names P_((OnigRegex reg)); +ONIG_EXTERN +int onig_number_of_captures P_((OnigRegex reg)); +ONIG_EXTERN +int onig_number_of_capture_histories P_((OnigRegex reg)); +ONIG_EXTERN +OnigCaptureTreeNode* onig_get_capture_tree P_((OnigRegion* region)); +ONIG_EXTERN +int onig_capture_tree_traverse P_((OnigRegion* region, int at, int(*callback_func)(int,OnigPosition,OnigPosition,int,int,void*), void* arg)); +ONIG_EXTERN +int onig_noname_group_capture_is_active P_((OnigRegex reg)); +ONIG_EXTERN +OnigEncoding onig_get_encoding P_((OnigRegex reg)); +ONIG_EXTERN +OnigOptionType onig_get_options P_((OnigRegex reg)); +ONIG_EXTERN +OnigCaseFoldType onig_get_case_fold_flag P_((OnigRegex reg)); +ONIG_EXTERN +const OnigSyntaxType* onig_get_syntax P_((OnigRegex reg)); +ONIG_EXTERN +int onig_set_default_syntax P_((const OnigSyntaxType* syntax)); +ONIG_EXTERN +void onig_copy_syntax P_((OnigSyntaxType* to, const OnigSyntaxType* from)); +ONIG_EXTERN +unsigned int onig_get_syntax_op P_((OnigSyntaxType* syntax)); +ONIG_EXTERN +unsigned int onig_get_syntax_op2 P_((OnigSyntaxType* syntax)); +ONIG_EXTERN +unsigned int onig_get_syntax_behavior P_((OnigSyntaxType* syntax)); +ONIG_EXTERN +OnigOptionType onig_get_syntax_options P_((OnigSyntaxType* syntax)); +ONIG_EXTERN +void onig_set_syntax_op P_((OnigSyntaxType* syntax, unsigned int op)); +ONIG_EXTERN +void onig_set_syntax_op2 P_((OnigSyntaxType* syntax, unsigned int op2)); +ONIG_EXTERN +void onig_set_syntax_behavior P_((OnigSyntaxType* syntax, unsigned int behavior)); +ONIG_EXTERN +void onig_set_syntax_options P_((OnigSyntaxType* syntax, OnigOptionType options)); +ONIG_EXTERN +int onig_set_meta_char P_((OnigSyntaxType* syntax, unsigned int what, OnigCodePoint code)); +ONIG_EXTERN +void onig_copy_encoding P_((OnigEncoding to, OnigEncoding from)); +ONIG_EXTERN +OnigCaseFoldType onig_get_default_case_fold_flag P_((void)); +ONIG_EXTERN +int onig_set_default_case_fold_flag P_((OnigCaseFoldType case_fold_flag)); +ONIG_EXTERN +unsigned int onig_get_match_stack_limit_size P_((void)); +ONIG_EXTERN +int onig_set_match_stack_limit_size P_((unsigned int size)); +ONIG_EXTERN +int onig_end P_((void)); +ONIG_EXTERN +const char* onig_version P_((void)); +ONIG_EXTERN +const char* onig_copyright P_((void)); + +RUBY_SYMBOL_EXPORT_END + +#ifdef __cplusplus +#if 0 +{ /* satisfy cc-mode */ +#endif +} +#endif + +#endif /* ONIGURUMA_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/re.h b/ruby/include/ruby-2.1.0/ruby/re.h new file mode 100644 index 0000000..af6074f --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/re.h @@ -0,0 +1,71 @@ +/********************************************************************** + + re.h - + + $Author: nobu $ + created at: Thu Sep 30 14:18:32 JST 1993 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +**********************************************************************/ + +#ifndef RUBY_RE_H +#define RUBY_RE_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#include +#include + +#include "ruby/regex.h" + +RUBY_SYMBOL_EXPORT_BEGIN + +typedef struct re_pattern_buffer Regexp; + +struct rmatch_offset { + long beg; + long end; +}; + +struct rmatch { + struct re_registers regs; + + int char_offset_updated; + int char_offset_num_allocated; + struct rmatch_offset *char_offset; +}; + +struct RMatch { + struct RBasic basic; + VALUE str; + struct rmatch *rmatch; + VALUE regexp; /* RRegexp */ +}; + +#define RMATCH(obj) (R_CAST(RMatch)(obj)) +#define RMATCH_REGS(obj) (&(R_CAST(RMatch)(obj))->rmatch->regs) + +VALUE rb_reg_regcomp(VALUE); +long rb_reg_search(VALUE, VALUE, long, int); +VALUE rb_reg_regsub(VALUE, VALUE, struct re_registers *, VALUE); +long rb_reg_adjust_startpos(VALUE, VALUE, long, int); +void rb_match_busy(VALUE); +VALUE rb_reg_quote(VALUE); +regex_t *rb_reg_prepare_re(VALUE re, VALUE str); + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_RE_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/regex.h b/ruby/include/ruby-2.1.0/ruby/regex.h new file mode 100644 index 0000000..5ccc0aa --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/regex.h @@ -0,0 +1,46 @@ +/********************************************************************** + + regex.h - + + $Author: nobu $ + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +**********************************************************************/ + +#ifndef ONIGURUMA_REGEX_H +#define ONIGURUMA_REGEX_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#ifdef RUBY +#include "ruby/oniguruma.h" +#else +#include "oniguruma.h" +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +#ifndef ONIG_RUBY_M17N + +ONIG_EXTERN OnigEncoding OnigEncDefaultCharEncoding; + +#define mbclen(p,e,enc) rb_enc_mbclen((p),(e),(enc)) + +#endif /* ifndef ONIG_RUBY_M17N */ + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* ONIGURUMA_REGEX_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/ruby.h b/ruby/include/ruby-2.1.0/ruby/ruby.h new file mode 100644 index 0000000..10ac202 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/ruby.h @@ -0,0 +1,1872 @@ +/********************************************************************** + + ruby/ruby.h - + + $Author: ko1 $ + created at: Thu Jun 10 14:26:32 JST 1993 + + Copyright (C) 1993-2008 Yukihiro Matsumoto + Copyright (C) 2000 Network Applied Communication Laboratory, Inc. + Copyright (C) 2000 Information-technology Promotion Agency, Japan + +**********************************************************************/ + +#ifndef RUBY_RUBY_H +#define RUBY_RUBY_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#include "ruby/config.h" +#ifdef RUBY_EXTCONF_H +#include RUBY_EXTCONF_H +#endif + +#include "defines.h" + +#define NORETURN_STYLE_NEW 1 +#ifndef NORETURN +# define NORETURN(x) x +#endif +#ifndef DEPRECATED +# define DEPRECATED(x) x +#endif +#ifndef NOINLINE +# define NOINLINE(x) x +#endif +#ifndef UNREACHABLE +# define UNREACHABLE /* unreachable */ +#endif + +#ifdef __GNUC__ +#define PRINTF_ARGS(decl, string_index, first_to_check) \ + decl __attribute__((format(printf, string_index, first_to_check))) +#else +#define PRINTF_ARGS(decl, string_index, first_to_check) decl +#endif + +#ifdef HAVE_INTRINSICS_H +# include +#endif + +#include + +RUBY_SYMBOL_EXPORT_BEGIN + +/* Make alloca work the best possible way. */ +#ifdef __GNUC__ +# ifndef atarist +# ifndef alloca +# define alloca __builtin_alloca +# endif +# endif /* atarist */ +#else +# ifdef HAVE_ALLOCA_H +# include +# else +# ifdef _AIX +#pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +void *alloca(); +# endif +# endif /* AIX */ +# endif /* HAVE_ALLOCA_H */ +#endif /* __GNUC__ */ + +#if defined HAVE_UINTPTR_T && 0 +typedef uintptr_t VALUE; +typedef uintptr_t ID; +# define SIGNED_VALUE intptr_t +# define SIZEOF_VALUE SIZEOF_UINTPTR_T +# undef PRI_VALUE_PREFIX +#elif SIZEOF_LONG == SIZEOF_VOIDP +typedef unsigned long VALUE; +typedef unsigned long ID; +# define SIGNED_VALUE long +# define SIZEOF_VALUE SIZEOF_LONG +# define PRI_VALUE_PREFIX "l" +#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP +typedef unsigned LONG_LONG VALUE; +typedef unsigned LONG_LONG ID; +# define SIGNED_VALUE LONG_LONG +# define LONG_LONG_VALUE 1 +# define SIZEOF_VALUE SIZEOF_LONG_LONG +# define PRI_VALUE_PREFIX PRI_LL_PREFIX +#else +# error ---->> ruby requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<---- +#endif + +typedef char ruby_check_sizeof_int[SIZEOF_INT == sizeof(int) ? 1 : -1]; +typedef char ruby_check_sizeof_long[SIZEOF_LONG == sizeof(long) ? 1 : -1]; +#ifdef HAVE_LONG_LONG +typedef char ruby_check_sizeof_long_long[SIZEOF_LONG_LONG == sizeof(LONG_LONG) ? 1 : -1]; +#endif +typedef char ruby_check_sizeof_voidp[SIZEOF_VOIDP == sizeof(void*) ? 1 : -1]; + +#ifndef PRI_INT_PREFIX +#define PRI_INT_PREFIX "" +#endif +#ifndef PRI_LONG_PREFIX +#define PRI_LONG_PREFIX "l" +#endif + +#if SIZEOF_LONG == 8 +#define PRI_64_PREFIX PRI_LONG_PREFIX +#elif SIZEOF_LONG_LONG == 8 +#define PRI_64_PREFIX PRI_LL_PREFIX +#endif + +#if defined PRIdPTR && !defined PRI_VALUE_PREFIX +#define PRIdVALUE PRIdPTR +#define PRIoVALUE PRIoPTR +#define PRIuVALUE PRIuPTR +#define PRIxVALUE PRIxPTR +#define PRIXVALUE PRIXPTR +#define PRIsVALUE PRIiPTR +#else +#define PRIdVALUE PRI_VALUE_PREFIX"d" +#define PRIoVALUE PRI_VALUE_PREFIX"o" +#define PRIuVALUE PRI_VALUE_PREFIX"u" +#define PRIxVALUE PRI_VALUE_PREFIX"x" +#define PRIXVALUE PRI_VALUE_PREFIX"X" +#define PRIsVALUE PRI_VALUE_PREFIX"i" +#endif +#ifndef PRI_VALUE_PREFIX +# define PRI_VALUE_PREFIX "" +#endif + +#ifndef PRI_TIMET_PREFIX +# if SIZEOF_TIME_T == SIZEOF_INT +# define PRI_TIMET_PREFIX +# elif SIZEOF_TIME_T == SIZEOF_LONG +# define PRI_TIMET_PREFIX "l" +# elif SIZEOF_TIME_T == SIZEOF_LONG_LONG +# define PRI_TIMET_PREFIX PRI_LL_PREFIX +# endif +#endif + +#if defined PRI_PTRDIFF_PREFIX +#elif SIZEOF_PTRDIFF_T == SIZEOF_INT +# define PRI_PTRDIFF_PREFIX "" +#elif SIZEOF_PTRDIFF_T == SIZEOF_LONG +# define PRI_PTRDIFF_PREFIX "l" +#elif SIZEOF_PTRDIFF_T == SIZEOF_LONG_LONG +# define PRI_PTRDIFF_PREFIX PRI_LL_PREFIX +#endif +#define PRIdPTRDIFF PRI_PTRDIFF_PREFIX"d" +#define PRIiPTRDIFF PRI_PTRDIFF_PREFIX"i" +#define PRIoPTRDIFF PRI_PTRDIFF_PREFIX"o" +#define PRIuPTRDIFF PRI_PTRDIFF_PREFIX"u" +#define PRIxPTRDIFF PRI_PTRDIFF_PREFIX"x" +#define PRIXPTRDIFF PRI_PTRDIFF_PREFIX"X" + +#if defined PRI_SIZE_PREFIX +#elif SIZEOF_SIZE_T == SIZEOF_INT +# define PRI_SIZE_PREFIX "" +#elif SIZEOF_SIZE_T == SIZEOF_LONG +# define PRI_SIZE_PREFIX "l" +#elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG +# define PRI_SIZE_PREFIX PRI_LL_PREFIX +#endif +#define PRIdSIZE PRI_SIZE_PREFIX"d" +#define PRIiSIZE PRI_SIZE_PREFIX"i" +#define PRIoSIZE PRI_SIZE_PREFIX"o" +#define PRIuSIZE PRI_SIZE_PREFIX"u" +#define PRIxSIZE PRI_SIZE_PREFIX"x" +#define PRIXSIZE PRI_SIZE_PREFIX"X" + +#ifdef __STDC__ +# include +#else +# ifndef LONG_MAX +# ifdef HAVE_LIMITS_H +# include +# else + /* assuming 32bit(2's complement) long */ +# define LONG_MAX 2147483647 +# endif +# endif +# ifndef LONG_MIN +# define LONG_MIN (-LONG_MAX-1) +# endif +# ifndef CHAR_BIT +# define CHAR_BIT 8 +# endif +#endif + +#ifdef HAVE_LONG_LONG +# ifndef LLONG_MAX +# ifdef LONG_LONG_MAX +# define LLONG_MAX LONG_LONG_MAX +# else +# ifdef _I64_MAX +# define LLONG_MAX _I64_MAX +# else + /* assuming 64bit(2's complement) long long */ +# define LLONG_MAX 9223372036854775807LL +# endif +# endif +# endif +# ifndef LLONG_MIN +# ifdef LONG_LONG_MIN +# define LLONG_MIN LONG_LONG_MIN +# else +# ifdef _I64_MIN +# define LLONG_MIN _I64_MIN +# else +# define LLONG_MIN (-LLONG_MAX-1) +# endif +# endif +# endif +#endif + +#define FIXNUM_MAX (LONG_MAX>>1) +#define FIXNUM_MIN RSHIFT((long)LONG_MIN,1) + +#define INT2FIX(i) ((VALUE)(((SIGNED_VALUE)(i))<<1 | FIXNUM_FLAG)) +#define LONG2FIX(i) INT2FIX(i) +#define rb_fix_new(v) INT2FIX(v) +VALUE rb_int2inum(SIGNED_VALUE); + +#define rb_int_new(v) rb_int2inum(v) +VALUE rb_uint2inum(VALUE); + +#define rb_uint_new(v) rb_uint2inum(v) + +#ifdef HAVE_LONG_LONG +VALUE rb_ll2inum(LONG_LONG); +#define LL2NUM(v) rb_ll2inum(v) +VALUE rb_ull2inum(unsigned LONG_LONG); +#define ULL2NUM(v) rb_ull2inum(v) +#endif + +#ifndef OFFT2NUM +#if SIZEOF_OFF_T > SIZEOF_LONG && defined(HAVE_LONG_LONG) +# define OFFT2NUM(v) LL2NUM(v) +#elif SIZEOF_OFF_T == SIZEOF_LONG +# define OFFT2NUM(v) LONG2NUM(v) +#else +# define OFFT2NUM(v) INT2NUM(v) +#endif +#endif + +#if SIZEOF_SIZE_T > SIZEOF_LONG && defined(HAVE_LONG_LONG) +# define SIZET2NUM(v) ULL2NUM(v) +# define SSIZET2NUM(v) LL2NUM(v) +#elif SIZEOF_SIZE_T == SIZEOF_LONG +# define SIZET2NUM(v) ULONG2NUM(v) +# define SSIZET2NUM(v) LONG2NUM(v) +#else +# define SIZET2NUM(v) UINT2NUM(v) +# define SSIZET2NUM(v) INT2NUM(v) +#endif + +#ifndef SIZE_MAX +# if SIZEOF_SIZE_T > SIZEOF_LONG && defined(HAVE_LONG_LONG) +# define SIZE_MAX ULLONG_MAX +# define SIZE_MIN ULLONG_MIN +# elif SIZEOF_SIZE_T == SIZEOF_LONG +# define SIZE_MAX ULONG_MAX +# define SIZE_MIN ULONG_MIN +# elif SIZEOF_SIZE_T == SIZEOF_INT +# define SIZE_MAX UINT_MAX +# define SIZE_MIN UINT_MIN +# else +# define SIZE_MAX USHRT_MAX +# define SIZE_MIN USHRT_MIN +# endif +#endif + +#ifndef SSIZE_MAX +# if SIZEOF_SIZE_T > SIZEOF_LONG && defined(HAVE_LONG_LONG) +# define SSIZE_MAX LLONG_MAX +# define SSIZE_MIN LLONG_MIN +# elif SIZEOF_SIZE_T == SIZEOF_LONG +# define SSIZE_MAX LONG_MAX +# define SSIZE_MIN LONG_MIN +# elif SIZEOF_SIZE_T == SIZEOF_INT +# define SSIZE_MAX INT_MAX +# define SSIZE_MIN INT_MIN +# else +# define SSIZE_MAX SHRT_MAX +# define SSIZE_MIN SHRT_MIN +# endif +#endif + +#if SIZEOF_INT < SIZEOF_VALUE +NORETURN(void rb_out_of_int(SIGNED_VALUE num)); +#endif + +#if SIZEOF_INT < SIZEOF_LONG +static inline int +rb_long2int_inline(long n) +{ + int i = (int)n; + if ((long)i != n) + rb_out_of_int(n); + + return i; +} +#define rb_long2int(n) rb_long2int_inline(n) +#else +#define rb_long2int(n) ((int)(n)) +#endif + +#ifndef PIDT2NUM +#define PIDT2NUM(v) LONG2NUM(v) +#endif +#ifndef NUM2PIDT +#define NUM2PIDT(v) NUM2LONG(v) +#endif +#ifndef UIDT2NUM +#define UIDT2NUM(v) LONG2NUM(v) +#endif +#ifndef NUM2UIDT +#define NUM2UIDT(v) NUM2LONG(v) +#endif +#ifndef GIDT2NUM +#define GIDT2NUM(v) LONG2NUM(v) +#endif +#ifndef NUM2GIDT +#define NUM2GIDT(v) NUM2LONG(v) +#endif +#ifndef NUM2MODET +#define NUM2MODET(v) NUM2INT(v) +#endif +#ifndef MODET2NUM +#define MODET2NUM(v) INT2NUM(v) +#endif + +#define FIX2LONG(x) ((long)RSHIFT((SIGNED_VALUE)(x),1)) +#define FIX2ULONG(x) ((unsigned long)FIX2LONG(x)) +#define FIXNUM_P(f) (((int)(SIGNED_VALUE)(f))&FIXNUM_FLAG) +#define POSFIXABLE(f) ((f) < FIXNUM_MAX+1) +#define NEGFIXABLE(f) ((f) >= FIXNUM_MIN) +#define FIXABLE(f) (POSFIXABLE(f) && NEGFIXABLE(f)) + +#define IMMEDIATE_P(x) ((VALUE)(x) & IMMEDIATE_MASK) + +#define SYMBOL_P(x) (((VALUE)(x)&~((~(VALUE)0)<= SIZEOF_DOUBLE +#define USE_FLONUM 1 +#else +#define USE_FLONUM 0 +#endif +#endif + +#if USE_FLONUM +#define FLONUM_P(x) ((((int)(SIGNED_VALUE)(x))&FLONUM_MASK) == FLONUM_FLAG) +#else +#define FLONUM_P(x) 0 +#endif + +/* Module#methods, #singleton_methods and so on return Symbols */ +#define USE_SYMBOL_AS_METHOD_NAME 1 + +/* +!USE_FLONUM +------------------------- +...xxxx xxx1 Fixnum +...0000 1110 Symbol +...0000 0000 Qfalse +...0000 0010 Qtrue +...0000 0100 Qnil +...0000 0110 Qundef + +USE_FLONUM +------------------------- +...xxxx xxx1 Fixnum +...xxxx xx10 Flonum +...0000 1100 Symbol +...0000 0000 Qfalse 0x00 = 0 +...0000 1000 Qnil 0x08 = 8 +...0001 0100 Qtrue 0x14 = 20 +...0011 0100 Qundef 0x34 = 52 + */ + +/* special constants - i.e. non-zero and non-fixnum constants */ +enum ruby_special_consts { +#if USE_FLONUM + RUBY_Qfalse = 0x00, + RUBY_Qtrue = 0x14, + RUBY_Qnil = 0x08, + RUBY_Qundef = 0x34, + + RUBY_IMMEDIATE_MASK = 0x07, + RUBY_FIXNUM_FLAG = 0x01, + RUBY_FLONUM_MASK = 0x03, + RUBY_FLONUM_FLAG = 0x02, + RUBY_SYMBOL_FLAG = 0x0c, + RUBY_SPECIAL_SHIFT = 8 +#else + RUBY_Qfalse = 0, + RUBY_Qtrue = 2, + RUBY_Qnil = 4, + RUBY_Qundef = 6, + + RUBY_IMMEDIATE_MASK = 0x03, + RUBY_FIXNUM_FLAG = 0x01, + RUBY_FLONUM_MASK = 0x00, /* any values ANDed with FLONUM_MASK cannot be FLONUM_FLAG */ + RUBY_FLONUM_FLAG = 0x02, + RUBY_SYMBOL_FLAG = 0x0e, + RUBY_SPECIAL_SHIFT = 8 +#endif +}; + +#define Qfalse ((VALUE)RUBY_Qfalse) +#define Qtrue ((VALUE)RUBY_Qtrue) +#define Qnil ((VALUE)RUBY_Qnil) +#define Qundef ((VALUE)RUBY_Qundef) /* undefined value for placeholder */ +#define IMMEDIATE_MASK RUBY_IMMEDIATE_MASK +#define FIXNUM_FLAG RUBY_FIXNUM_FLAG +#if USE_FLONUM +#define FLONUM_MASK RUBY_FLONUM_MASK +#define FLONUM_FLAG RUBY_FLONUM_FLAG +#endif +#define SYMBOL_FLAG RUBY_SYMBOL_FLAG + +#define RTEST(v) !(((VALUE)(v) & ~Qnil) == 0) +#define NIL_P(v) !((VALUE)(v) != Qnil) + +#define CLASS_OF(v) rb_class_of((VALUE)(v)) + +enum ruby_value_type { + RUBY_T_NONE = 0x00, + + RUBY_T_OBJECT = 0x01, + RUBY_T_CLASS = 0x02, + RUBY_T_MODULE = 0x03, + RUBY_T_FLOAT = 0x04, + RUBY_T_STRING = 0x05, + RUBY_T_REGEXP = 0x06, + RUBY_T_ARRAY = 0x07, + RUBY_T_HASH = 0x08, + RUBY_T_STRUCT = 0x09, + RUBY_T_BIGNUM = 0x0a, + RUBY_T_FILE = 0x0b, + RUBY_T_DATA = 0x0c, + RUBY_T_MATCH = 0x0d, + RUBY_T_COMPLEX = 0x0e, + RUBY_T_RATIONAL = 0x0f, + + RUBY_T_NIL = 0x11, + RUBY_T_TRUE = 0x12, + RUBY_T_FALSE = 0x13, + RUBY_T_SYMBOL = 0x14, + RUBY_T_FIXNUM = 0x15, + + RUBY_T_UNDEF = 0x1b, + RUBY_T_NODE = 0x1c, + RUBY_T_ICLASS = 0x1d, + RUBY_T_ZOMBIE = 0x1e, + + RUBY_T_MASK = 0x1f +}; + +#define T_NONE RUBY_T_NONE +#define T_NIL RUBY_T_NIL +#define T_OBJECT RUBY_T_OBJECT +#define T_CLASS RUBY_T_CLASS +#define T_ICLASS RUBY_T_ICLASS +#define T_MODULE RUBY_T_MODULE +#define T_FLOAT RUBY_T_FLOAT +#define T_STRING RUBY_T_STRING +#define T_REGEXP RUBY_T_REGEXP +#define T_ARRAY RUBY_T_ARRAY +#define T_HASH RUBY_T_HASH +#define T_STRUCT RUBY_T_STRUCT +#define T_BIGNUM RUBY_T_BIGNUM +#define T_FILE RUBY_T_FILE +#define T_FIXNUM RUBY_T_FIXNUM +#define T_TRUE RUBY_T_TRUE +#define T_FALSE RUBY_T_FALSE +#define T_DATA RUBY_T_DATA +#define T_MATCH RUBY_T_MATCH +#define T_SYMBOL RUBY_T_SYMBOL +#define T_RATIONAL RUBY_T_RATIONAL +#define T_COMPLEX RUBY_T_COMPLEX +#define T_UNDEF RUBY_T_UNDEF +#define T_NODE RUBY_T_NODE +#define T_ZOMBIE RUBY_T_ZOMBIE +#define T_MASK RUBY_T_MASK + +#define BUILTIN_TYPE(x) (int)(((struct RBasic*)(x))->flags & T_MASK) + +static inline int rb_type(VALUE obj); +#define TYPE(x) rb_type((VALUE)(x)) + +/* RB_GC_GUARD_PTR() is an intermediate macro, and has no effect by + * itself. don't use it directly */ +#ifdef __GNUC__ +#define RB_GC_GUARD_PTR(ptr) \ + __extension__ ({volatile VALUE *rb_gc_guarded_ptr = (ptr); rb_gc_guarded_ptr;}) +#else +#ifdef _MSC_VER +#pragma optimize("", off) +static inline volatile VALUE *rb_gc_guarded_ptr(volatile VALUE *ptr) {return ptr;} +#pragma optimize("", on) +#else +volatile VALUE *rb_gc_guarded_ptr(volatile VALUE *ptr); +#define HAVE_RB_GC_GUARDED_PTR 1 +#endif +#define RB_GC_GUARD_PTR(ptr) rb_gc_guarded_ptr(ptr) +#endif +#define RB_GC_GUARD(v) (*RB_GC_GUARD_PTR(&(v))) + +#ifdef __GNUC__ +#define RB_UNUSED_VAR(x) x __attribute__ ((unused)) +#else +#define RB_UNUSED_VAR(x) x +#endif + +void rb_check_type(VALUE,int); +#define Check_Type(v,t) rb_check_type((VALUE)(v),(t)) + +VALUE rb_str_to_str(VALUE); +VALUE rb_string_value(volatile VALUE*); +char *rb_string_value_ptr(volatile VALUE*); +char *rb_string_value_cstr(volatile VALUE*); + +#define StringValue(v) rb_string_value(&(v)) +#define StringValuePtr(v) rb_string_value_ptr(&(v)) +#define StringValueCStr(v) rb_string_value_cstr(&(v)) + +void rb_check_safe_obj(VALUE); +DEPRECATED(void rb_check_safe_str(VALUE)); +#define SafeStringValue(v) do {\ + StringValue(v);\ + rb_check_safe_obj(v);\ +} while (0) +/* obsolete macro - use SafeStringValue(v) */ +#define Check_SafeStr(v) rb_check_safe_str((VALUE)(v)) + +VALUE rb_str_export(VALUE); +#define ExportStringValue(v) do {\ + SafeStringValue(v);\ + (v) = rb_str_export(v);\ +} while (0) +VALUE rb_str_export_locale(VALUE); + +VALUE rb_get_path(VALUE); +#define FilePathValue(v) (RB_GC_GUARD(v) = rb_get_path(v)) + +VALUE rb_get_path_no_checksafe(VALUE); +#define FilePathStringValue(v) ((v) = rb_get_path_no_checksafe(v)) + +#define RUBY_SAFE_LEVEL_MAX 3 +void rb_secure(int); +int rb_safe_level(void); +void rb_set_safe_level(int); +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) +int ruby_safe_level_4_error(void) __attribute__((error("$SAFE=4 is obsolete"))); +int ruby_safe_level_4_warning(void) __attribute__((warning("$SAFE=4 is obsolete"))); +# ifdef RUBY_EXPORT +# define ruby_safe_level_4_warning() ruby_safe_level_4_error() +# endif +#define RUBY_SAFE_LEVEL_INVALID_P(level) \ + __extension__(__builtin_constant_p(level) && \ + ((level) < 0 || RUBY_SAFE_LEVEL_MAX < (level))) +#define RUBY_SAFE_LEVEL_CHECK(level, type) \ + (RUBY_SAFE_LEVEL_INVALID_P(level) ? ruby_safe_level_4_##type() : (level)) +#define rb_secure(level) rb_secure(RUBY_SAFE_LEVEL_CHECK(level, warning)) +#define rb_set_safe_level(level) rb_set_safe_level(RUBY_SAFE_LEVEL_CHECK(level, error)) +#endif +void rb_set_safe_level_force(int); +void rb_secure_update(VALUE); +NORETURN(void rb_insecure_operation(void)); + +VALUE rb_errinfo(void); +void rb_set_errinfo(VALUE); + +SIGNED_VALUE rb_num2long(VALUE); +VALUE rb_num2ulong(VALUE); +static inline long +rb_num2long_inline(VALUE x) +{ + if (FIXNUM_P(x)) + return FIX2LONG(x); + else + return (long)rb_num2long(x); +} +#define NUM2LONG(x) rb_num2long_inline(x) +static inline unsigned long +rb_num2ulong_inline(VALUE x) +{ + if (FIXNUM_P(x)) + return (unsigned long)FIX2LONG(x); + else + return (unsigned long)rb_num2ulong(x); +} +#define NUM2ULONG(x) rb_num2ulong_inline(x) +#if SIZEOF_INT < SIZEOF_LONG +long rb_num2int(VALUE); +long rb_fix2int(VALUE); +#define FIX2INT(x) ((int)rb_fix2int((VALUE)(x))) + +static inline int +rb_num2int_inline(VALUE x) +{ + if (FIXNUM_P(x)) + return FIX2INT(x); + else + return (int)rb_num2int(x); +} +#define NUM2INT(x) rb_num2int_inline(x) + +unsigned long rb_num2uint(VALUE); +#define NUM2UINT(x) ((unsigned int)rb_num2uint(x)) +unsigned long rb_fix2uint(VALUE); +#define FIX2UINT(x) ((unsigned int)rb_fix2uint(x)) +#else /* SIZEOF_INT < SIZEOF_LONG */ +#define NUM2INT(x) ((int)NUM2LONG(x)) +#define NUM2UINT(x) ((unsigned int)NUM2ULONG(x)) +#define FIX2INT(x) ((int)FIX2LONG(x)) +#define FIX2UINT(x) ((unsigned int)FIX2ULONG(x)) +#endif /* SIZEOF_INT < SIZEOF_LONG */ + +short rb_num2short(VALUE); +unsigned short rb_num2ushort(VALUE); +short rb_fix2short(VALUE); +unsigned short rb_fix2ushort(VALUE); +#define FIX2SHORT(x) (rb_fix2short((VALUE)(x))) +static inline short +rb_num2short_inline(VALUE x) +{ + if (FIXNUM_P(x)) + return FIX2SHORT(x); + else + return rb_num2short(x); +} + +#define NUM2SHORT(x) rb_num2short_inline(x) +#define NUM2USHORT(x) rb_num2ushort(x) + +#ifdef HAVE_LONG_LONG +LONG_LONG rb_num2ll(VALUE); +unsigned LONG_LONG rb_num2ull(VALUE); +static inline LONG_LONG +rb_num2ll_inline(VALUE x) +{ + if (FIXNUM_P(x)) + return FIX2LONG(x); + else + return rb_num2ll(x); +} +# define NUM2LL(x) rb_num2ll_inline(x) +# define NUM2ULL(x) rb_num2ull(x) +#endif + +#if !defined(NUM2OFFT) +# if defined(HAVE_LONG_LONG) && SIZEOF_OFF_T > SIZEOF_LONG +# define NUM2OFFT(x) ((off_t)NUM2LL(x)) +# else +# define NUM2OFFT(x) NUM2LONG(x) +# endif +#endif + +#if defined(HAVE_LONG_LONG) && SIZEOF_SIZE_T > SIZEOF_LONG +# define NUM2SIZET(x) ((size_t)NUM2ULL(x)) +# define NUM2SSIZET(x) ((ssize_t)NUM2LL(x)) +#else +# define NUM2SIZET(x) NUM2ULONG(x) +# define NUM2SSIZET(x) NUM2LONG(x) +#endif + +double rb_num2dbl(VALUE); +#define NUM2DBL(x) rb_num2dbl((VALUE)(x)) + +VALUE rb_uint2big(VALUE); +VALUE rb_int2big(SIGNED_VALUE); + +VALUE rb_newobj(void); +VALUE rb_newobj_of(VALUE, VALUE); +VALUE rb_obj_setup(VALUE obj, VALUE klass, VALUE type); +#define NEWOBJ(obj,type) type *(obj) = (type*)rb_newobj() +#define NEWOBJ_OF(obj,type,klass,flags) type *(obj) = (type*)rb_newobj_of(klass, flags) +#define OBJSETUP(obj,c,t) rb_obj_setup(obj, c, t) /* use NEWOBJ_OF instead of NEWOBJ()+OBJSETUP() */ +#define CLONESETUP(clone,obj) do {\ + OBJSETUP((clone),rb_singleton_class_clone((VALUE)(obj)),RBASIC(obj)->flags);\ + rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)(clone));\ + if (FL_TEST((obj), FL_EXIVAR)) rb_copy_generic_ivar((VALUE)(clone),(VALUE)(obj));\ +} while (0) +#define DUPSETUP(dup,obj) do {\ + OBJSETUP((dup),rb_obj_class(obj), (RBASIC(obj)->flags)&(T_MASK|FL_EXIVAR|FL_TAINT)); \ + if (FL_TEST((obj), FL_EXIVAR)) rb_copy_generic_ivar((VALUE)(dup),(VALUE)(obj));\ +} while (0) + +#ifndef USE_RGENGC +#define USE_RGENGC 1 +#endif + +#ifndef RGENGC_WB_PROTECTED_ARRAY +#define RGENGC_WB_PROTECTED_ARRAY 1 +#endif +#ifndef RGENGC_WB_PROTECTED_HASH +#define RGENGC_WB_PROTECTED_HASH 1 +#endif +#ifndef RGENGC_WB_PROTECTED_STRUCT +#define RGENGC_WB_PROTECTED_STRUCT 1 +#endif +#ifndef RGENGC_WB_PROTECTED_STRING +#define RGENGC_WB_PROTECTED_STRING 1 +#endif +#ifndef RGENGC_WB_PROTECTED_OBJECT +#define RGENGC_WB_PROTECTED_OBJECT 1 +#endif +#ifndef RGENGC_WB_PROTECTED_REGEXP +#define RGENGC_WB_PROTECTED_REGEXP 1 +#endif +#ifndef RGENGC_WB_PROTECTED_CLASS +#define RGENGC_WB_PROTECTED_CLASS 1 +#endif +#ifndef RGENGC_WB_PROTECTED_FLOAT +#define RGENGC_WB_PROTECTED_FLOAT 1 +#endif +#ifndef RGENGC_WB_PROTECTED_COMPLEX +#define RGENGC_WB_PROTECTED_COMPLEX 1 +#endif +#ifndef RGENGC_WB_PROTECTED_RATIONAL +#define RGENGC_WB_PROTECTED_RATIONAL 1 +#endif +#ifndef RGENGC_WB_PROTECTED_BIGNUM +#define RGENGC_WB_PROTECTED_BIGNUM 1 +#endif +#ifndef RGENGC_WB_PROTECTED_NODE_CREF +#define RGENGC_WB_PROTECTED_NODE_CREF 1 +#endif + +struct RBasic { + VALUE flags; + const VALUE klass; +} +#ifdef __GNUC__ + __attribute__((aligned(sizeof(VALUE)))) +#endif +; + +VALUE rb_obj_hide(VALUE obj); +VALUE rb_obj_reveal(VALUE obj, VALUE klass); /* do not use this API to change klass information */ + +#define RBASIC_CLASS(obj) (RBASIC(obj)->klass) + +#define ROBJECT_EMBED_LEN_MAX 3 +struct RObject { + struct RBasic basic; + union { + struct { + long numiv; + VALUE *ivptr; + struct st_table *iv_index_tbl; /* shortcut for RCLASS_IV_INDEX_TBL(rb_obj_class(obj)) */ + } heap; + VALUE ary[ROBJECT_EMBED_LEN_MAX]; + } as; +}; +#define ROBJECT_EMBED FL_USER1 +#define ROBJECT_NUMIV(o) \ + ((RBASIC(o)->flags & ROBJECT_EMBED) ? \ + ROBJECT_EMBED_LEN_MAX : \ + ROBJECT(o)->as.heap.numiv) +#define ROBJECT_IVPTR(o) \ + ((RBASIC(o)->flags & ROBJECT_EMBED) ? \ + ROBJECT(o)->as.ary : \ + ROBJECT(o)->as.heap.ivptr) +#define ROBJECT_IV_INDEX_TBL(o) \ + ((RBASIC(o)->flags & ROBJECT_EMBED) ? \ + RCLASS_IV_INDEX_TBL(rb_obj_class(o)) : \ + ROBJECT(o)->as.heap.iv_index_tbl) + +/** @internal */ +typedef struct rb_classext_struct rb_classext_t; + +struct RClass { + struct RBasic basic; + VALUE super; + rb_classext_t *ptr; + struct method_table_wrapper *m_tbl_wrapper; +}; +#define RCLASS_SUPER(c) rb_class_get_superclass(c) +#define RMODULE_IV_TBL(m) RCLASS_IV_TBL(m) +#define RMODULE_CONST_TBL(m) RCLASS_CONST_TBL(m) +#define RMODULE_M_TBL(m) RCLASS_M_TBL(m) +#define RMODULE_SUPER(m) RCLASS_SUPER(m) +#define RMODULE_IS_OVERLAID FL_USER2 +#define RMODULE_IS_REFINEMENT FL_USER3 +#define RMODULE_INCLUDED_INTO_REFINEMENT FL_USER4 + +struct RFloat { + struct RBasic basic; + double float_value; +}; + +double rb_float_value(VALUE); +VALUE rb_float_new(double); +VALUE rb_float_new_in_heap(double); + +#define RFLOAT_VALUE(v) rb_float_value(v) +#define DBL2NUM(dbl) rb_float_new(dbl) + +#define ELTS_SHARED FL_USER2 + +#define RSTRING_EMBED_LEN_MAX ((int)((sizeof(VALUE)*3)/sizeof(char)-1)) +struct RString { + struct RBasic basic; + union { + struct { + long len; + char *ptr; + union { + long capa; + VALUE shared; + } aux; + } heap; + char ary[RSTRING_EMBED_LEN_MAX + 1]; + } as; +}; +#define RSTRING_NOEMBED FL_USER1 +#define RSTRING_FSTR FL_USER17 +#define RSTRING_EMBED_LEN_MASK (FL_USER2|FL_USER3|FL_USER4|FL_USER5|FL_USER6) +#define RSTRING_EMBED_LEN_SHIFT (FL_USHIFT+2) +#define RSTRING_EMBED_LEN(str) \ + (long)((RBASIC(str)->flags >> RSTRING_EMBED_LEN_SHIFT) & \ + (RSTRING_EMBED_LEN_MASK >> RSTRING_EMBED_LEN_SHIFT)) +#define RSTRING_LEN(str) \ + (!(RBASIC(str)->flags & RSTRING_NOEMBED) ? \ + RSTRING_EMBED_LEN(str) : \ + RSTRING(str)->as.heap.len) +#define RSTRING_PTR(str) \ + (!(RBASIC(str)->flags & RSTRING_NOEMBED) ? \ + RSTRING(str)->as.ary : \ + RSTRING(str)->as.heap.ptr) +#define RSTRING_END(str) \ + (!(RBASIC(str)->flags & RSTRING_NOEMBED) ? \ + (RSTRING(str)->as.ary + RSTRING_EMBED_LEN(str)) : \ + (RSTRING(str)->as.heap.ptr + RSTRING(str)->as.heap.len)) +#define RSTRING_LENINT(str) rb_long2int(RSTRING_LEN(str)) +#define RSTRING_GETMEM(str, ptrvar, lenvar) \ + (!(RBASIC(str)->flags & RSTRING_NOEMBED) ? \ + ((ptrvar) = RSTRING(str)->as.ary, (lenvar) = RSTRING_EMBED_LEN(str)) : \ + ((ptrvar) = RSTRING(str)->as.heap.ptr, (lenvar) = RSTRING(str)->as.heap.len)) + +#define RARRAY_EMBED_LEN_MAX 3 +struct RArray { + struct RBasic basic; + union { + struct { + long len; + union { + long capa; + VALUE shared; + } aux; + const VALUE *ptr; + } heap; + const VALUE ary[RARRAY_EMBED_LEN_MAX]; + } as; +}; +#define RARRAY_EMBED_FLAG FL_USER1 +/* FL_USER2 is for ELTS_SHARED */ +#define RARRAY_EMBED_LEN_MASK (FL_USER4|FL_USER3) +#define RARRAY_EMBED_LEN_SHIFT (FL_USHIFT+3) +#define RARRAY_LEN(a) \ + ((RBASIC(a)->flags & RARRAY_EMBED_FLAG) ? \ + (long)((RBASIC(a)->flags >> RARRAY_EMBED_LEN_SHIFT) & \ + (RARRAY_EMBED_LEN_MASK >> RARRAY_EMBED_LEN_SHIFT)) : \ + RARRAY(a)->as.heap.len) + +#define RARRAY_LENINT(ary) rb_long2int(RARRAY_LEN(ary)) + +#define RARRAY_CONST_PTR(a) \ + ((const VALUE *)((RBASIC(a)->flags & RARRAY_EMBED_FLAG) ? \ + RARRAY(a)->as.ary : \ + RARRAY(a)->as.heap.ptr)) + +#define RARRAY_PTR_USE_START(a) ((VALUE *)RARRAY_CONST_PTR(a)) +#define RARRAY_PTR_USE_END(a) /* */ + +#define RARRAY_PTR_USE(ary, ptr_name, expr) do { \ + const VALUE _ary = (ary); \ + VALUE *ptr_name = (VALUE *)RARRAY_PTR_USE_START(_ary); \ + expr; \ + RARRAY_PTR_USE_END(_ary); \ +} while (0) + +#define RARRAY_AREF(a, i) (RARRAY_CONST_PTR(a)[i]) +#define RARRAY_ASET(a, i, v) do { \ + const VALUE _ary_ = (a); \ + RB_OBJ_WRITE(_ary_, &RARRAY_CONST_PTR(_ary_)[i], (v)); \ +} while (0) + +#define RARRAY_PTR(a) ((VALUE *)RARRAY_CONST_PTR(RGENGC_WB_PROTECTED_ARRAY ? OBJ_WB_UNPROTECT((VALUE)a) : ((VALUE)a))) + +struct RRegexp { + struct RBasic basic; + struct re_pattern_buffer *ptr; + const VALUE src; + unsigned long usecnt; +}; +#define RREGEXP_SRC(r) RREGEXP(r)->src +#define RREGEXP_SRC_PTR(r) RSTRING_PTR(RREGEXP(r)->src) +#define RREGEXP_SRC_LEN(r) RSTRING_LEN(RREGEXP(r)->src) +#define RREGEXP_SRC_END(r) RSTRING_END(RREGEXP(r)->src) + +struct RHash { + struct RBasic basic; + struct st_table *ntbl; /* possibly 0 */ + int iter_lev; + const VALUE ifnone; +}; +/* RHASH_TBL allocates st_table if not available. */ +#define RHASH_TBL(h) rb_hash_tbl(h) +#define RHASH_ITER_LEV(h) (RHASH(h)->iter_lev) +#define RHASH_IFNONE(h) (RHASH(h)->ifnone) +#define RHASH_SIZE(h) (RHASH(h)->ntbl ? (st_index_t)RHASH(h)->ntbl->num_entries : 0) +#define RHASH_EMPTY_P(h) (RHASH_SIZE(h) == 0) +#define RHASH_SET_IFNONE(h, ifnone) rb_hash_set_ifnone((VALUE)h, ifnone) + +struct RFile { + struct RBasic basic; + struct rb_io_t *fptr; +}; + +struct RRational { + struct RBasic basic; + const VALUE num; + const VALUE den; +}; + +#define RRATIONAL_SET_NUM(rat, n) RB_OBJ_WRITE((rat), &((struct RRational *)(rat))->num,(n)) +#define RRATIONAL_SET_DEN(rat, d) RB_OBJ_WRITE((rat), &((struct RRational *)(rat))->den,(d)) + +struct RComplex { + struct RBasic basic; + const VALUE real; + const VALUE imag; +}; + +#define RCOMPLEX_SET_REAL(cmp, r) RB_OBJ_WRITE((cmp), &((struct RComplex *)(cmp))->real,(r)) +#define RCOMPLEX_SET_IMAG(cmp, i) RB_OBJ_WRITE((cmp), &((struct RComplex *)(cmp))->imag,(i)) + +struct RData { + struct RBasic basic; + void (*dmark)(void*); + void (*dfree)(void*); + void *data; +}; + +typedef struct rb_data_type_struct rb_data_type_t; + +struct rb_data_type_struct { + const char *wrap_struct_name; + struct { + void (*dmark)(void*); + void (*dfree)(void*); + size_t (*dsize)(const void *); + void *reserved[2]; /* For future extension. + This array *must* be filled with ZERO. */ + } function; + const rb_data_type_t *parent; + void *data; /* This area can be used for any purpose + by a programmer who define the type. */ + VALUE flags; /* FL_WB_PROTECTED */ +}; + +#define HAVE_TYPE_RB_DATA_TYPE_T 1 +#define HAVE_RB_DATA_TYPE_T_FUNCTION 1 +#define HAVE_RB_DATA_TYPE_T_PARENT 1 + +struct RTypedData { + struct RBasic basic; + const rb_data_type_t *type; + VALUE typed_flag; /* 1 or not */ + void *data; +}; + +#define DATA_PTR(dta) (RDATA(dta)->data) + +#define RTYPEDDATA_P(v) (RTYPEDDATA(v)->typed_flag == 1) +#define RTYPEDDATA_TYPE(v) (RTYPEDDATA(v)->type) +#define RTYPEDDATA_DATA(v) (RTYPEDDATA(v)->data) + +/* +#define RUBY_DATA_FUNC(func) ((void (*)(void*))(func)) +*/ +typedef void (*RUBY_DATA_FUNC)(void*); + +VALUE rb_data_object_alloc(VALUE,void*,RUBY_DATA_FUNC,RUBY_DATA_FUNC); +VALUE rb_data_typed_object_alloc(VALUE klass, void *datap, const rb_data_type_t *); +int rb_typeddata_inherited_p(const rb_data_type_t *child, const rb_data_type_t *parent); +int rb_typeddata_is_kind_of(VALUE, const rb_data_type_t *); +void *rb_check_typeddata(VALUE, const rb_data_type_t *); +#define Check_TypedStruct(v,t) rb_check_typeddata((VALUE)(v),(t)) +#define RUBY_DEFAULT_FREE ((RUBY_DATA_FUNC)-1) +#define RUBY_NEVER_FREE ((RUBY_DATA_FUNC)0) +#define RUBY_TYPED_DEFAULT_FREE RUBY_DEFAULT_FREE +#define RUBY_TYPED_NEVER_FREE RUBY_NEVER_FREE + +/* bits for rb_data_type_struct::flags */ +#define RUBY_TYPED_FREE_IMMEDIATELY 1 /* TYPE field */ +#define RUBY_TYPED_WB_PROTECTED FL_WB_PROTECTED + +#define Data_Wrap_Struct(klass,mark,free,sval)\ + rb_data_object_alloc((klass),(sval),(RUBY_DATA_FUNC)(mark),(RUBY_DATA_FUNC)(free)) + +#define Data_Make_Struct(klass,type,mark,free,sval) (\ + (sval) = ALLOC(type),\ + memset((sval), 0, sizeof(type)),\ + Data_Wrap_Struct((klass),(mark),(free),(sval))\ +) + +#define TypedData_Wrap_Struct(klass,data_type,sval)\ + rb_data_typed_object_alloc((klass),(sval),(data_type)) + +#define TypedData_Make_Struct(klass, type, data_type, sval) (\ + (sval) = ALLOC(type),\ + memset((sval), 0, sizeof(type)),\ + TypedData_Wrap_Struct((klass),(data_type),(sval))\ +) + +#define Data_Get_Struct(obj,type,sval) do {\ + Check_Type((obj), T_DATA); \ + (sval) = (type*)DATA_PTR(obj);\ +} while (0) + +#define TypedData_Get_Struct(obj,type,data_type,sval) do {\ + (sval) = (type*)rb_check_typeddata((obj), (data_type)); \ +} while (0) + +#define RSTRUCT_EMBED_LEN_MAX 3 +struct RStruct { + struct RBasic basic; + union { + struct { + long len; + const VALUE *ptr; + } heap; + const VALUE ary[RSTRUCT_EMBED_LEN_MAX]; + } as; +}; +#define RSTRUCT_EMBED_LEN_MASK (FL_USER2|FL_USER1) +#define RSTRUCT_EMBED_LEN_SHIFT (FL_USHIFT+1) +#define RSTRUCT_LEN(st) \ + ((RBASIC(st)->flags & RSTRUCT_EMBED_LEN_MASK) ? \ + (long)((RBASIC(st)->flags >> RSTRUCT_EMBED_LEN_SHIFT) & \ + (RSTRUCT_EMBED_LEN_MASK >> RSTRUCT_EMBED_LEN_SHIFT)) : \ + RSTRUCT(st)->as.heap.len) +#define RSTRUCT_LENINT(st) rb_long2int(RSTRUCT_LEN(st)) +#define RSTRUCT_CONST_PTR(st) \ + ((RBASIC(st)->flags & RSTRUCT_EMBED_LEN_MASK) ? \ + RSTRUCT(st)->as.ary : \ + RSTRUCT(st)->as.heap.ptr) +#define RSTRUCT_PTR(st) ((VALUE *)RSTRUCT_CONST_PTR(RGENGC_WB_PROTECTED_STRUCT ? OBJ_WB_UNPROTECT((VALUE)st) : (VALUE)st)) + +#define RSTRUCT_SET(st, idx, v) RB_OBJ_WRITE(st, &RSTRUCT_CONST_PTR(st)[idx], (v)) +#define RSTRUCT_GET(st, idx) (RSTRUCT_CONST_PTR(st)[idx]) + +#define RBIGNUM_EMBED_LEN_NUMBITS 3 +#ifndef RBIGNUM_EMBED_LEN_MAX +# if (SIZEOF_VALUE*3/SIZEOF_ACTUAL_BDIGIT) < (1 << RBIGNUM_EMBED_LEN_NUMBITS)-1 +# define RBIGNUM_EMBED_LEN_MAX (SIZEOF_VALUE*3/SIZEOF_ACTUAL_BDIGIT) +# else +# define RBIGNUM_EMBED_LEN_MAX ((1 << RBIGNUM_EMBED_LEN_NUMBITS)-1) +# endif +#endif +struct RBignum { + struct RBasic basic; + union { + struct { + long len; + BDIGIT *digits; + } heap; + BDIGIT ary[RBIGNUM_EMBED_LEN_MAX]; + } as; +}; +#define RBIGNUM_SIGN_BIT FL_USER1 +/* sign: positive:1, negative:0 */ +#define RBIGNUM_SIGN(b) ((RBASIC(b)->flags & RBIGNUM_SIGN_BIT) != 0) +#define RBIGNUM_SET_SIGN(b,sign) \ + ((sign) ? (RBASIC(b)->flags |= RBIGNUM_SIGN_BIT) \ + : (RBASIC(b)->flags &= ~RBIGNUM_SIGN_BIT)) +#define RBIGNUM_POSITIVE_P(b) RBIGNUM_SIGN(b) +#define RBIGNUM_NEGATIVE_P(b) (!RBIGNUM_SIGN(b)) + +#define RBIGNUM_EMBED_FLAG FL_USER2 +#define RBIGNUM_EMBED_LEN_MASK (FL_USER5|FL_USER4|FL_USER3) +#define RBIGNUM_EMBED_LEN_SHIFT (FL_USHIFT+RBIGNUM_EMBED_LEN_NUMBITS) +#define RBIGNUM_LEN(b) \ + ((RBASIC(b)->flags & RBIGNUM_EMBED_FLAG) ? \ + (long)((RBASIC(b)->flags >> RBIGNUM_EMBED_LEN_SHIFT) & \ + (RBIGNUM_EMBED_LEN_MASK >> RBIGNUM_EMBED_LEN_SHIFT)) : \ + RBIGNUM(b)->as.heap.len) +/* LSB:RBIGNUM_DIGITS(b)[0], MSB:RBIGNUM_DIGITS(b)[RBIGNUM_LEN(b)-1] */ +#define RBIGNUM_DIGITS(b) \ + ((RBASIC(b)->flags & RBIGNUM_EMBED_FLAG) ? \ + RBIGNUM(b)->as.ary : \ + RBIGNUM(b)->as.heap.digits) +#define RBIGNUM_LENINT(b) rb_long2int(RBIGNUM_LEN(b)) + +#define R_CAST(st) (struct st*) +#define RBASIC(obj) (R_CAST(RBasic)(obj)) +#define ROBJECT(obj) (R_CAST(RObject)(obj)) +#define RCLASS(obj) (R_CAST(RClass)(obj)) +#define RMODULE(obj) RCLASS(obj) +#define RFLOAT(obj) (R_CAST(RFloat)(obj)) +#define RSTRING(obj) (R_CAST(RString)(obj)) +#define RREGEXP(obj) (R_CAST(RRegexp)(obj)) +#define RARRAY(obj) (R_CAST(RArray)(obj)) +#define RHASH(obj) (R_CAST(RHash)(obj)) +#define RDATA(obj) (R_CAST(RData)(obj)) +#define RTYPEDDATA(obj) (R_CAST(RTypedData)(obj)) +#define RSTRUCT(obj) (R_CAST(RStruct)(obj)) +#define RBIGNUM(obj) (R_CAST(RBignum)(obj)) +#define RFILE(obj) (R_CAST(RFile)(obj)) +#define RRATIONAL(obj) (R_CAST(RRational)(obj)) +#define RCOMPLEX(obj) (R_CAST(RComplex)(obj)) + +#define FL_SINGLETON FL_USER0 +#define FL_WB_PROTECTED (((VALUE)1)<<5) +#define FL_PROMOTED (((VALUE)1)<<6) +#define FL_FINALIZE (((VALUE)1)<<7) +#define FL_TAINT (((VALUE)1)<<8) +#define FL_UNTRUSTED FL_TAINT +#define FL_EXIVAR (((VALUE)1)<<10) +#define FL_FREEZE (((VALUE)1)<<11) + +#define FL_USHIFT 12 + +#define FL_USER0 (((VALUE)1)<<(FL_USHIFT+0)) +#define FL_USER1 (((VALUE)1)<<(FL_USHIFT+1)) +#define FL_USER2 (((VALUE)1)<<(FL_USHIFT+2)) +#define FL_USER3 (((VALUE)1)<<(FL_USHIFT+3)) +#define FL_USER4 (((VALUE)1)<<(FL_USHIFT+4)) +#define FL_USER5 (((VALUE)1)<<(FL_USHIFT+5)) +#define FL_USER6 (((VALUE)1)<<(FL_USHIFT+6)) +#define FL_USER7 (((VALUE)1)<<(FL_USHIFT+7)) +#define FL_USER8 (((VALUE)1)<<(FL_USHIFT+8)) +#define FL_USER9 (((VALUE)1)<<(FL_USHIFT+9)) +#define FL_USER10 (((VALUE)1)<<(FL_USHIFT+10)) +#define FL_USER11 (((VALUE)1)<<(FL_USHIFT+11)) +#define FL_USER12 (((VALUE)1)<<(FL_USHIFT+12)) +#define FL_USER13 (((VALUE)1)<<(FL_USHIFT+13)) +#define FL_USER14 (((VALUE)1)<<(FL_USHIFT+14)) +#define FL_USER15 (((VALUE)1)<<(FL_USHIFT+15)) +#define FL_USER16 (((VALUE)1)<<(FL_USHIFT+16)) +#define FL_USER17 (((VALUE)1)<<(FL_USHIFT+17)) +#define FL_USER18 (((VALUE)1)<<(FL_USHIFT+18)) +#define FL_USER19 (((VALUE)1)<<(FL_USHIFT+19)) + +#define SPECIAL_CONST_P(x) (IMMEDIATE_P(x) || !RTEST(x)) + +#define FL_ABLE(x) (!SPECIAL_CONST_P(x) && BUILTIN_TYPE(x) != T_NODE) +#define FL_TEST_RAW(x,f) (RBASIC(x)->flags&(f)) +#define FL_TEST(x,f) (FL_ABLE(x)?FL_TEST_RAW((x),(f)):0) +#define FL_ANY(x,f) FL_TEST((x),(f)) +#define FL_ALL(x,f) (FL_TEST((x),(f)) == (f)) +#define FL_SET(x,f) do {if (FL_ABLE(x)) RBASIC(x)->flags |= (f);} while (0) +#define FL_UNSET(x,f) do {if (FL_ABLE(x)) RBASIC(x)->flags &= ~(f);} while (0) +#define FL_REVERSE(x,f) do {if (FL_ABLE(x)) RBASIC(x)->flags ^= (f);} while (0) + +#define OBJ_TAINTED(x) (!!FL_TEST((x), FL_TAINT)) +#define OBJ_TAINT(x) FL_SET((x), FL_TAINT) +#define OBJ_UNTRUSTED(x) OBJ_TAINTED(x) +#define OBJ_UNTRUST(x) OBJ_TAINT(x) +#define OBJ_INFECT(x,s) do { \ + if (FL_ABLE(x) && FL_ABLE(s)) \ + RBASIC(x)->flags |= RBASIC(s)->flags & FL_TAINT; \ +} while (0) + +#define OBJ_FROZEN(x) (!!(FL_ABLE(x)?(RBASIC(x)->flags&(FL_FREEZE)):(FIXNUM_P(x)||FLONUM_P(x)||SYMBOL_P(x)))) +#define OBJ_FREEZE(x) FL_SET((x), FL_FREEZE) + +#if USE_RGENGC +#define OBJ_PROMOTED(x) (SPECIAL_CONST_P(x) ? 0 : FL_TEST_RAW((x), FL_PROMOTED)) +#define OBJ_WB_PROTECTED(x) (SPECIAL_CONST_P(x) ? 1 : FL_TEST_RAW((x), FL_WB_PROTECTED)) +#define OBJ_WB_UNPROTECT(x) rb_obj_wb_unprotect(x, __FILE__, __LINE__) + +void rb_gc_writebarrier(VALUE a, VALUE b); +void rb_gc_writebarrier_unprotect_promoted(VALUE obj); + +#else /* USE_RGENGC */ +#define OBJ_PROMOTED(x) 0 +#define OBJ_WB_PROTECTED(x) 0 +#define OBJ_WB_UNPROTECT(x) rb_obj_wb_unprotect(x, __FILE__, __LINE__) +#endif + +/* Write barrier (WB) interfaces: + * - RB_OBJ_WRITE(a, slot, b): WB for new reference from `a' to `b'. + * Write `b' into `*slot'. `slot' is a pointer in `a'. + * - RB_OBJ_WRITTEN(a, oldv, b): WB for new reference from `a' to `b'. + * This doesn't write any values, but only a WB declaration. + * `oldv' is replaced value with `b' (not used in current Ruby). + * + * NOTE: The following core interfaces can be changed in the future. + * Please catch up if you want to insert WB into C-extensions + * correctly. + */ +#define RB_OBJ_WRITE(a, slot, b) rb_obj_write((VALUE)(a), (VALUE *)(slot), (VALUE)(b), __FILE__, __LINE__) +#define RB_OBJ_WRITTEN(a, oldv, b) rb_obj_written((VALUE)(a), (VALUE)(oldv), (VALUE)(b), __FILE__, __LINE__) + +#ifndef USE_RGENGC_LOGGING_WB_UNPROTECT +#define USE_RGENGC_LOGGING_WB_UNPROTECT 0 +#endif + +#if USE_RGENGC_LOGGING_WB_UNPROTECT +void rb_gc_unprotect_logging(void *objptr, const char *filename, int line); +#define RGENGC_LOGGING_WB_UNPROTECT rb_gc_unprotect_logging +#endif + +static inline VALUE +rb_obj_wb_unprotect(VALUE x, RB_UNUSED_VAR(const char *filename), RB_UNUSED_VAR(int line)) +{ +#ifdef RGENGC_LOGGING_WB_UNPROTECT + RGENGC_LOGGING_WB_UNPROTECT((void *)x, filename, line); +#endif + +#if USE_RGENGC + /* `x' should be an RVALUE object */ + if (FL_TEST_RAW((x), FL_WB_PROTECTED)) { + if (FL_TEST_RAW((x), FL_PROMOTED)) { + rb_gc_writebarrier_unprotect_promoted(x); + } + RBASIC(x)->flags &= ~FL_WB_PROTECTED; + } +#endif + return x; +} + +static inline VALUE +rb_obj_written(VALUE a, RB_UNUSED_VAR(VALUE oldv), VALUE b, RB_UNUSED_VAR(const char *filename), RB_UNUSED_VAR(int line)) +{ +#ifdef RGENGC_LOGGING_OBJ_WRITTEN + RGENGC_LOGGING_OBJ_WRITTEN(a, oldv, b, filename, line); +#endif + +#if USE_RGENGC + /* `a' should be an RVALUE object */ + if (FL_TEST_RAW((a), FL_PROMOTED) && !SPECIAL_CONST_P(b)) { + rb_gc_writebarrier(a, b); + } +#endif + + return a; +} + +static inline VALUE +rb_obj_write(VALUE a, VALUE *slot, VALUE b, RB_UNUSED_VAR(const char *filename), RB_UNUSED_VAR(int line)) +{ +#ifdef RGENGC_LOGGING_WRITE + RGENGC_LOGGING_WRITE(a, slot, b, filename, line); +#endif + + *slot = b; + +#if USE_RGENGC + rb_obj_written(a, Qundef /* ignore `oldv' now */, b, filename, line); +#endif + return a; +} + +#if SIZEOF_INT < SIZEOF_LONG +# define INT2NUM(v) INT2FIX((int)(v)) +# define UINT2NUM(v) LONG2FIX((unsigned int)(v)) +#else +static inline VALUE +rb_int2num_inline(int v) +{ + if (FIXABLE(v)) + return INT2FIX(v); + else + return rb_int2big(v); +} +#define INT2NUM(x) rb_int2num_inline(x) + +static inline VALUE +rb_uint2num_inline(unsigned int v) +{ + if (POSFIXABLE(v)) + return LONG2FIX(v); + else + return rb_uint2big(v); +} +#define UINT2NUM(x) rb_uint2num_inline(x) +#endif + +static inline VALUE +rb_long2num_inline(long v) +{ + if (FIXABLE(v)) + return LONG2FIX(v); + else + return rb_int2big(v); +} +#define LONG2NUM(x) rb_long2num_inline(x) + +static inline VALUE +rb_ulong2num_inline(unsigned long v) +{ + if (POSFIXABLE(v)) + return LONG2FIX(v); + else + return rb_uint2big(v); +} +#define ULONG2NUM(x) rb_ulong2num_inline(x) + +static inline char +rb_num2char_inline(VALUE x) +{ + if ((TYPE(x) == T_STRING) && (RSTRING_LEN(x)>=1)) + return RSTRING_PTR(x)[0]; + else + return (char)(NUM2INT(x) & 0xff); +} +#define NUM2CHR(x) rb_num2char_inline(x) + +#define CHR2FIX(x) INT2FIX((long)((x)&0xff)) + +#define ALLOC_N(type,n) ((type*)xmalloc2((n),sizeof(type))) +#define ALLOC(type) ((type*)xmalloc(sizeof(type))) +#define REALLOC_N(var,type,n) ((var)=(type*)xrealloc2((char*)(var),(n),sizeof(type))) + +#define ALLOCA_N(type,n) ((type*)alloca(sizeof(type)*(n))) + +void *rb_alloc_tmp_buffer(volatile VALUE *store, long len) RUBY_ATTR_ALLOC_SIZE((2)); +void rb_free_tmp_buffer(volatile VALUE *store); +/* allocates _n_ bytes temporary buffer and stores VALUE including it + * in _v_. _n_ may be evaluated twice. */ +#ifdef C_ALLOCA +# define ALLOCV(v, n) rb_alloc_tmp_buffer(&(v), (n)) +#else +# define ALLOCV(v, n) ((n) < 1024 ? (RB_GC_GUARD(v) = 0, alloca(n)) : rb_alloc_tmp_buffer(&(v), (n))) +#endif +#define ALLOCV_N(type, v, n) ((type*)ALLOCV((v), sizeof(type)*(n))) +#define ALLOCV_END(v) rb_free_tmp_buffer(&(v)) + +#define MEMZERO(p,type,n) memset((p), 0, sizeof(type)*(n)) +#define MEMCPY(p1,p2,type,n) memcpy((p1), (p2), sizeof(type)*(n)) +#define MEMMOVE(p1,p2,type,n) memmove((p1), (p2), sizeof(type)*(n)) +#define MEMCMP(p1,p2,type,n) memcmp((p1), (p2), sizeof(type)*(n)) + +void rb_obj_infect(VALUE,VALUE); + +typedef int ruby_glob_func(const char*,VALUE, void*); +void rb_glob(const char*,void(*)(const char*,VALUE,void*),VALUE); +int ruby_glob(const char*,int,ruby_glob_func*,VALUE); +int ruby_brace_glob(const char*,int,ruby_glob_func*,VALUE); + +VALUE rb_define_class(const char*,VALUE); +VALUE rb_define_module(const char*); +VALUE rb_define_class_under(VALUE, const char*, VALUE); +VALUE rb_define_module_under(VALUE, const char*); + +void rb_include_module(VALUE,VALUE); +void rb_extend_object(VALUE,VALUE); +void rb_prepend_module(VALUE,VALUE); + +struct rb_global_variable; + +typedef VALUE rb_gvar_getter_t(ID id, void *data, struct rb_global_variable *gvar); +typedef void rb_gvar_setter_t(VALUE val, ID id, void *data, struct rb_global_variable *gvar); +typedef void rb_gvar_marker_t(VALUE *var); + +VALUE rb_gvar_undef_getter(ID id, void *data, struct rb_global_variable *gvar); +void rb_gvar_undef_setter(VALUE val, ID id, void *data, struct rb_global_variable *gvar); +void rb_gvar_undef_marker(VALUE *var); + +VALUE rb_gvar_val_getter(ID id, void *data, struct rb_global_variable *gvar); +void rb_gvar_val_setter(VALUE val, ID id, void *data, struct rb_global_variable *gvar); +void rb_gvar_val_marker(VALUE *var); + +VALUE rb_gvar_var_getter(ID id, void *data, struct rb_global_variable *gvar); +void rb_gvar_var_setter(VALUE val, ID id, void *data, struct rb_global_variable *gvar); +void rb_gvar_var_marker(VALUE *var); + +void rb_gvar_readonly_setter(VALUE val, ID id, void *data, struct rb_global_variable *gvar); + +void rb_define_variable(const char*,VALUE*); +void rb_define_virtual_variable(const char*,VALUE(*)(ANYARGS),void(*)(ANYARGS)); +void rb_define_hooked_variable(const char*,VALUE*,VALUE(*)(ANYARGS),void(*)(ANYARGS)); +void rb_define_readonly_variable(const char*,VALUE*); +void rb_define_const(VALUE,const char*,VALUE); +void rb_define_global_const(const char*,VALUE); + +#define RUBY_METHOD_FUNC(func) ((VALUE (*)(ANYARGS))(func)) +void rb_define_method(VALUE,const char*,VALUE(*)(ANYARGS),int); +void rb_define_module_function(VALUE,const char*,VALUE(*)(ANYARGS),int); +void rb_define_global_function(const char*,VALUE(*)(ANYARGS),int); + +void rb_undef_method(VALUE,const char*); +void rb_define_alias(VALUE,const char*,const char*); +void rb_define_attr(VALUE,const char*,int,int); + +void rb_global_variable(VALUE*); +void rb_gc_register_mark_object(VALUE); +void rb_gc_register_address(VALUE*); +void rb_gc_unregister_address(VALUE*); + +ID rb_intern(const char*); +ID rb_intern2(const char*, long); +ID rb_intern_str(VALUE str); +const char *rb_id2name(ID); +ID rb_check_id(volatile VALUE *); +ID rb_to_id(VALUE); +VALUE rb_id2str(ID); + +#define CONST_ID_CACHE(result, str) \ + { \ + static ID rb_intern_id_cache; \ + if (!rb_intern_id_cache) \ + rb_intern_id_cache = rb_intern2((str), (long)strlen(str)); \ + result rb_intern_id_cache; \ + } +#define CONST_ID(var, str) \ + do CONST_ID_CACHE((var) =, (str)) while (0) +#ifdef __GNUC__ +/* __builtin_constant_p and statement expression is available + * since gcc-2.7.2.3 at least. */ +#define rb_intern(str) \ + (__builtin_constant_p(str) ? \ + __extension__ (CONST_ID_CACHE((ID), (str))) : \ + rb_intern(str)) +#define rb_intern_const(str) \ + (__builtin_constant_p(str) ? \ + __extension__ (rb_intern2((str), (long)strlen(str))) : \ + (rb_intern)(str)) +#else +#define rb_intern_const(str) rb_intern2((str), (long)strlen(str)) +#endif + +const char *rb_class2name(VALUE); +const char *rb_obj_classname(VALUE); + +void rb_p(VALUE); + +VALUE rb_eval_string(const char*); +VALUE rb_eval_string_protect(const char*, int*); +VALUE rb_eval_string_wrap(const char*, int*); +VALUE rb_funcall(VALUE, ID, int, ...); +VALUE rb_funcallv(VALUE, ID, int, const VALUE*); +VALUE rb_funcallv_public(VALUE, ID, int, const VALUE*); +#define rb_funcall2 rb_funcallv +#define rb_funcall3 rb_funcallv_public +VALUE rb_funcall_passing_block(VALUE, ID, int, const VALUE*); +VALUE rb_funcall_with_block(VALUE, ID, int, const VALUE*, VALUE); +int rb_scan_args(int, const VALUE*, const char*, ...); +VALUE rb_call_super(int, const VALUE*); + +/* rb_scan_args() format allows ':' for optional hash */ +#define HAVE_RB_SCAN_ARGS_OPTIONAL_HASH 1 + +VALUE rb_gv_set(const char*, VALUE); +VALUE rb_gv_get(const char*); +VALUE rb_iv_get(VALUE, const char*); +VALUE rb_iv_set(VALUE, const char*, VALUE); + +VALUE rb_equal(VALUE,VALUE); + +VALUE *rb_ruby_verbose_ptr(void); +VALUE *rb_ruby_debug_ptr(void); +#define ruby_verbose (*rb_ruby_verbose_ptr()) +#define ruby_debug (*rb_ruby_debug_ptr()) + +PRINTF_ARGS(NORETURN(void rb_raise(VALUE, const char*, ...)), 2, 3); +PRINTF_ARGS(NORETURN(void rb_fatal(const char*, ...)), 1, 2); +PRINTF_ARGS(NORETURN(void rb_bug(const char*, ...)), 1, 2); +NORETURN(void rb_bug_errno(const char*, int)); +NORETURN(void rb_sys_fail(const char*)); +NORETURN(void rb_sys_fail_str(VALUE)); +NORETURN(void rb_mod_sys_fail(VALUE, const char*)); +NORETURN(void rb_mod_sys_fail_str(VALUE, VALUE)); +NORETURN(void rb_readwrite_sys_fail(int, const char*)); +NORETURN(void rb_iter_break(void)); +NORETURN(void rb_iter_break_value(VALUE)); +NORETURN(void rb_exit(int)); +NORETURN(void rb_notimplement(void)); +VALUE rb_syserr_new(int, const char *); +VALUE rb_syserr_new_str(int n, VALUE arg); +NORETURN(void rb_syserr_fail(int, const char*)); +NORETURN(void rb_syserr_fail_str(int, VALUE)); +NORETURN(void rb_mod_syserr_fail(VALUE, int, const char*)); +NORETURN(void rb_mod_syserr_fail_str(VALUE, int, VALUE)); + +/* reports if `-W' specified */ +PRINTF_ARGS(void rb_warning(const char*, ...), 1, 2); +PRINTF_ARGS(void rb_compile_warning(const char *, int, const char*, ...), 3, 4); +PRINTF_ARGS(void rb_sys_warning(const char*, ...), 1, 2); +/* reports always */ +PRINTF_ARGS(void rb_warn(const char*, ...), 1, 2); +PRINTF_ARGS(void rb_compile_warn(const char *, int, const char*, ...), 3, 4); + +/* for rb_readwrite_sys_fail first argument */ +#define RB_IO_WAIT_READABLE 0 +#define RB_IO_WAIT_WRITABLE 1 + +#define RUBY_BLOCK_CALL_FUNC_TAKES_BLOCKARG 1 +#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg) \ + VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE *argv, VALUE blockarg +typedef VALUE rb_block_call_func(RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)); + +#if defined RB_BLOCK_CALL_FUNC_STRICT && RB_BLOCK_CALL_FUNC_STRICT +typedef rb_block_call_func *rb_block_call_func_t; +#else +typedef VALUE (*rb_block_call_func_t)(ANYARGS); +#endif + +VALUE rb_each(VALUE); +VALUE rb_yield(VALUE); +VALUE rb_yield_values(int n, ...); +VALUE rb_yield_values2(int n, const VALUE *argv); +VALUE rb_yield_splat(VALUE); +VALUE rb_yield_block(VALUE, VALUE, int, const VALUE *, VALUE); /* rb_block_call_func */ +int rb_block_given_p(void); +void rb_need_block(void); +VALUE rb_iterate(VALUE(*)(VALUE),VALUE,VALUE(*)(ANYARGS),VALUE); +VALUE rb_block_call(VALUE,ID,int,const VALUE*,rb_block_call_func_t,VALUE); +VALUE rb_rescue(VALUE(*)(ANYARGS),VALUE,VALUE(*)(ANYARGS),VALUE); +VALUE rb_rescue2(VALUE(*)(ANYARGS),VALUE,VALUE(*)(ANYARGS),VALUE,...); +VALUE rb_ensure(VALUE(*)(ANYARGS),VALUE,VALUE(*)(ANYARGS),VALUE); +VALUE rb_catch(const char*,VALUE(*)(ANYARGS),VALUE); +VALUE rb_catch_obj(VALUE,VALUE(*)(ANYARGS),VALUE); +NORETURN(void rb_throw(const char*,VALUE)); +NORETURN(void rb_throw_obj(VALUE,VALUE)); + +VALUE rb_require(const char*); + +RUBY_EXTERN VALUE rb_mKernel; +RUBY_EXTERN VALUE rb_mComparable; +RUBY_EXTERN VALUE rb_mEnumerable; +RUBY_EXTERN VALUE rb_mErrno; +RUBY_EXTERN VALUE rb_mFileTest; +RUBY_EXTERN VALUE rb_mGC; +RUBY_EXTERN VALUE rb_mMath; +RUBY_EXTERN VALUE rb_mProcess; +RUBY_EXTERN VALUE rb_mWaitReadable; +RUBY_EXTERN VALUE rb_mWaitWritable; + +RUBY_EXTERN VALUE rb_cBasicObject; +RUBY_EXTERN VALUE rb_cObject; +RUBY_EXTERN VALUE rb_cArray; +RUBY_EXTERN VALUE rb_cBignum; +RUBY_EXTERN VALUE rb_cBinding; +RUBY_EXTERN VALUE rb_cClass; +RUBY_EXTERN VALUE rb_cCont; +RUBY_EXTERN VALUE rb_cDir; +RUBY_EXTERN VALUE rb_cData; +RUBY_EXTERN VALUE rb_cFalseClass; +RUBY_EXTERN VALUE rb_cEncoding; +RUBY_EXTERN VALUE rb_cEnumerator; +RUBY_EXTERN VALUE rb_cFile; +RUBY_EXTERN VALUE rb_cFixnum; +RUBY_EXTERN VALUE rb_cFloat; +RUBY_EXTERN VALUE rb_cHash; +RUBY_EXTERN VALUE rb_cInteger; +RUBY_EXTERN VALUE rb_cIO; +RUBY_EXTERN VALUE rb_cMatch; +RUBY_EXTERN VALUE rb_cMethod; +RUBY_EXTERN VALUE rb_cModule; +RUBY_EXTERN VALUE rb_cNameErrorMesg; +RUBY_EXTERN VALUE rb_cNilClass; +RUBY_EXTERN VALUE rb_cNumeric; +RUBY_EXTERN VALUE rb_cProc; +RUBY_EXTERN VALUE rb_cRandom; +RUBY_EXTERN VALUE rb_cRange; +RUBY_EXTERN VALUE rb_cRational; +RUBY_EXTERN VALUE rb_cComplex; +RUBY_EXTERN VALUE rb_cRegexp; +RUBY_EXTERN VALUE rb_cStat; +RUBY_EXTERN VALUE rb_cString; +RUBY_EXTERN VALUE rb_cStruct; +RUBY_EXTERN VALUE rb_cSymbol; +RUBY_EXTERN VALUE rb_cThread; +RUBY_EXTERN VALUE rb_cTime; +RUBY_EXTERN VALUE rb_cTrueClass; +RUBY_EXTERN VALUE rb_cUnboundMethod; + +RUBY_EXTERN VALUE rb_eException; +RUBY_EXTERN VALUE rb_eStandardError; +RUBY_EXTERN VALUE rb_eSystemExit; +RUBY_EXTERN VALUE rb_eInterrupt; +RUBY_EXTERN VALUE rb_eSignal; +RUBY_EXTERN VALUE rb_eFatal; +RUBY_EXTERN VALUE rb_eArgError; +RUBY_EXTERN VALUE rb_eEOFError; +RUBY_EXTERN VALUE rb_eIndexError; +RUBY_EXTERN VALUE rb_eStopIteration; +RUBY_EXTERN VALUE rb_eKeyError; +RUBY_EXTERN VALUE rb_eRangeError; +RUBY_EXTERN VALUE rb_eIOError; +RUBY_EXTERN VALUE rb_eRuntimeError; +RUBY_EXTERN VALUE rb_eSecurityError; +RUBY_EXTERN VALUE rb_eSystemCallError; +RUBY_EXTERN VALUE rb_eThreadError; +RUBY_EXTERN VALUE rb_eTypeError; +RUBY_EXTERN VALUE rb_eZeroDivError; +RUBY_EXTERN VALUE rb_eNotImpError; +RUBY_EXTERN VALUE rb_eNoMemError; +RUBY_EXTERN VALUE rb_eNoMethodError; +RUBY_EXTERN VALUE rb_eFloatDomainError; +RUBY_EXTERN VALUE rb_eLocalJumpError; +RUBY_EXTERN VALUE rb_eSysStackError; +RUBY_EXTERN VALUE rb_eRegexpError; +RUBY_EXTERN VALUE rb_eEncodingError; +RUBY_EXTERN VALUE rb_eEncCompatError; + +RUBY_EXTERN VALUE rb_eScriptError; +RUBY_EXTERN VALUE rb_eNameError; +RUBY_EXTERN VALUE rb_eSyntaxError; +RUBY_EXTERN VALUE rb_eLoadError; + +RUBY_EXTERN VALUE rb_eMathDomainError; + +RUBY_EXTERN VALUE rb_stdin, rb_stdout, rb_stderr; + +static inline VALUE +rb_class_of(VALUE obj) +{ + if (IMMEDIATE_P(obj)) { + if (FIXNUM_P(obj)) return rb_cFixnum; + if (FLONUM_P(obj)) return rb_cFloat; + if (obj == Qtrue) return rb_cTrueClass; + if (SYMBOL_P(obj)) return rb_cSymbol; + } + else if (!RTEST(obj)) { + if (obj == Qnil) return rb_cNilClass; + if (obj == Qfalse) return rb_cFalseClass; + } + return RBASIC(obj)->klass; +} + +static inline int +rb_type(VALUE obj) +{ + if (IMMEDIATE_P(obj)) { + if (FIXNUM_P(obj)) return T_FIXNUM; + if (FLONUM_P(obj)) return T_FLOAT; + if (obj == Qtrue) return T_TRUE; + if (SYMBOL_P(obj)) return T_SYMBOL; + if (obj == Qundef) return T_UNDEF; + } + else if (!RTEST(obj)) { + if (obj == Qnil) return T_NIL; + if (obj == Qfalse) return T_FALSE; + } + return BUILTIN_TYPE(obj); +} + +#define RB_FLOAT_TYPE_P(obj) (FLONUM_P(obj) || (!SPECIAL_CONST_P(obj) && BUILTIN_TYPE(obj) == T_FLOAT)) + +#define RB_TYPE_P(obj, type) ( \ + ((type) == T_FIXNUM) ? FIXNUM_P(obj) : \ + ((type) == T_TRUE) ? ((obj) == Qtrue) : \ + ((type) == T_FALSE) ? ((obj) == Qfalse) : \ + ((type) == T_NIL) ? ((obj) == Qnil) : \ + ((type) == T_UNDEF) ? ((obj) == Qundef) : \ + ((type) == T_SYMBOL) ? SYMBOL_P(obj) : \ + ((type) == T_FLOAT) ? RB_FLOAT_TYPE_P(obj) : \ + (!SPECIAL_CONST_P(obj) && BUILTIN_TYPE(obj) == (type))) + +#ifdef __GNUC__ +#define rb_type_p(obj, type) \ + __extension__ (__builtin_constant_p(type) ? RB_TYPE_P((obj), (type)) : \ + rb_type(obj) == (type)) +#else +#define rb_type_p(obj, type) (rb_type(obj) == (type)) +#endif + +#ifdef __GNUC__ +#define rb_special_const_p(obj) \ + __extension__ ({VALUE special_const_obj = (obj); (int)(SPECIAL_CONST_P(special_const_obj) ? Qtrue : Qfalse);}) +#else +static inline int +rb_special_const_p(VALUE obj) +{ + if (SPECIAL_CONST_P(obj)) return (int)Qtrue; + return (int)Qfalse; +} +#endif + +#include "ruby/intern.h" + +#if defined(EXTLIB) && defined(USE_DLN_A_OUT) +/* hook for external modules */ +static char *dln_libs_to_be_linked[] = { EXTLIB, 0 }; +#endif + +#define RUBY_VM 1 /* YARV */ +#define HAVE_NATIVETHREAD +int ruby_native_thread_p(void); + +/* traditional set_trace_func events */ +#define RUBY_EVENT_NONE 0x0000 +#define RUBY_EVENT_LINE 0x0001 +#define RUBY_EVENT_CLASS 0x0002 +#define RUBY_EVENT_END 0x0004 +#define RUBY_EVENT_CALL 0x0008 +#define RUBY_EVENT_RETURN 0x0010 +#define RUBY_EVENT_C_CALL 0x0020 +#define RUBY_EVENT_C_RETURN 0x0040 +#define RUBY_EVENT_RAISE 0x0080 +#define RUBY_EVENT_ALL 0x00ff + +/* for TracePoint extended events */ +#define RUBY_EVENT_B_CALL 0x0100 +#define RUBY_EVENT_B_RETURN 0x0200 +#define RUBY_EVENT_THREAD_BEGIN 0x0400 +#define RUBY_EVENT_THREAD_END 0x0800 +#define RUBY_EVENT_TRACEPOINT_ALL 0xffff + +/* special events */ +#define RUBY_EVENT_SPECIFIED_LINE 0x010000 +#define RUBY_EVENT_COVERAGE 0x020000 + +/* internal events */ +#define RUBY_INTERNAL_EVENT_SWITCH 0x040000 +#define RUBY_EVENT_SWITCH 0x040000 /* obsolete name. this macro is for compatibility */ + /* 0x080000 */ +#define RUBY_INTERNAL_EVENT_NEWOBJ 0x100000 +#define RUBY_INTERNAL_EVENT_FREEOBJ 0x200000 +#define RUBY_INTERNAL_EVENT_GC_START 0x400000 +#define RUBY_INTERNAL_EVENT_GC_END_MARK 0x800000 +#define RUBY_INTERNAL_EVENT_GC_END_SWEEP 0x1000000 +#define RUBY_INTERNAL_EVENT_OBJSPACE_MASK 0x1f00000 +#define RUBY_INTERNAL_EVENT_MASK 0xfffe0000 + +typedef unsigned long rb_event_flag_t; +typedef void (*rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass); + +#define RB_EVENT_HOOKS_HAVE_CALLBACK_DATA 1 +void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data); +int rb_remove_event_hook(rb_event_hook_func_t func); + +/* locale insensitive functions */ + +#define rb_isascii(c) ((unsigned long)(c) < 128) +int rb_isalnum(int c); +int rb_isalpha(int c); +int rb_isblank(int c); +int rb_iscntrl(int c); +int rb_isdigit(int c); +int rb_isgraph(int c); +int rb_islower(int c); +int rb_isprint(int c); +int rb_ispunct(int c); +int rb_isspace(int c); +int rb_isupper(int c); +int rb_isxdigit(int c); +int rb_tolower(int c); +int rb_toupper(int c); + +#ifndef ISPRINT +#define ISASCII(c) rb_isascii((unsigned char)(c)) +#undef ISPRINT +#define ISPRINT(c) rb_isprint((unsigned char)(c)) +#define ISGRAPH(c) rb_isgraph((unsigned char)(c)) +#define ISSPACE(c) rb_isspace((unsigned char)(c)) +#define ISUPPER(c) rb_isupper((unsigned char)(c)) +#define ISLOWER(c) rb_islower((unsigned char)(c)) +#define ISALNUM(c) rb_isalnum((unsigned char)(c)) +#define ISALPHA(c) rb_isalpha((unsigned char)(c)) +#define ISDIGIT(c) rb_isdigit((unsigned char)(c)) +#define ISXDIGIT(c) rb_isxdigit((unsigned char)(c)) +#endif +#define TOUPPER(c) rb_toupper((unsigned char)(c)) +#define TOLOWER(c) rb_tolower((unsigned char)(c)) + +int st_locale_insensitive_strcasecmp(const char *s1, const char *s2); +int st_locale_insensitive_strncasecmp(const char *s1, const char *s2, size_t n); +#define STRCASECMP(s1, s2) (st_locale_insensitive_strcasecmp((s1), (s2))) +#define STRNCASECMP(s1, s2, n) (st_locale_insensitive_strncasecmp((s1), (s2), (n))) + +unsigned long ruby_strtoul(const char *str, char **endptr, int base); +#define STRTOUL(str, endptr, base) (ruby_strtoul((str), (endptr), (base))) + +#define InitVM(ext) {void InitVM_##ext(void);InitVM_##ext();} + +PRINTF_ARGS(int ruby_snprintf(char *str, size_t n, char const *fmt, ...), 3, 4); +int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap); + +#ifndef RUBY_DONT_SUBST +#include "ruby/subst.h" +#endif + +/** + * @defgroup embed CRuby Embedding APIs + * CRuby interpreter APIs. These are APIs to embed MRI interpreter into your + * program. + * These functions are not a part of Ruby extension library API. + * Extension libraries of Ruby should not depend on these functions. + * @{ + */ + +/** @defgroup ruby1 ruby(1) implementation + * A part of the implementation of ruby(1) command. + * Other programs that embed Ruby interpreter do not always need to use these + * functions. + * @{ + */ + +void ruby_sysinit(int *argc, char ***argv); +void ruby_init(void); +void* ruby_options(int argc, char** argv); +int ruby_executable_node(void *n, int *status); +int ruby_run_node(void *n); + +/* version.c */ +void ruby_show_version(void); +void ruby_show_copyright(void); + + +/*! A convenience macro to call ruby_init_stack(). Must be placed just after + * variable declarations */ +#define RUBY_INIT_STACK \ + VALUE variable_in_this_stack_frame; \ + ruby_init_stack(&variable_in_this_stack_frame); +/*! @} */ + +#ifdef __ia64 +void ruby_init_stack(volatile VALUE*, void*); +#define ruby_init_stack(addr) ruby_init_stack((addr), rb_ia64_bsp()) +#else +void ruby_init_stack(volatile VALUE*); +#endif +#define Init_stack(addr) ruby_init_stack(addr) + +int ruby_setup(void); +int ruby_cleanup(volatile int); + +void ruby_finalize(void); +NORETURN(void ruby_stop(int)); + +void ruby_set_stack_size(size_t); +int ruby_stack_check(void); +size_t ruby_stack_length(VALUE**); + +int ruby_exec_node(void *n); + +void ruby_script(const char* name); +void ruby_set_script_name(VALUE name); + +void ruby_prog_init(void); +void ruby_set_argv(int, char**); +void *ruby_process_options(int, char**); +void ruby_init_loadpath(void); +void ruby_incpush(const char*); +void ruby_sig_finalize(void); + +/*! @} */ + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif +#endif /* RUBY_RUBY_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/st.h b/ruby/include/ruby-2.1.0/ruby/st.h new file mode 100644 index 0000000..975da65 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/st.h @@ -0,0 +1,154 @@ +/* This is a public domain general purpose hash table package written by Peter Moore @ UCB. */ + +/* @(#) st.h 5.1 89/12/14 */ + +#ifndef RUBY_ST_H +#define RUBY_ST_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#include "ruby/defines.h" + +RUBY_SYMBOL_EXPORT_BEGIN + +#if SIZEOF_LONG == SIZEOF_VOIDP +typedef unsigned long st_data_t; +#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP +typedef unsigned LONG_LONG st_data_t; +#else +# error ---->> st.c requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<---- +#endif +#define ST_DATA_T_DEFINED + +#ifndef CHAR_BIT +# ifdef HAVE_LIMITS_H +# include +# else +# define CHAR_BIT 8 +# endif +#endif +#ifndef _ +# define _(args) args +#endif +#ifndef ANYARGS +# ifdef __cplusplus +# define ANYARGS ... +# else +# define ANYARGS +# endif +#endif + +typedef struct st_table st_table; + +typedef st_data_t st_index_t; +typedef int st_compare_func(st_data_t, st_data_t); +typedef st_index_t st_hash_func(st_data_t); + +typedef char st_check_for_sizeof_st_index_t[SIZEOF_VOIDP == (int)sizeof(st_index_t) ? 1 : -1]; +#define SIZEOF_ST_INDEX_T SIZEOF_VOIDP + +struct st_hash_type { + int (*compare)(ANYARGS /*st_data_t, st_data_t*/); /* st_compare_func* */ + st_index_t (*hash)(ANYARGS /*st_data_t*/); /* st_hash_func* */ +}; + +#define ST_INDEX_BITS (sizeof(st_index_t) * CHAR_BIT) + +#if defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR) && defined(HAVE_BUILTIN___BUILTIN_TYPES_COMPATIBLE_P) +# define ST_DATA_COMPATIBLE_P(type) \ + __builtin_choose_expr(__builtin_types_compatible_p(type, st_data_t), 1, 0) +#else +# define ST_DATA_COMPATIBLE_P(type) 0 +#endif + +struct st_table { + const struct st_hash_type *type; + st_index_t num_bins; + unsigned int entries_packed : 1; +#ifdef __GNUC__ + /* + * C spec says, + * A bit-field shall have a type that is a qualified or unqualified + * version of _Bool, signed int, unsigned int, or some other + * implementation-defined type. It is implementation-defined whether + * atomic types are permitted. + * In short, long and long long bit-field are implementation-defined + * feature. Therefore we want to supress a warning explicitly. + */ + __extension__ +#endif + st_index_t num_entries : ST_INDEX_BITS - 1; + union { + struct { + struct st_table_entry **bins; + struct st_table_entry *head, *tail; + } big; + struct { + struct st_packed_entry *entries; + st_index_t real_entries; + } packed; + } as; +}; + +#define st_is_member(table,key) st_lookup((table),(key),(st_data_t *)0) + +enum st_retval {ST_CONTINUE, ST_STOP, ST_DELETE, ST_CHECK}; + +st_table *st_init_table(const struct st_hash_type *); +st_table *st_init_table_with_size(const struct st_hash_type *, st_index_t); +st_table *st_init_numtable(void); +st_table *st_init_numtable_with_size(st_index_t); +st_table *st_init_strtable(void); +st_table *st_init_strtable_with_size(st_index_t); +st_table *st_init_strcasetable(void); +st_table *st_init_strcasetable_with_size(st_index_t); +int st_delete(st_table *, st_data_t *, st_data_t *); /* returns 0:notfound 1:deleted */ +int st_delete_safe(st_table *, st_data_t *, st_data_t *, st_data_t); +int st_shift(st_table *, st_data_t *, st_data_t *); /* returns 0:notfound 1:deleted */ +int st_insert(st_table *, st_data_t, st_data_t); +int st_insert2(st_table *, st_data_t, st_data_t, st_data_t (*)(st_data_t)); +int st_lookup(st_table *, st_data_t, st_data_t *); +int st_get_key(st_table *, st_data_t, st_data_t *); +typedef int st_update_callback_func(st_data_t *key, st_data_t *value, st_data_t arg, int existing); +int st_update(st_table *table, st_data_t key, st_update_callback_func *func, st_data_t arg); +int st_foreach(st_table *, int (*)(ANYARGS), st_data_t); +int st_foreach_check(st_table *, int (*)(ANYARGS), st_data_t, st_data_t); +int st_reverse_foreach(st_table *, int (*)(ANYARGS), st_data_t); +st_index_t st_keys(st_table *table, st_data_t *keys, st_index_t size); +st_index_t st_keys_check(st_table *table, st_data_t *keys, st_index_t size, st_data_t never); +st_index_t st_values(st_table *table, st_data_t *values, st_index_t size); +st_index_t st_values_check(st_table *table, st_data_t *values, st_index_t size, st_data_t never); +void st_add_direct(st_table *, st_data_t, st_data_t); +void st_free_table(st_table *); +void st_cleanup_safe(st_table *, st_data_t); +void st_clear(st_table *); +st_table *st_copy(st_table *); +int st_numcmp(st_data_t, st_data_t); +st_index_t st_numhash(st_data_t); +int st_locale_insensitive_strcasecmp(const char *s1, const char *s2); +int st_locale_insensitive_strncasecmp(const char *s1, const char *s2, size_t n); +#define st_strcasecmp st_locale_insensitive_strcasecmp +#define st_strncasecmp st_locale_insensitive_strncasecmp +size_t st_memsize(const st_table *); +st_index_t st_hash(const void *ptr, size_t len, st_index_t h); +st_index_t st_hash_uint32(st_index_t h, uint32_t i); +st_index_t st_hash_uint(st_index_t h, st_index_t i); +st_index_t st_hash_end(st_index_t h); +st_index_t st_hash_start(st_index_t h); +#define st_hash_start(h) ((st_index_t)(h)) + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_ST_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/subst.h b/ruby/include/ruby-2.1.0/ruby/subst.h new file mode 100644 index 0000000..1f0e6db --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/subst.h @@ -0,0 +1,19 @@ +#ifndef RUBY_SUBST_H +#define RUBY_SUBST_H 1 + +#undef snprintf +#undef vsnprintf +#define snprintf ruby_snprintf +#define vsnprintf ruby_vsnprintf + +#ifdef BROKEN_CLOSE +#undef getpeername +#define getpeername ruby_getpeername +#undef getsockname +#define getsockname ruby_getsockname +#undef shutdown +#define shutdown ruby_shutdown +#undef close +#define close ruby_close +#endif +#endif diff --git a/ruby/include/ruby-2.1.0/ruby/thread.h b/ruby/include/ruby-2.1.0/ruby/thread.h new file mode 100644 index 0000000..550f678 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/thread.h @@ -0,0 +1,45 @@ +/********************************************************************** + + thread.h - + + $Author: matz $ + created at: Tue Jul 10 17:35:43 JST 2012 + + Copyright (C) 2007 Yukihiro Matsumoto + +**********************************************************************/ + +#ifndef RUBY_THREAD_H +#define RUBY_THREAD_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#include "ruby/intern.h" + +RUBY_SYMBOL_EXPORT_BEGIN + +void *rb_thread_call_with_gvl(void *(*func)(void *), void *data1); + +void *rb_thread_call_without_gvl(void *(*func)(void *), void *data1, + rb_unblock_function_t *ubf, void *data2); +void *rb_thread_call_without_gvl2(void *(*func)(void *), void *data1, + rb_unblock_function_t *ubf, void *data2); + +#define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_AFTER 0x01 +#define RUBY_CALL_WO_GVL_FLAG_SKIP_CHECK_INTS_ + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_THREAD_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/util.h b/ruby/include/ruby-2.1.0/ruby/util.h new file mode 100644 index 0000000..6521493 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/util.h @@ -0,0 +1,95 @@ +/********************************************************************** + + util.h - + + $Author: nobu $ + created at: Thu Mar 9 11:55:53 JST 1995 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + +**********************************************************************/ + +#ifndef RUBY_UTIL_H +#define RUBY_UTIL_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +#include "ruby/defines.h" +#ifdef RUBY_EXTCONF_H +#include RUBY_EXTCONF_H +#endif + +#ifndef _ +#ifdef __cplusplus +# ifndef HAVE_PROTOTYPES +# define HAVE_PROTOTYPES 1 +# endif +# ifndef HAVE_STDARG_PROTOTYPES +# define HAVE_STDARG_PROTOTYPES 1 +# endif +#endif +#ifdef HAVE_PROTOTYPES +# define _(args) args +#else +# define _(args) () +#endif +#ifdef HAVE_STDARG_PROTOTYPES +# define __(args) args +#else +# define __(args) () +#endif +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +#define scan_oct(s,l,e) ((int)ruby_scan_oct((s),(l),(e))) +unsigned long ruby_scan_oct(const char *, size_t, size_t *); +#define scan_hex(s,l,e) ((int)ruby_scan_hex((s),(l),(e))) +unsigned long ruby_scan_hex(const char *, size_t, size_t *); + +void ruby_qsort(void *, const size_t, const size_t, + int (*)(const void *, const void *, void *), void *); + +void ruby_setenv(const char *, const char *); +void ruby_unsetenv(const char *); +#undef setenv +#undef unsetenv +#define setenv(name,val) ruby_setenv((name),(val)) +#define unsetenv(name,val) ruby_unsetenv(name) + +char *ruby_strdup(const char *); +#undef strdup +#define strdup(s) ruby_strdup(s) + +char *ruby_getcwd(void); +#define my_getcwd() ruby_getcwd() + +double ruby_strtod(const char *, char **); +#undef strtod +#define strtod(s,e) ruby_strtod((s),(e)) + +#if defined _MSC_VER && _MSC_VER >= 1300 +#pragma warning(push) +#pragma warning(disable:4723) +#endif +#if defined _MSC_VER && _MSC_VER >= 1300 +#pragma warning(pop) +#endif + +void ruby_each_words(const char *, void (*)(const char*, int, void*), void *); + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_UTIL_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/version.h b/ruby/include/ruby-2.1.0/ruby/version.h new file mode 100644 index 0000000..18c0e5a --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/version.h @@ -0,0 +1,74 @@ +/********************************************************************** + + ruby/version.h - + + $Author: nobu $ + created at: Wed May 13 12:56:56 JST 2009 + + Copyright (C) 1993-2009 Yukihiro Matsumoto + Copyright (C) 2000 Network Applied Communication Laboratory, Inc. + Copyright (C) 2000 Information-technology Promotion Agency, Japan + +**********************************************************************/ + +/* + * This file contains only + * - never-changable informations, and + * - interfaces accessible from extension libraries. + * + * Never try to check RUBY_VERSION_CODE etc in extension libraries, + * check the features with mkmf.rb instead. + */ + +#ifndef RUBY_VERSION_H +#define RUBY_VERSION_H 1 + +/* The origin. */ +#define RUBY_AUTHOR "Yukihiro Matsumoto" +#define RUBY_BIRTH_YEAR 1993 +#define RUBY_BIRTH_MONTH 2 +#define RUBY_BIRTH_DAY 24 + +/* API version */ +#define RUBY_API_VERSION_MAJOR 2 +#define RUBY_API_VERSION_MINOR 1 +#define RUBY_API_VERSION_TEENY 0 +#define RUBY_API_VERSION_CODE (RUBY_API_VERSION_MAJOR*10000+RUBY_API_VERSION_MINOR*100+RUBY_API_VERSION_TEENY) + +#ifdef RUBY_EXTERN +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +/* + * Interfaces from extension libraries. + * + * Before using these infos, think thrice whether they are really + * necessary or not, and if the answer was yes, think twice a week + * later again. + */ +RUBY_EXTERN const int ruby_api_version[3]; +RUBY_EXTERN const char ruby_version[]; +RUBY_EXTERN const char ruby_release_date[]; +RUBY_EXTERN const char ruby_platform[]; +RUBY_EXTERN const int ruby_patchlevel; +RUBY_EXTERN const char ruby_description[]; +RUBY_EXTERN const char ruby_copyright[]; +RUBY_EXTERN const char ruby_engine[]; + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif +#endif + +#endif diff --git a/ruby/include/ruby-2.1.0/ruby/vm.h b/ruby/include/ruby-2.1.0/ruby/vm.h new file mode 100644 index 0000000..6b7b81c --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/vm.h @@ -0,0 +1,64 @@ +/********************************************************************** + + ruby/vm.h - + + $Author: nobu $ + created at: Sat May 31 15:17:36 2008 + + Copyright (C) 2008 Yukihiro Matsumoto + +**********************************************************************/ + +#ifndef RUBY_VM_H +#define RUBY_VM_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +/* Place holder. + * + * We will prepare VM creation/control APIs on 1.9.2 or later. + * If you have an interest about it, please see mvm branch. + * http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/branches/mvm/ + */ + +/* VM type declaration */ +typedef struct rb_vm_struct ruby_vm_t; + +/* core API */ +int ruby_vm_destruct(ruby_vm_t *vm); + +/** + * ruby_vm_at_exit registers a function _func_ to be invoked when a VM + * passed away. Functions registered this way runs in reverse order + * of registration, just like END {} block does. The difference is + * its timing to be triggered. ruby_vm_at_exit functions runs when a + * VM _passed_ _away_, while END {} blocks runs just _before_ a VM + * _is_ _passing_ _away_. + * + * You cannot register a function to another VM than where you are in. + * So where to register is intuitive, omitted. OTOH the argument + * _func_ cannot know which VM it is in because at the time of + * invocation, the VM has already died and there is no execution + * context. The VM itself is passed as the first argument to it. + * + * @param[in] func the function to register. + */ +void ruby_vm_at_exit(void(*func)(ruby_vm_t *)); + +RUBY_SYMBOL_EXPORT_END + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#endif /* RUBY_VM_H */ diff --git a/ruby/include/ruby-2.1.0/ruby/win32.h b/ruby/include/ruby-2.1.0/ruby/win32.h new file mode 100644 index 0000000..64fbdf2 --- /dev/null +++ b/ruby/include/ruby-2.1.0/ruby/win32.h @@ -0,0 +1,837 @@ +#ifndef RUBY_WIN32_H +#define RUBY_WIN32_H 1 + +#if defined(__cplusplus) +extern "C" { +#if 0 +} /* satisfy cc-mode */ +#endif +#endif + +RUBY_SYMBOL_EXPORT_BEGIN + +/* + * Copyright (c) 1993, Intergraph Corporation + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the perl README file. + * + */ + +/* + * Definitions for NT port of Perl + */ + + +/* + * Ok now we can include the normal include files. + */ + +/* #include conflict with varargs.h? */ +#if !defined(WSAAPI) +#if defined(__cplusplus) && defined(_MSC_VER) +extern "C++" { /* template without extern "C++" */ +#endif +#if !defined(_WIN64) && !defined(WIN32) +#define WIN32 +#endif +#include +#include +#if !defined(_MSC_VER) || _MSC_VER >= 1400 +#include +#endif +#if defined(__cplusplus) && defined(_MSC_VER) +} +#endif +#endif + +/* + * We're not using Microsoft's "extensions" to C for + * Structured Exception Handling (SEH) so we can nuke these + */ +#undef try +#undef except +#undef finally +#undef leave + +#include +#include +#include +#include +#include +#include +#if defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER == 1200 +extern "C++" { /* template without extern "C++" */ +#endif +#include +#if defined(__cplusplus) && defined(_MSC_VER) && _MSC_VER == 1200 +} +#endif +#include +#include +#include +#ifdef HAVE_SYS_UTIME_H +# include +#else +# include +#endif +#include +#include +#if defined __MINGW32__ || __BORLANDC__ >= 0x0580 +# include +#else +# if !defined(_INTPTR_T_DEFINED) +# ifdef _WIN64 +typedef __int64 intptr_t; +# else +typedef int intptr_t; +# endif +# define _INTPTR_T_DEFINED +# endif +# if !defined(INTPTR_MAX) +# ifdef _WIN64 +# define INTPTR_MAX 9223372036854775807I64 +# else +# define INTPTR_MAX 2147483647 +# endif +# define INTPTR_MIN (-INTPTR_MAX-1) +# endif +# if !defined(_UINTPTR_T_DEFINED) +# ifdef _WIN64 +typedef unsigned __int64 uintptr_t; +# else +typedef unsigned int uintptr_t; +# endif +# define _UINTPTR_T_DEFINED +# endif +# if !defined(UINTPTR_MAX) +# ifdef _WIN64 +# define UINTPTR_MAX 18446744073709551615UI64 +# else +# define UINTPTR_MAX 4294967295U +# endif +# endif +#endif +#ifndef __MINGW32__ +# define mode_t int +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#define rb_w32_iswinnt() TRUE +#define rb_w32_iswin95() FALSE + +#define WNOHANG -1 + +typedef int clockid_t; +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 + +#undef getc +#undef putc +#undef fgetc +#undef fputc +#undef getchar +#undef putchar +#undef fgetchar +#undef fputchar +#undef utime +#undef lseek +#undef stat +#undef fstat +#define getc(_stream) rb_w32_getc(_stream) +#define getchar() rb_w32_getc(stdin) +#define putc(_c, _stream) rb_w32_putc(_c, _stream) +#define putchar(_c) rb_w32_putc(_c, stdout) +#ifdef RUBY_EXPORT +#define fgetc(_stream) getc(_stream) +#define fputc(_c, _stream) putc(_c, _stream) +#define fgetchar() getchar() +#define fputchar(_c) putchar(_c) +#define utime(_p, _t) rb_w32_utime(_p, _t) +#define lseek(_f, _o, _w) _lseeki64(_f, _o, _w) + +#define pipe(p) rb_w32_pipe(p) +#define open rb_w32_open +#define close(h) rb_w32_close(h) +#define fclose(f) rb_w32_fclose(f) +#define read(f, b, s) rb_w32_read(f, b, s) +#define write(f, b, s) rb_w32_write(f, b, s) +#define getpid() rb_w32_getpid() +#define getppid() rb_w32_getppid() +#define sleep(x) rb_w32_Sleep((x)*1000) +#define Sleep(msec) (void)rb_w32_Sleep(msec) +#define fstati64(fd,st) rb_w32_fstati64(fd,st) +#ifdef __BORLANDC__ +#define creat(p, m) _creat(p, m) +#define eof() _eof() +#define filelength(h) _filelength(h) +#define mktemp(t) _mktemp(t) +#define tell(h) _tell(h) +#define _open _sopen +#define sopen _sopen +#undef fopen +#define fopen(p, m) rb_w32_fopen(p, m) +#undef fdopen +#define fdopen(h, m) rb_w32_fdopen(h, m) +#undef fsopen +#define fsopen(p, m, sh) rb_w32_fsopen(p, m, sh) +#endif /* __BORLANDC__ */ + +#undef execv +#define execv(path,argv) rb_w32_aspawn(P_OVERLAY,path,argv) +#if !defined(__BORLANDC__) +#undef isatty +#define isatty(h) rb_w32_isatty(h) +#endif /* __BORLANDC__ */ + +#undef mkdir +#define mkdir(p, m) rb_w32_mkdir(p, m) +#undef rmdir +#define rmdir(p) rb_w32_rmdir(p) +#undef unlink +#define unlink(p) rb_w32_unlink(p) +#endif /* RUBY_EXPORT */ + +#if SIZEOF_OFF_T == 8 +#define off_t __int64 +#define stat stati64 +#define fstat(fd,st) fstati64(fd,st) +#if defined(__BORLANDC__) +#define stati64(path, st) rb_w32_stati64(path, st) +#elif !defined(_MSC_VER) || RT_VER < 80 +#define stati64 _stati64 +#ifndef _stati64 +#define _stati64(path, st) rb_w32_stati64(path, st) +#endif +#else +#define stati64 _stat64 +#define _stat64(path, st) rb_w32_stati64(path, st) +#endif +#else +#define stat(path,st) rb_w32_stat(path,st) +#define fstat(fd,st) rb_w32_fstat(fd,st) +extern int rb_w32_stat(const char *, struct stat *); +extern int rb_w32_fstat(int, struct stat *); +#endif +#define access(path,mode) rb_w32_access(path,mode) + +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define fsync _commit + +struct timezone; + +#ifdef __MINGW32__ +#undef isascii +#define isascii __isascii +#endif + +struct iovec { + void *iov_base; + size_t iov_len; +}; +struct msghdr { + void *msg_name; + int msg_namelen; + struct iovec *msg_iov; + int msg_iovlen; + void *msg_control; + int msg_controllen; + int msg_flags; +}; + +/* for getifaddrs() and others */ +struct ifaddrs { + struct ifaddrs *ifa_next; + char *ifa_name; + u_int ifa_flags; + struct sockaddr *ifa_addr; + struct sockaddr *ifa_netmask; + struct sockaddr *ifa_broadaddr; + struct sockaddr *ifa_dstaddr; + void *ifa_data; +}; +#ifdef IF_NAMESIZE +#define IFNAMSIZ IF_NAMESIZE +#else +#define IFNAMSIZ 256 +#endif +#ifdef IFF_POINTTOPOINT +#define IFF_POINTOPOINT IFF_POINTTOPOINT +#endif + +extern DWORD rb_w32_osid(void); +extern int rb_w32_cmdvector(const char *, char ***); +extern rb_pid_t rb_w32_pipe_exec(const char *, const char *, int, int *, int *); +extern int flock(int fd, int oper); +extern int rb_w32_io_cancelable_p(int); +extern int rb_w32_is_socket(int); +extern int WSAAPI rb_w32_accept(int, struct sockaddr *, int *); +extern int WSAAPI rb_w32_bind(int, const struct sockaddr *, int); +extern int WSAAPI rb_w32_connect(int, const struct sockaddr *, int); +extern void rb_w32_fdset(int, fd_set*); +extern void rb_w32_fdclr(int, fd_set*); +extern int rb_w32_fdisset(int, fd_set*); +extern int WSAAPI rb_w32_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +extern int WSAAPI rb_w32_getpeername(int, struct sockaddr *, int *); +extern int WSAAPI rb_w32_getsockname(int, struct sockaddr *, int *); +extern int WSAAPI rb_w32_getsockopt(int, int, int, char *, int *); +extern int WSAAPI rb_w32_ioctlsocket(int, long, u_long *); +extern int WSAAPI rb_w32_listen(int, int); +extern int WSAAPI rb_w32_recv(int, char *, int, int); +extern int WSAAPI rb_w32_recvfrom(int, char *, int, int, struct sockaddr *, int *); +extern int WSAAPI rb_w32_send(int, const char *, int, int); +extern int WSAAPI rb_w32_sendto(int, const char *, int, int, const struct sockaddr *, int); +extern int recvmsg(int, struct msghdr *, int); +extern int sendmsg(int, const struct msghdr *, int); +extern int WSAAPI rb_w32_setsockopt(int, int, int, const char *, int); +extern int WSAAPI rb_w32_shutdown(int, int); +extern int WSAAPI rb_w32_socket(int, int, int); +extern SOCKET rb_w32_get_osfhandle(int); +extern struct hostent *WSAAPI rb_w32_gethostbyaddr(const char *, int, int); +extern struct hostent *WSAAPI rb_w32_gethostbyname(const char *); +extern int WSAAPI rb_w32_gethostname(char *, int); +extern struct protoent *WSAAPI rb_w32_getprotobyname(const char *); +extern struct protoent *WSAAPI rb_w32_getprotobynumber(int); +extern struct servent *WSAAPI rb_w32_getservbyname(const char *, const char *); +extern struct servent *WSAAPI rb_w32_getservbyport(int, const char *); +extern int socketpair(int, int, int, int *); +extern int getifaddrs(struct ifaddrs **); +extern void freeifaddrs(struct ifaddrs *); +extern char * rb_w32_getcwd(char *, int); +extern char * rb_w32_ugetenv(const char *); +extern char * rb_w32_getenv(const char *); +extern int rb_w32_rename(const char *, const char *); +extern int rb_w32_urename(const char *, const char *); +extern char **rb_w32_get_environ(void); +extern void rb_w32_free_environ(char **); +extern int rb_w32_map_errno(DWORD); +extern const char *WSAAPI rb_w32_inet_ntop(int,const void *,char *,size_t); +extern int WSAAPI rb_w32_inet_pton(int,const char *,void *); +extern DWORD rb_w32_osver(void); + +extern int chown(const char *, int, int); +extern int rb_w32_uchown(const char *, int, int); +extern int link(const char *, const char *); +extern int rb_w32_ulink(const char *, const char *); +extern int gettimeofday(struct timeval *, struct timezone *); +extern int clock_gettime(clockid_t, struct timespec *); +extern int clock_getres(clockid_t, struct timespec *); +extern rb_pid_t waitpid (rb_pid_t, int *, int); +extern rb_pid_t rb_w32_spawn(int, const char *, const char*); +extern rb_pid_t rb_w32_aspawn(int, const char *, char *const *); +extern rb_pid_t rb_w32_aspawn_flags(int, const char *, char *const *, DWORD); +extern rb_pid_t rb_w32_uspawn(int, const char *, const char*); +extern rb_pid_t rb_w32_uaspawn(int, const char *, char *const *); +extern rb_pid_t rb_w32_uaspawn_flags(int, const char *, char *const *, DWORD); +extern int kill(int, int); +extern int fcntl(int, int, ...); +extern rb_pid_t rb_w32_getpid(void); +extern rb_pid_t rb_w32_getppid(void); +#if !defined(__BORLANDC__) +extern int rb_w32_isatty(int); +#endif +extern int rb_w32_uchdir(const char *); +extern int rb_w32_mkdir(const char *, int); +extern int rb_w32_umkdir(const char *, int); +extern int rb_w32_rmdir(const char *); +extern int rb_w32_urmdir(const char *); +extern int rb_w32_unlink(const char *); +extern int rb_w32_uunlink(const char *); +extern int rb_w32_uchmod(const char *, int); +extern int rb_w32_stati64(const char *, struct stati64 *); +extern int rb_w32_ustati64(const char *, struct stati64 *); +extern int rb_w32_access(const char *, int); +extern int rb_w32_uaccess(const char *, int); +extern char rb_w32_fd_is_text(int); +extern int rb_w32_fstati64(int, struct stati64 *); +extern int rb_w32_dup2(int, int); + +#ifdef __BORLANDC__ +extern off_t _lseeki64(int, off_t, int); +extern FILE *rb_w32_fopen(const char *, const char *); +extern FILE *rb_w32_fdopen(int, const char *); +extern FILE *rb_w32_fsopen(const char *, const char *, int); +#endif + +#include + +#if defined _MSC_VER && _MSC_VER >= 1800 && defined INFINITY +#pragma warning(push) +#pragma warning(disable:4756) +static inline float +rb_infinity_float(void) +{ + return INFINITY; +} +#pragma warning(pop) +#undef INFINITY +#define INFINITY rb_infinity_float() +#endif + +#if !defined __MINGW32__ || defined __NO_ISOCEXT +#ifndef isnan +#define isnan(x) _isnan(x) +#endif +static inline int +finite(double x) +{ + return _finite(x); +} +#ifndef copysign +#define copysign(a, b) _copysign(a, b) +#endif +static inline double +scalb(double a, long b) +{ + return _scalb(a, b); +} +#else +__declspec(dllimport) extern int finite(double); +#endif + +#if !defined S_IFIFO && defined _S_IFIFO +#define S_IFIFO _S_IFIFO +#endif + +#if 0 && defined __BORLANDC__ +#undef S_ISDIR +#undef S_ISFIFO +#undef S_ISBLK +#undef S_ISCHR +#undef S_ISREG +#define S_ISDIR(m) (((unsigned short)(m) & S_IFMT) == S_IFDIR) +#define S_ISFIFO(m) (((unsigned short)(m) & S_IFMT) == S_IFIFO) +#define S_ISBLK(m) (((unsigned short)(m) & S_IFMT) == S_IFBLK) +#define S_ISCHR(m) (((unsigned short)(m) & S_IFMT) == S_IFCHR) +#define S_ISREG(m) (((unsigned short)(m) & S_IFMT) == S_IFREG) +#endif + +#if !defined S_IRUSR && !defined __MINGW32__ +#define S_IRUSR 0400 +#endif +#ifndef S_IRGRP +#define S_IRGRP 0040 +#endif +#ifndef S_IROTH +#define S_IROTH 0004 +#endif + +#if !defined S_IWUSR && !defined __MINGW32__ +#define S_IWUSR 0200 +#endif +#ifndef S_IWGRP +#define S_IWGRP 0020 +#endif +#ifndef S_IWOTH +#define S_IWOTH 0002 +#endif + +#if !defined S_IXUSR && !defined __MINGW32__ +#define S_IXUSR 0100 +#endif +#ifndef S_IXGRP +#define S_IXGRP 0010 +#endif +#ifndef S_IXOTH +#define S_IXOTH 0001 +#endif + +/* + * define this so we can do inplace editing + */ + +#define SUFFIX + +extern int rb_w32_ftruncate(int fd, off_t length); +extern int rb_w32_truncate(const char *path, off_t length); + +#undef HAVE_FTRUNCATE +#define HAVE_FTRUNCATE 1 +#if defined HAVE_FTRUNCATE64 +#define ftruncate ftruncate64 +#else +#define ftruncate rb_w32_ftruncate +#endif + +#undef HAVE_TRUNCATE +#define HAVE_TRUNCATE 1 +#if defined HAVE_TRUNCATE64 +#define truncate truncate64 +#else +#define truncate rb_w32_truncate +#endif + +/* + * stubs + */ +extern int ioctl (int, int, ...); +extern rb_uid_t getuid (void); +extern rb_uid_t geteuid (void); +extern rb_gid_t getgid (void); +extern rb_gid_t getegid (void); +extern int setuid (rb_uid_t); +extern int setgid (rb_gid_t); + +extern int fstati64(int, struct stati64 *); + +extern char *rb_w32_strerror(int); + +#ifdef RUBY_EXPORT +#define strerror(e) rb_w32_strerror(e) +#endif + +#define PIPE_BUF 1024 + +#define LOCK_SH 1 +#define LOCK_EX 2 +#define LOCK_NB 4 +#define LOCK_UN 8 + + +#ifndef SIGINT +#define SIGINT 2 +#endif +#ifndef SIGKILL +#define SIGKILL 9 +#endif + + +/* #undef va_start */ +/* #undef va_end */ + +/* winsock error map */ +#include + +#ifndef EWOULDBLOCK +# define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef EINPROGRESS +# define EINPROGRESS WSAEINPROGRESS +#endif +#ifndef EALREADY +# define EALREADY WSAEALREADY +#endif +#ifndef ENOTSOCK +# define ENOTSOCK WSAENOTSOCK +#endif +#ifndef EDESTADDRREQ +# define EDESTADDRREQ WSAEDESTADDRREQ +#endif +#ifndef EMSGSIZE +# define EMSGSIZE WSAEMSGSIZE +#endif +#ifndef EPROTOTYPE +# define EPROTOTYPE WSAEPROTOTYPE +#endif +#ifndef ENOPROTOOPT +# define ENOPROTOOPT WSAENOPROTOOPT +#endif +#ifndef EPROTONOSUPPORT +# define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#endif +#ifndef ESOCKTNOSUPPORT +# define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#endif +#ifndef EOPNOTSUPP +# define EOPNOTSUPP WSAEOPNOTSUPP +#endif +#ifndef EPFNOSUPPORT +# define EPFNOSUPPORT WSAEPFNOSUPPORT +#endif +#ifndef EAFNOSUPPORT +# define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif +#ifndef EADDRINUSE +# define EADDRINUSE WSAEADDRINUSE +#endif +#ifndef EADDRNOTAVAIL +# define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#endif +#ifndef ENETDOWN +# define ENETDOWN WSAENETDOWN +#endif +#ifndef ENETUNREACH +# define ENETUNREACH WSAENETUNREACH +#endif +#ifndef ENETRESET +# define ENETRESET WSAENETRESET +#endif +#ifndef ECONNABORTED +# define ECONNABORTED WSAECONNABORTED +#endif +#ifndef ECONNRESET +# define ECONNRESET WSAECONNRESET +#endif +#ifndef ENOBUFS +# define ENOBUFS WSAENOBUFS +#endif +#ifndef EISCONN +# define EISCONN WSAEISCONN +#endif +#ifndef ENOTCONN +# define ENOTCONN WSAENOTCONN +#endif +#ifndef ESHUTDOWN +# define ESHUTDOWN WSAESHUTDOWN +#endif +#ifndef ETOOMANYREFS +# define ETOOMANYREFS WSAETOOMANYREFS +#endif +#ifndef ETIMEDOUT +# define ETIMEDOUT WSAETIMEDOUT +#endif +#ifndef ECONNREFUSED +# define ECONNREFUSED WSAECONNREFUSED +#endif +#ifndef ELOOP +# define ELOOP WSAELOOP +#endif +/*#define ENAMETOOLONG WSAENAMETOOLONG*/ +#ifndef EHOSTDOWN +# define EHOSTDOWN WSAEHOSTDOWN +#endif +#ifndef EHOSTUNREACH +# define EHOSTUNREACH WSAEHOSTUNREACH +#endif +/*#define ENOTEMPTY WSAENOTEMPTY*/ +#ifndef EPROCLIM +# define EPROCLIM WSAEPROCLIM +#endif +#ifndef EUSERS +# define EUSERS WSAEUSERS +#endif +#ifndef EDQUOT +# define EDQUOT WSAEDQUOT +#endif +#ifndef ESTALE +# define ESTALE WSAESTALE +#endif +#ifndef EREMOTE +# define EREMOTE WSAEREMOTE +#endif + +#define F_DUPFD 0 +#if 0 +#define F_GETFD 1 +#define F_SETFD 2 +#define F_GETFL 3 +#endif +#define F_SETFL 4 +#if 0 +#define FD_CLOEXEC 1 /* F_GETFD, F_SETFD */ +#endif +#define O_NONBLOCK 1 + +#undef FD_SET +#define FD_SET(fd, set) do {\ + unsigned int i;\ + SOCKET s = _get_osfhandle(fd);\ +\ + for (i = 0; i < (set)->fd_count; i++) {\ + if ((set)->fd_array[i] == s) {\ + break;\ + }\ + }\ + if (i == (set)->fd_count) {\ + if ((set)->fd_count < FD_SETSIZE) {\ + (set)->fd_array[i] = s;\ + (set)->fd_count++;\ + }\ + }\ +} while(0) + +#undef FD_CLR +#define FD_CLR(f, s) rb_w32_fdclr(f, s) + +#undef FD_ISSET +#define FD_ISSET(f, s) rb_w32_fdisset(f, s) + +#ifdef RUBY_EXPORT +#undef inet_ntop +#define inet_ntop(f,a,n,l) rb_w32_inet_ntop(f,a,n,l) + +#undef inet_pton +#define inet_pton(f,s,d) rb_w32_inet_pton(f,s,d) + +#undef accept +#define accept(s, a, l) rb_w32_accept(s, a, l) + +#undef bind +#define bind(s, a, l) rb_w32_bind(s, a, l) + +#undef connect +#define connect(s, a, l) rb_w32_connect(s, a, l) + +#undef select +#define select(n, r, w, e, t) rb_w32_select(n, r, w, e, t) + +#undef getpeername +#define getpeername(s, a, l) rb_w32_getpeername(s, a, l) + +#undef getsockname +#define getsockname(s, a, l) rb_w32_getsockname(s, a, l) + +#undef getsockopt +#define getsockopt(s, v, n, o, l) rb_w32_getsockopt(s, v, n, o, l) + +#undef ioctlsocket +#define ioctlsocket(s, c, a) rb_w32_ioctlsocket(s, c, a) + +#undef listen +#define listen(s, b) rb_w32_listen(s, b) + +#undef recv +#define recv(s, b, l, f) rb_w32_recv(s, b, l, f) + +#undef recvfrom +#define recvfrom(s, b, l, f, fr, frl) rb_w32_recvfrom(s, b, l, f, fr, frl) + +#undef send +#define send(s, b, l, f) rb_w32_send(s, b, l, f) + +#undef sendto +#define sendto(s, b, l, f, t, tl) rb_w32_sendto(s, b, l, f, t, tl) + +#undef setsockopt +#define setsockopt(s, v, n, o, l) rb_w32_setsockopt(s, v, n, o, l) + +#undef shutdown +#define shutdown(s, h) rb_w32_shutdown(s, h) + +#undef socket +#define socket(s, t, p) rb_w32_socket(s, t, p) + +#undef gethostbyaddr +#define gethostbyaddr(a, l, t) rb_w32_gethostbyaddr(a, l, t) + +#undef gethostbyname +#define gethostbyname(n) rb_w32_gethostbyname(n) + +#undef gethostname +#define gethostname(n, l) rb_w32_gethostname(n, l) + +#undef getprotobyname +#define getprotobyname(n) rb_w32_getprotobyname(n) + +#undef getprotobynumber +#define getprotobynumber(n) rb_w32_getprotobynumber(n) + +#undef getservbyname +#define getservbyname(n, p) rb_w32_getservbyname(n, p) + +#undef getservbyport +#define getservbyport(p, pr) rb_w32_getservbyport(p, pr) + +#undef get_osfhandle +#define get_osfhandle(h) rb_w32_get_osfhandle(h) + +#undef getcwd +#define getcwd(b, s) rb_w32_getcwd(b, s) + +#undef getenv +#define getenv(n) rb_w32_getenv(n) + +#undef rename +#define rename(o, n) rb_w32_rename(o, n) + +#undef times +#define times(t) rb_w32_times(t) + +#undef dup2 +#define dup2(o, n) rb_w32_dup2(o, n) +#endif + +struct tms { + long tms_utime; + long tms_stime; + long tms_cutime; + long tms_cstime; +}; + +int rb_w32_times(struct tms *); + +struct tm *gmtime_r(const time_t *, struct tm *); +struct tm *localtime_r(const time_t *, struct tm *); + +/* thread stuff */ +int rb_w32_sleep(unsigned long msec); +int rb_w32_putc(int, FILE*); +int rb_w32_getc(FILE*); +int rb_w32_open(const char *, int, ...); +int rb_w32_uopen(const char *, int, ...); +int rb_w32_wopen(const WCHAR *, int, ...); +int rb_w32_close(int); +int rb_w32_fclose(FILE*); +int rb_w32_pipe(int[2]); +ssize_t rb_w32_read(int, void *, size_t); +ssize_t rb_w32_write(int, const void *, size_t); +int rb_w32_utime(const char *, const struct utimbuf *); +int rb_w32_uutime(const char *, const struct utimbuf *); +long rb_w32_write_console(uintptr_t, int); /* use uintptr_t instead of VALUE because it's not defined yet here */ +int WINAPI rb_w32_Sleep(unsigned long msec); +int rb_w32_wait_events_blocking(HANDLE *events, int num, DWORD timeout); +int rb_w32_time_subtract(struct timeval *rest, const struct timeval *wait); +int rb_w32_wrap_io_handle(HANDLE, int); +int rb_w32_unwrap_io_handle(int); +WCHAR *rb_w32_mbstr_to_wstr(UINT, const char *, int, long *); +char *rb_w32_wstr_to_mbstr(UINT, const WCHAR *, int, long *); + +/* +== ***CAUTION*** +Since this function is very dangerous, ((*NEVER*)) +* lock any HANDLEs(i.e. Mutex, Semaphore, CriticalSection and so on) or, +* use anything like TRAP_BEG...TRAP_END block structure, +in asynchronous_func_t. +*/ +typedef uintptr_t (*asynchronous_func_t)(uintptr_t self, int argc, uintptr_t* argv); +uintptr_t rb_w32_asynchronize(asynchronous_func_t func, uintptr_t self, int argc, uintptr_t* argv, uintptr_t intrval); + +RUBY_SYMBOL_EXPORT_END + +#ifdef __MINGW_ATTRIB_PURE +/* License: Ruby's */ +/* get rid of bugs in math.h of mingw */ +#define frexp(_X, _Y) __extension__ ({\ + int intpart_frexp_bug = intpart_frexp_bug;\ + double result_frexp_bug = frexp((_X), &intpart_frexp_bug);\ + *(_Y) = intpart_frexp_bug;\ + result_frexp_bug;\ +}) +/* License: Ruby's */ +#define modf(_X, _Y) __extension__ ({\ + double intpart_modf_bug = intpart_modf_bug;\ + double result_modf_bug = modf((_X), &intpart_modf_bug);\ + *(_Y) = intpart_modf_bug;\ + result_modf_bug;\ +}) +#endif + +#if defined(__cplusplus) +#if 0 +{ /* satisfy cc-mode */ +#endif +} /* extern "C" { */ +#endif + +#if defined(__MINGW64__) +/* + * Use powl() instead of broken pow() of x86_64-w64-mingw32. + * This workaround will fix test failures in test_bignum.rb, + * test_fixnum.rb and test_float.rb etc. + */ +static inline double +rb_w32_pow(double x, double y) +{ + return powl(x, y); +} +#elif defined(__MINGW64_VERSION_MAJOR) +double rb_w32_pow(double x, double y); +#endif +#if defined(__MINGW64_VERSION_MAJOR) || defined(__MINGW64__) +#define pow rb_w32_pow +#endif + +#endif /* RUBY_WIN32_H */ diff --git a/ruby/lib/libmsvcrt-ruby210-static.a b/ruby/lib/libmsvcrt-ruby210-static.a new file mode 100644 index 0000000..1734009 Binary files /dev/null and b/ruby/lib/libmsvcrt-ruby210-static.a differ diff --git a/ruby/lib/libmsvcrt-ruby210.dll.a b/ruby/lib/libmsvcrt-ruby210.dll.a new file mode 100644 index 0000000..0b11dad Binary files /dev/null and b/ruby/lib/libmsvcrt-ruby210.dll.a differ diff --git a/ruby/lib/pkgconfig/ruby-2.1.pc b/ruby/lib/pkgconfig/ruby-2.1.pc new file mode 100644 index 0000000..26d4501 --- /dev/null +++ b/ruby/lib/pkgconfig/ruby-2.1.pc @@ -0,0 +1,56 @@ +arch=i386-mingw32 +sitearch=i386-msvcrt +prefix=C:/Users/Luis/Code/oneclick/rubyinstaller/sandbox/ruby21_mingw +exec_prefix=${prefix} +bindir=${exec_prefix}/bin +libdir=${exec_prefix}/lib +includedir=${prefix}/include +MAJOR=2 +MINOR=1 +TEENY=0 +ruby_version=2.1.0 +RUBY_PROGRAM_VERSION=2.1.3 +RUBY_BASE_NAME=ruby +RUBY_VERSION_NAME=${RUBY_BASE_NAME}-${ruby_version} +RUBY_SO_NAME=msvcrt-${RUBY_BASE_NAME}${MAJOR}${MINOR}${TEENY} +RUBY_INSTALL_NAME=${RUBY_BASE_NAME} +DEFFILE= +archlibdir=${libdir}/${arch} +sitearchlibdir=${libdir}/${sitearch} +archincludedir=${includedir}/${arch} +sitearchincludedir=${includedir}/${sitearch} +ruby=${bindir}/${RUBY_INSTALL_NAME}.exe +rubylibprefix=${libdir}/${RUBY_BASE_NAME} +rubyarchprefix=${rubylibprefix}/${arch} +rubysitearchprefix=${rubylibprefix}/${sitearch} +rubylibdir=${rubylibprefix}/${ruby_version} +vendordir=${rubylibprefix}/vendor_ruby +sitedir=${rubylibprefix}/site_ruby +vendorlibdir=${vendordir}/${ruby_version} +sitelibdir=${sitedir}/${ruby_version} +rubyarchdir=${rubylibdir}/${arch} +vendorarchdir=${vendorlibdir}/${sitearch} +sitearchdir=${sitelibdir}/${sitearch} +rubyhdrdir=${includedir}/${RUBY_VERSION_NAME} +vendorhdrdir=${rubyhdrdir}/vendor_ruby +sitehdrdir=${rubyhdrdir}/site_ruby +rubyarchhdrdir=${rubyhdrdir}/${arch} +vendorarchhdrdir=${vendorhdrdir}/${sitearch} +sitearchhdrdir=${sitehdrdir}/${sitearch} +LIBPATH= +LIBRUBY_A=lib${RUBY_SO_NAME}-static.a +LIBRUBY_SO=${RUBY_SO_NAME}.dll +LIBRUBY=lib${RUBY_SO_NAME}.dll.a +LIBRUBYARG_SHARED=-l${RUBY_SO_NAME} +LIBRUBYARG_STATIC=-l${RUBY_SO_NAME}-static +LIBRUBYARG=${LIBRUBYARG_SHARED} +LIBS=-lshell32 -lws2_32 -liphlpapi -limagehlp -lshlwapi +DLDFLAGS= -Wl,--enable-auto-image-base,--enable-auto-import ${DEFFILE} + +Name: Ruby +Description: Object Oriented Script Language +Version: ${ruby_version} +URL: http://www.ruby-lang.org +Cflags: -I${rubyarchhdrdir} -I${rubyhdrdir} +Libs: ${DLDFLAGS} ${LIBRUBYARG_SHARED} ${LIBS} +Requires: diff --git a/ruby/lib/ruby/2.1.0/English.rb b/ruby/lib/ruby/2.1.0/English.rb new file mode 100644 index 0000000..32769fb --- /dev/null +++ b/ruby/lib/ruby/2.1.0/English.rb @@ -0,0 +1,185 @@ +# Include the English library file in a Ruby script, and you can +# reference the global variables such as \VAR{\$\_} using less +# cryptic names, listed in the following table.% \vref{tab:english}. +# +# Without 'English': +# +# $\ = ' -- ' +# "waterbuffalo" =~ /buff/ +# print $", $', $$, "\n" +# +# With English: +# +# require "English" +# +# $OUTPUT_FIELD_SEPARATOR = ' -- ' +# "waterbuffalo" =~ /buff/ +# print $LOADED_FEATURES, $POSTMATCH, $PID, "\n" +# +# Below is a full list of descriptive aliases and their associated global +# variable: +# +# $ERROR_INFO:: $! +# $ERROR_POSITION:: $@ +# $FS:: $; +# $FIELD_SEPARATOR:: $; +# $OFS:: $, +# $OUTPUT_FIELD_SEPARATOR:: $, +# $RS:: $/ +# $INPUT_RECORD_SEPARATOR:: $/ +# $ORS:: $\ +# $OUTPUT_RECORD_SEPARATOR:: $\ +# $INPUT_LINE_NUMBER:: $. +# $NR:: $. +# $LAST_READ_LINE:: $_ +# $DEFAULT_OUTPUT:: $> +# $DEFAULT_INPUT:: $< +# $PID:: $$ +# $PROCESS_ID:: $$ +# $CHILD_STATUS:: $? +# $LAST_MATCH_INFO:: $~ +# $IGNORECASE:: $= +# $ARGV:: $* +# $MATCH:: $& +# $PREMATCH:: $` +# $POSTMATCH:: $' +# $LAST_PAREN_MATCH:: $+ +# +module English end if false + +# The exception object passed to +raise+. +alias $ERROR_INFO $! + +# The stack backtrace generated by the last +# exception. See Kernel.caller for details. Thread local. +alias $ERROR_POSITION $@ + +# The default separator pattern used by String.split. May be +# set from the command line using the -F flag. +alias $FS $; + +# The default separator pattern used by String.split. May be +# set from the command line using the -F flag. +alias $FIELD_SEPARATOR $; + +# The separator string output between the parameters to methods such +# as Kernel.print and Array.join. Defaults to +nil+, +# which adds no text. +alias $OFS $, + +# The separator string output between the parameters to methods such +# as Kernel.print and Array.join. Defaults to +nil+, +# which adds no text. +alias $OUTPUT_FIELD_SEPARATOR $, + +# The input record separator (newline by default). This is the value +# that routines such as Kernel.gets use to determine record +# boundaries. If set to +nil+, +gets+ will read the entire file. +alias $RS $/ + +# The input record separator (newline by default). This is the value +# that routines such as Kernel.gets use to determine record +# boundaries. If set to +nil+, +gets+ will read the entire file. +alias $INPUT_RECORD_SEPARATOR $/ + +# The string appended to the output of every call to methods such as +# Kernel.print and IO.write. The default value is +# +nil+. +alias $ORS $\ + +# The string appended to the output of every call to methods such as +# Kernel.print and IO.write. The default value is +# +nil+. +alias $OUTPUT_RECORD_SEPARATOR $\ + +# The number of the last line read from the current input file. +alias $INPUT_LINE_NUMBER $. + +# The number of the last line read from the current input file. +alias $NR $. + +# The last line read by Kernel.gets or +# Kernel.readline. Many string-related functions in the +# +Kernel+ module operate on $_ by default. The variable is +# local to the current scope. Thread local. +alias $LAST_READ_LINE $_ + +# The destination of output for Kernel.print +# and Kernel.printf. The default value is +# $stdout. +alias $DEFAULT_OUTPUT $> + +# An object that provides access to the concatenation +# of the contents of all the files +# given as command-line arguments, or $stdin +# (in the case where there are no +# arguments). $< supports methods similar to a +# +File+ object: +# +inmode+, +close+, +# closed?, +each+, +# each_byte, each_line, +# +eof+, eof?, +file+, +# +filename+, +fileno+, +# +getc+, +gets+, +lineno+, +# lineno=, +path+, +# +pos+, pos=, +# +read+, +readchar+, +# +readline+, +readlines+, +# +rewind+, +seek+, +skip+, +# +tell+, to_a, to_i, +# to_io, to_s, along with the +# methods in +Enumerable+. The method +file+ +# returns a +File+ object for the file currently +# being read. This may change as $< reads +# through the files on the command line. Read only. +alias $DEFAULT_INPUT $< + +# The process number of the program being executed. Read only. +alias $PID $$ + +# The process number of the program being executed. Read only. +alias $PROCESS_ID $$ + +# The exit status of the last child process to terminate. Read +# only. Thread local. +alias $CHILD_STATUS $? + +# A +MatchData+ object that encapsulates the results of a successful +# pattern match. The variables $&, $`, $', +# and $1 to $9 are all derived from +# $~. Assigning to $~ changes the values of these +# derived variables. This variable is local to the current +# scope. +alias $LAST_MATCH_INFO $~ + +# If set to any value apart from +nil+ or +false+, all pattern matches +# will be case insensitive, string comparisons will ignore case, and +# string hash values will be case insensitive. Deprecated +alias $IGNORECASE $= + +# An array of strings containing the command-line +# options from the invocation of the program. Options +# used by the Ruby interpreter will have been +# removed. Read only. Also known simply as +ARGV+. +alias $ARGV $* + +# The string matched by the last successful pattern +# match. This variable is local to the current +# scope. Read only. +alias $MATCH $& + +# The string preceding the match in the last +# successful pattern match. This variable is local to +# the current scope. Read only. +alias $PREMATCH $` + +# The string following the match in the last +# successful pattern match. This variable is local to +# the current scope. Read only. +alias $POSTMATCH $' + +# The contents of the highest-numbered group matched in the last +# successful pattern match. Thus, in "cat" =~ /(c|a)(t|z)/, +# $+ will be set to "t". This variable is local to the +# current scope. Read only. +alias $LAST_PAREN_MATCH $+ diff --git a/ruby/lib/ruby/2.1.0/Win32API.rb b/ruby/lib/ruby/2.1.0/Win32API.rb new file mode 100644 index 0000000..4d7d488 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/Win32API.rb @@ -0,0 +1,31 @@ +# -*- ruby -*- +# for backward compatibility +warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: Win32API is deprecated after Ruby 1.9.1; use dl directly instead" if $VERBOSE + +require 'dl' + +class Win32API + DLL = {} + TYPEMAP = {"0" => DL::TYPE_VOID, "S" => DL::TYPE_VOIDP, "I" => DL::TYPE_LONG} + POINTER_TYPE = DL::SIZEOF_VOIDP == DL::SIZEOF_LONG_LONG ? 'q*' : 'l!*' + + def initialize(dllname, func, import, export = "0", calltype = :stdcall) + @proto = [import].join.tr("VPpNnLlIi", "0SSI").sub(/^(.)0*$/, '\1') + handle = DLL[dllname] ||= DL.dlopen(dllname) + @func = DL::CFunc.new(handle[func], TYPEMAP[export.tr("VPpNnLlIi", "0SSI")], func, calltype) + rescue DL::DLError => e + raise LoadError, e.message, e.backtrace + end + + def call(*args) + import = @proto.split("") + args.each_with_index do |x, i| + args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S" + args[i], = [x].pack("I").unpack("i") if import[i] == "I" + end + ret, = @func.call(args) + return ret || 0 + end + + alias Call call +end diff --git a/ruby/lib/ruby/2.1.0/abbrev.rb b/ruby/lib/ruby/2.1.0/abbrev.rb new file mode 100644 index 0000000..80350c2 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/abbrev.rb @@ -0,0 +1,136 @@ +#!/usr/bin/env ruby +#-- +# Copyright (c) 2001,2003 Akinori MUSHA +# +# All rights reserved. You can redistribute and/or modify it under +# the same terms as Ruby. +# +# $Idaemons: /home/cvs/rb/abbrev.rb,v 1.2 2001/05/30 09:37:45 knu Exp $ +# $RoughId: abbrev.rb,v 1.4 2003/10/14 19:45:42 knu Exp $ +# $Id: abbrev.rb 39362 2013-02-21 17:35:32Z zzak $ +#++ + +## +# Calculates the set of unique abbreviations for a given set of strings. +# +# require 'abbrev' +# require 'pp' +# +# pp Abbrev.abbrev(['ruby', 'rules']) +# +# Generates: +# +# { "rub" => "ruby", +# "ruby" => "ruby", +# "rul" => "rules", +# "rule" => "rules", +# "rules" => "rules" } +# +# It also provides an array core extension, Array#abbrev. +# +# pp %w{summer winter}.abbrev +# #=> {"summe"=>"summer", +# "summ"=>"summer", +# "sum"=>"summer", +# "su"=>"summer", +# "s"=>"summer", +# "winte"=>"winter", +# "wint"=>"winter", +# "win"=>"winter", +# "wi"=>"winter", +# "w"=>"winter", +# "summer"=>"summer", +# "winter"=>"winter"} + +module Abbrev + + # Given a set of strings, calculate the set of unambiguous + # abbreviations for those strings, and return a hash where the keys + # are all the possible abbreviations and the values are the full + # strings. + # + # Thus, given +words+ is "car" and "cone", the keys pointing to "car" would + # be "ca" and "car", while those pointing to "cone" would be "co", "con", and + # "cone". + # + # require 'abbrev' + # + # Abbrev.abbrev(['car', 'cone']) + # #=> {"ca"=>"car", "con"=>"cone", "co"=>"cone", "car"=>"car", "cone"=>"cone"} + # + # The optional +pattern+ parameter is a pattern or a string. Only + # input strings that match the pattern or start with the string + # are included in the output hash. + # + # Abbrev.abbrev(%w{car box cone}, /b/) + # #=> {"bo"=>"box", "b"=>"box", "box"=>"box"} + def abbrev(words, pattern = nil) + table = {} + seen = Hash.new(0) + + if pattern.is_a?(String) + pattern = /\A#{Regexp.quote(pattern)}/ # regard as a prefix + end + + words.each do |word| + next if word.empty? + word.size.downto(1) { |len| + abbrev = word[0...len] + + next if pattern && pattern !~ abbrev + + case seen[abbrev] += 1 + when 1 + table[abbrev] = word + when 2 + table.delete(abbrev) + else + break + end + } + end + + words.each do |word| + next if pattern && pattern !~ word + + table[word] = word + end + + table + end + + module_function :abbrev +end + +class Array + # Calculates the set of unambiguous abbreviations for the strings in + # +self+. + # + # require 'abbrev' + # %w{ car cone }.abbrev + # #=> {"ca" => "car", "con"=>"cone", "co" => "cone", + # "car"=>"car", "cone" => "cone"} + # + # The optional +pattern+ parameter is a pattern or a string. Only + # input strings that match the pattern or start with the string + # are included in the output hash. + # + # %w{ fast boat day }.abbrev(/^.a/) + # #=> {"fas"=>"fast", "fa"=>"fast", "da"=>"day", + # "fast"=>"fast", "day"=>"day"} + # + # See also Abbrev.abbrev + def abbrev(pattern = nil) + Abbrev::abbrev(self, pattern) + end +end + +if $0 == __FILE__ + while line = gets + hash = line.split.abbrev + + hash.sort.each do |k, v| + puts "#{k} => #{v}" + end + end +end diff --git a/ruby/lib/ruby/2.1.0/base64.rb b/ruby/lib/ruby/2.1.0/base64.rb new file mode 100644 index 0000000..98829f0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/base64.rb @@ -0,0 +1,91 @@ +# +# = base64.rb: methods for base64-encoding and -decoding strings +# + +# The Base64 module provides for the encoding (#encode64, #strict_encode64, +# #urlsafe_encode64) and decoding (#decode64, #strict_decode64, +# #urlsafe_decode64) of binary data using a Base64 representation. +# +# == Example +# +# A simple encoding and decoding. +# +# require "base64" +# +# enc = Base64.encode64('Send reinforcements') +# # -> "U2VuZCByZWluZm9yY2VtZW50cw==\n" +# plain = Base64.decode64(enc) +# # -> "Send reinforcements" +# +# The purpose of using base64 to encode data is that it translates any +# binary data into purely printable characters. + +module Base64 + module_function + + # Returns the Base64-encoded version of +bin+. + # This method complies with RFC 2045. + # Line feeds are added to every 60 encoded characters. + # + # require 'base64' + # Base64.encode64("Now is the time for all good coders\nto learn Ruby") + # + # Generates: + # + # Tm93IGlzIHRoZSB0aW1lIGZvciBhbGwgZ29vZCBjb2RlcnMKdG8gbGVhcm4g + # UnVieQ== + def encode64(bin) + [bin].pack("m") + end + + # Returns the Base64-decoded version of +str+. + # This method complies with RFC 2045. + # Characters outside the base alphabet are ignored. + # + # require 'base64' + # str = 'VGhpcyBpcyBsaW5lIG9uZQpUaGlzIG' + + # 'lzIGxpbmUgdHdvClRoaXMgaXMgbGlu' + + # 'ZSB0aHJlZQpBbmQgc28gb24uLi4K' + # puts Base64.decode64(str) + # + # Generates: + # + # This is line one + # This is line two + # This is line three + # And so on... + def decode64(str) + str.unpack("m").first + end + + # Returns the Base64-encoded version of +bin+. + # This method complies with RFC 4648. + # No line feeds are added. + def strict_encode64(bin) + [bin].pack("m0") + end + + # Returns the Base64-decoded version of +str+. + # This method complies with RFC 4648. + # ArgumentError is raised if +str+ is incorrectly padded or contains + # non-alphabet characters. Note that CR or LF are also rejected. + def strict_decode64(str) + str.unpack("m0").first + end + + # Returns the Base64-encoded version of +bin+. + # This method complies with ``Base 64 Encoding with URL and Filename Safe + # Alphabet'' in RFC 4648. + # The alphabet uses '-' instead of '+' and '_' instead of '/'. + def urlsafe_encode64(bin) + strict_encode64(bin).tr("+/", "-_") + end + + # Returns the Base64-decoded version of +str+. + # This method complies with ``Base 64 Encoding with URL and Filename Safe + # Alphabet'' in RFC 4648. + # The alphabet uses '-' instead of '+' and '_' instead of '/'. + def urlsafe_decode64(str) + strict_decode64(str.tr("-_", "+/")) + end +end diff --git a/ruby/lib/ruby/2.1.0/benchmark.rb b/ruby/lib/ruby/2.1.0/benchmark.rb new file mode 100644 index 0000000..e24547e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/benchmark.rb @@ -0,0 +1,568 @@ +#-- +# benchmark.rb - a performance benchmarking library +# +# $Id: benchmark.rb 43002 2013-09-20 16:05:48Z zzak $ +# +# Created by Gotoken (gotoken@notwork.org). +# +# Documentation by Gotoken (original RD), Lyle Johnson (RDoc conversion), and +# Gavin Sinclair (editing). +#++ +# +# == Overview +# +# The Benchmark module provides methods for benchmarking Ruby code, giving +# detailed reports on the time taken for each task. +# + +# The Benchmark module provides methods to measure and report the time +# used to execute Ruby code. +# +# * Measure the time to construct the string given by the expression +# "a"*1_000_000_000: +# +# require 'benchmark' +# +# puts Benchmark.measure { "a"*1_000_000_000 } +# +# On my machine (OSX 10.8.3 on i5 1.7 Ghz) this generates: +# +# 0.350000 0.400000 0.750000 ( 0.835234) +# +# This report shows the user CPU time, system CPU time, the sum of +# the user and system CPU times, and the elapsed real time. The unit +# of time is seconds. +# +# * Do some experiments sequentially using the #bm method: +# +# require 'benchmark' +# +# n = 5000000 +# Benchmark.bm do |x| +# x.report { for i in 1..n; a = "1"; end } +# x.report { n.times do ; a = "1"; end } +# x.report { 1.upto(n) do ; a = "1"; end } +# end +# +# The result: +# +# user system total real +# 1.010000 0.000000 1.010000 ( 1.014479) +# 1.000000 0.000000 1.000000 ( 0.998261) +# 0.980000 0.000000 0.980000 ( 0.981335) +# +# * Continuing the previous example, put a label in each report: +# +# require 'benchmark' +# +# n = 5000000 +# Benchmark.bm(7) do |x| +# x.report("for:") { for i in 1..n; a = "1"; end } +# x.report("times:") { n.times do ; a = "1"; end } +# x.report("upto:") { 1.upto(n) do ; a = "1"; end } +# end +# +# The result: +# +# user system total real +# for: 1.010000 0.000000 1.010000 ( 1.015688) +# times: 1.000000 0.000000 1.000000 ( 1.003611) +# upto: 1.030000 0.000000 1.030000 ( 1.028098) +# +# * The times for some benchmarks depend on the order in which items +# are run. These differences are due to the cost of memory +# allocation and garbage collection. To avoid these discrepancies, +# the #bmbm method is provided. For example, to compare ways to +# sort an array of floats: +# +# require 'benchmark' +# +# array = (1..1000000).map { rand } +# +# Benchmark.bmbm do |x| +# x.report("sort!") { array.dup.sort! } +# x.report("sort") { array.dup.sort } +# end +# +# The result: +# +# Rehearsal ----------------------------------------- +# sort! 1.490000 0.010000 1.500000 ( 1.490520) +# sort 1.460000 0.000000 1.460000 ( 1.463025) +# -------------------------------- total: 2.960000sec +# +# user system total real +# sort! 1.460000 0.000000 1.460000 ( 1.460465) +# sort 1.450000 0.010000 1.460000 ( 1.448327) +# +# * Report statistics of sequential experiments with unique labels, +# using the #benchmark method: +# +# require 'benchmark' +# include Benchmark # we need the CAPTION and FORMAT constants +# +# n = 5000000 +# Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x| +# tf = x.report("for:") { for i in 1..n; a = "1"; end } +# tt = x.report("times:") { n.times do ; a = "1"; end } +# tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end } +# [tf+tt+tu, (tf+tt+tu)/3] +# end +# +# The result: +# +# user system total real +# for: 0.950000 0.000000 0.950000 ( 0.952039) +# times: 0.980000 0.000000 0.980000 ( 0.984938) +# upto: 0.950000 0.000000 0.950000 ( 0.946787) +# >total: 2.880000 0.000000 2.880000 ( 2.883764) +# >avg: 0.960000 0.000000 0.960000 ( 0.961255) + +module Benchmark + + BENCHMARK_VERSION = "2002-04-25" # :nodoc: + + # Invokes the block with a Benchmark::Report object, which + # may be used to collect and report on the results of individual + # benchmark tests. Reserves +label_width+ leading spaces for + # labels on each line. Prints +caption+ at the top of the + # report, and uses +format+ to format each line. + # Returns an array of Benchmark::Tms objects. + # + # If the block returns an array of + # Benchmark::Tms objects, these will be used to format + # additional lines of output. If +label+ parameters are + # given, these are used to label these extra lines. + # + # _Note_: Other methods provide a simpler interface to this one, and are + # suitable for nearly all benchmarking requirements. See the examples in + # Benchmark, and the #bm and #bmbm methods. + # + # Example: + # + # require 'benchmark' + # include Benchmark # we need the CAPTION and FORMAT constants + # + # n = 5000000 + # Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x| + # tf = x.report("for:") { for i in 1..n; a = "1"; end } + # tt = x.report("times:") { n.times do ; a = "1"; end } + # tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end } + # [tf+tt+tu, (tf+tt+tu)/3] + # end + # + # Generates: + # + # user system total real + # for: 0.970000 0.000000 0.970000 ( 0.970493) + # times: 0.990000 0.000000 0.990000 ( 0.989542) + # upto: 0.970000 0.000000 0.970000 ( 0.972854) + # >total: 2.930000 0.000000 2.930000 ( 2.932889) + # >avg: 0.976667 0.000000 0.976667 ( 0.977630) + # + + def benchmark(caption = "", label_width = nil, format = nil, *labels) # :yield: report + sync = STDOUT.sync + STDOUT.sync = true + label_width ||= 0 + label_width += 1 + format ||= FORMAT + print ' '*label_width + caption unless caption.empty? + report = Report.new(label_width, format) + results = yield(report) + Array === results and results.grep(Tms).each {|t| + print((labels.shift || t.label || "").ljust(label_width), t.format(format)) + } + report.list + ensure + STDOUT.sync = sync unless sync.nil? + end + + + # A simple interface to the #benchmark method, #bm generates sequential + # reports with labels. The parameters have the same meaning as for + # #benchmark. + # + # require 'benchmark' + # + # n = 5000000 + # Benchmark.bm(7) do |x| + # x.report("for:") { for i in 1..n; a = "1"; end } + # x.report("times:") { n.times do ; a = "1"; end } + # x.report("upto:") { 1.upto(n) do ; a = "1"; end } + # end + # + # Generates: + # + # user system total real + # for: 0.960000 0.000000 0.960000 ( 0.957966) + # times: 0.960000 0.000000 0.960000 ( 0.960423) + # upto: 0.950000 0.000000 0.950000 ( 0.954864) + # + + def bm(label_width = 0, *labels, &blk) # :yield: report + benchmark(CAPTION, label_width, FORMAT, *labels, &blk) + end + + + # Sometimes benchmark results are skewed because code executed + # earlier encounters different garbage collection overheads than + # that run later. #bmbm attempts to minimize this effect by running + # the tests twice, the first time as a rehearsal in order to get the + # runtime environment stable, the second time for + # real. GC.start is executed before the start of each of + # the real timings; the cost of this is not included in the + # timings. In reality, though, there's only so much that #bmbm can + # do, and the results are not guaranteed to be isolated from garbage + # collection and other effects. + # + # Because #bmbm takes two passes through the tests, it can + # calculate the required label width. + # + # require 'benchmark' + # + # array = (1..1000000).map { rand } + # + # Benchmark.bmbm do |x| + # x.report("sort!") { array.dup.sort! } + # x.report("sort") { array.dup.sort } + # end + # + # Generates: + # + # Rehearsal ----------------------------------------- + # sort! 1.440000 0.010000 1.450000 ( 1.446833) + # sort 1.440000 0.000000 1.440000 ( 1.448257) + # -------------------------------- total: 2.890000sec + # + # user system total real + # sort! 1.460000 0.000000 1.460000 ( 1.458065) + # sort 1.450000 0.000000 1.450000 ( 1.455963) + # + # #bmbm yields a Benchmark::Job object and returns an array of + # Benchmark::Tms objects. + # + def bmbm(width = 0) # :yield: job + job = Job.new(width) + yield(job) + width = job.width + 1 + sync = STDOUT.sync + STDOUT.sync = true + + # rehearsal + puts 'Rehearsal '.ljust(width+CAPTION.length,'-') + ets = job.list.inject(Tms.new) { |sum,(label,item)| + print label.ljust(width) + res = Benchmark.measure(&item) + print res.format + sum + res + }.format("total: %tsec") + print " #{ets}\n\n".rjust(width+CAPTION.length+2,'-') + + # take + print ' '*width + CAPTION + job.list.map { |label,item| + GC.start + print label.ljust(width) + Benchmark.measure(label, &item).tap { |res| print res } + } + ensure + STDOUT.sync = sync unless sync.nil? + end + + # + # Returns the time used to execute the given block as a + # Benchmark::Tms object. + # + def measure(label = "") # :yield: + t0, r0 = Process.times, Time.now + yield + t1, r1 = Process.times, Time.now + Benchmark::Tms.new(t1.utime - t0.utime, + t1.stime - t0.stime, + t1.cutime - t0.cutime, + t1.cstime - t0.cstime, + r1 - r0, + label) + end + + # + # Returns the elapsed real time used to execute the given block. + # + def realtime # :yield: + r0 = Time.now + yield + Time.now - r0 + end + + module_function :benchmark, :measure, :realtime, :bm, :bmbm + + # + # A Job is a sequence of labelled blocks to be processed by the + # Benchmark.bmbm method. It is of little direct interest to the user. + # + class Job # :nodoc: + # + # Returns an initialized Job instance. + # Usually, one doesn't call this method directly, as new + # Job objects are created by the #bmbm method. + # +width+ is a initial value for the label offset used in formatting; + # the #bmbm method passes its +width+ argument to this constructor. + # + def initialize(width) + @width = width + @list = [] + end + + # + # Registers the given label and block pair in the job list. + # + def item(label = "", &blk) # :yield: + raise ArgumentError, "no block" unless block_given? + label = label.to_s + w = label.length + @width = w if @width < w + @list << [label, blk] + self + end + + alias report item + + # An array of 2-element arrays, consisting of label and block pairs. + attr_reader :list + + # Length of the widest label in the #list. + attr_reader :width + end + + # + # This class is used by the Benchmark.benchmark and Benchmark.bm methods. + # It is of little direct interest to the user. + # + class Report # :nodoc: + # + # Returns an initialized Report instance. + # Usually, one doesn't call this method directly, as new + # Report objects are created by the #benchmark and #bm methods. + # +width+ and +format+ are the label offset and + # format string used by Tms#format. + # + def initialize(width = 0, format = nil) + @width, @format, @list = width, format, [] + end + + # + # Prints the +label+ and measured time for the block, + # formatted by +format+. See Tms#format for the + # formatting rules. + # + def item(label = "", *format, &blk) # :yield: + print label.to_s.ljust(@width) + @list << res = Benchmark.measure(label, &blk) + print res.format(@format, *format) + res + end + + alias report item + + # An array of Benchmark::Tms objects representing each item. + attr_reader :list + end + + + + # + # A data object, representing the times associated with a benchmark + # measurement. + # + class Tms + + # Default caption, see also Benchmark::CAPTION + CAPTION = " user system total real\n" + + # Default format string, see also Benchmark::FORMAT + FORMAT = "%10.6u %10.6y %10.6t %10.6r\n" + + # User CPU time + attr_reader :utime + + # System CPU time + attr_reader :stime + + # User CPU time of children + attr_reader :cutime + + # System CPU time of children + attr_reader :cstime + + # Elapsed real time + attr_reader :real + + # Total time, that is +utime+ + +stime+ + +cutime+ + +cstime+ + attr_reader :total + + # Label + attr_reader :label + + # + # Returns an initialized Tms object which has + # +utime+ as the user CPU time, +stime+ as the system CPU time, + # +cutime+ as the children's user CPU time, +cstime+ as the children's + # system CPU time, +real+ as the elapsed real time and +label+ as the label. + # + def initialize(utime = 0.0, stime = 0.0, cutime = 0.0, cstime = 0.0, real = 0.0, label = nil) + @utime, @stime, @cutime, @cstime, @real, @label = utime, stime, cutime, cstime, real, label.to_s + @total = @utime + @stime + @cutime + @cstime + end + + # + # Returns a new Tms object whose times are the sum of the times for this + # Tms object, plus the time required to execute the code block (+blk+). + # + def add(&blk) # :yield: + self + Benchmark.measure(&blk) + end + + # + # An in-place version of #add. + # + def add!(&blk) + t = Benchmark.measure(&blk) + @utime = utime + t.utime + @stime = stime + t.stime + @cutime = cutime + t.cutime + @cstime = cstime + t.cstime + @real = real + t.real + self + end + + # + # Returns a new Tms object obtained by memberwise summation + # of the individual times for this Tms object with those of the other + # Tms object. + # This method and #/() are useful for taking statistics. + # + def +(other); memberwise(:+, other) end + + # + # Returns a new Tms object obtained by memberwise subtraction + # of the individual times for the other Tms object from those of this + # Tms object. + # + def -(other); memberwise(:-, other) end + + # + # Returns a new Tms object obtained by memberwise multiplication + # of the individual times for this Tms object by _x_. + # + def *(x); memberwise(:*, x) end + + # + # Returns a new Tms object obtained by memberwise division + # of the individual times for this Tms object by _x_. + # This method and #+() are useful for taking statistics. + # + def /(x); memberwise(:/, x) end + + # + # Returns the contents of this Tms object as + # a formatted string, according to a format string + # like that passed to Kernel.format. In addition, #format + # accepts the following extensions: + # + # %u:: Replaced by the user CPU time, as reported by Tms#utime. + # %y:: Replaced by the system CPU time, as reported by #stime (Mnemonic: y of "s*y*stem") + # %U:: Replaced by the children's user CPU time, as reported by Tms#cutime + # %Y:: Replaced by the children's system CPU time, as reported by Tms#cstime + # %t:: Replaced by the total CPU time, as reported by Tms#total + # %r:: Replaced by the elapsed real time, as reported by Tms#real + # %n:: Replaced by the label string, as reported by Tms#label (Mnemonic: n of "*n*ame") + # + # If _format_ is not given, FORMAT is used as default value, detailing the + # user, system and real elapsed time. + # + def format(format = nil, *args) + str = (format || FORMAT).dup + str.gsub!(/(%[-+.\d]*)n/) { "#{$1}s" % label } + str.gsub!(/(%[-+.\d]*)u/) { "#{$1}f" % utime } + str.gsub!(/(%[-+.\d]*)y/) { "#{$1}f" % stime } + str.gsub!(/(%[-+.\d]*)U/) { "#{$1}f" % cutime } + str.gsub!(/(%[-+.\d]*)Y/) { "#{$1}f" % cstime } + str.gsub!(/(%[-+.\d]*)t/) { "#{$1}f" % total } + str.gsub!(/(%[-+.\d]*)r/) { "(#{$1}f)" % real } + format ? str % args : str + end + + # + # Same as #format. + # + def to_s + format + end + + # + # Returns a new 6-element array, consisting of the + # label, user CPU time, system CPU time, children's + # user CPU time, children's system CPU time and elapsed + # real time. + # + def to_a + [@label, @utime, @stime, @cutime, @cstime, @real] + end + + protected + + # + # Returns a new Tms object obtained by memberwise operation +op+ + # of the individual times for this Tms object with those of the other + # Tms object. + # + # +op+ can be a mathematical operation such as +, -, + # *, / + # + def memberwise(op, x) + case x + when Benchmark::Tms + Benchmark::Tms.new(utime.__send__(op, x.utime), + stime.__send__(op, x.stime), + cutime.__send__(op, x.cutime), + cstime.__send__(op, x.cstime), + real.__send__(op, x.real) + ) + else + Benchmark::Tms.new(utime.__send__(op, x), + stime.__send__(op, x), + cutime.__send__(op, x), + cstime.__send__(op, x), + real.__send__(op, x) + ) + end + end + end + + # The default caption string (heading above the output times). + CAPTION = Benchmark::Tms::CAPTION + + # The default format string used to display times. See also Benchmark::Tms#format. + FORMAT = Benchmark::Tms::FORMAT +end + +if __FILE__ == $0 + include Benchmark + + n = ARGV[0].to_i.nonzero? || 50000 + puts %Q([#{n} times iterations of `a = "1"']) + benchmark(CAPTION, 7, FORMAT) do |x| + x.report("for:") {for _ in 1..n; _ = "1"; end} # Benchmark.measure + x.report("times:") {n.times do ; _ = "1"; end} + x.report("upto:") {1.upto(n) do ; _ = "1"; end} + end + + benchmark do + [ + measure{for _ in 1..n; _ = "1"; end}, # Benchmark.measure + measure{n.times do ; _ = "1"; end}, + measure{1.upto(n) do ; _ = "1"; end} + ] + end +end diff --git a/ruby/lib/ruby/2.1.0/bigdecimal/jacobian.rb b/ruby/lib/ruby/2.1.0/bigdecimal/jacobian.rb new file mode 100644 index 0000000..eb9b1c9 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/bigdecimal/jacobian.rb @@ -0,0 +1,87 @@ +# +# require 'bigdecimal/jacobian' +# +# Provides methods to compute the Jacobian matrix of a set of equations at a +# point x. In the methods below: +# +# f is an Object which is used to compute the Jacobian matrix of the equations. +# It must provide the following methods: +# +# f.values(x):: returns the values of all functions at x +# +# f.zero:: returns 0.0 +# f.one:: returns 1.0 +# f.two:: returns 2.0 +# f.ten:: returns 10.0 +# +# f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal. +# +# x is the point at which to compute the Jacobian. +# +# fx is f.values(x). +# +module Jacobian + module_function + + # Determines the equality of two numbers by comparing to zero, or using the epsilon value + def isEqual(a,b,zero=0.0,e=1.0e-8) + aa = a.abs + bb = b.abs + if aa == zero && bb == zero then + true + else + if ((a-b)/(aa+bb)).abs < e then + true + else + false + end + end + end + + + # Computes the derivative of f[i] at x[i]. + # fx is the value of f at x. + def dfdxi(f,fx,x,i) + nRetry = 0 + n = x.size + xSave = x[i] + ok = 0 + ratio = f.ten*f.ten*f.ten + dx = x[i].abs/ratio + dx = fx[i].abs/ratio if isEqual(dx,f.zero,f.zero,f.eps) + dx = f.one/f.ten if isEqual(dx,f.zero,f.zero,f.eps) + until ok>0 do + deriv = [] + nRetry += 1 + if nRetry > 100 + raise "Singular Jacobian matrix. No change at x[" + i.to_s + "]" + end + dx = dx*f.two + x[i] += dx + fxNew = f.values(x) + for j in 0...n do + if !isEqual(fxNew[j],fx[j],f.zero,f.eps) then + ok += 1 + deriv <<= (fxNew[j]-fx[j])/dx + else + deriv <<= f.zero + end + end + x[i] = xSave + end + deriv + end + + # Computes the Jacobian of f at x. fx is the value of f at x. + def jacobian(f,fx,x) + n = x.size + dfdx = Array::new(n*n) + for i in 0...n do + df = dfdxi(f,fx,x,i) + for j in 0...n do + dfdx[j*n+i] = df[j] + end + end + dfdx + end +end diff --git a/ruby/lib/ruby/2.1.0/bigdecimal/ludcmp.rb b/ruby/lib/ruby/2.1.0/bigdecimal/ludcmp.rb new file mode 100644 index 0000000..6cbe29b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/bigdecimal/ludcmp.rb @@ -0,0 +1,88 @@ +require 'bigdecimal' + +# +# Solves a*x = b for x, using LU decomposition. +# +module LUSolve + module_function + + # Performs LU decomposition of the n by n matrix a. + def ludecomp(a,n,zero=0,one=1) + prec = BigDecimal.limit(nil) + ps = [] + scales = [] + for i in 0...n do # pick up largest(abs. val.) element in each row. + ps <<= i + nrmrow = zero + ixn = i*n + for j in 0...n do + biggst = a[ixn+j].abs + nrmrow = biggst if biggst>nrmrow + end + if nrmrow>zero then + scales <<= one.div(nrmrow,prec) + else + raise "Singular matrix" + end + end + n1 = n - 1 + for k in 0...n1 do # Gaussian elimination with partial pivoting. + biggst = zero; + for i in k...n do + size = a[ps[i]*n+k].abs*scales[ps[i]] + if size>biggst then + biggst = size + pividx = i + end + end + raise "Singular matrix" if biggst<=zero + if pividx!=k then + j = ps[k] + ps[k] = ps[pividx] + ps[pividx] = j + end + pivot = a[ps[k]*n+k] + for i in (k+1)...n do + psin = ps[i]*n + a[psin+k] = mult = a[psin+k].div(pivot,prec) + if mult!=zero then + pskn = ps[k]*n + for j in (k+1)...n do + a[psin+j] -= mult.mult(a[pskn+j],prec) + end + end + end + end + raise "Singular matrix" if a[ps[n1]*n+n1] == zero + ps + end + + # Solves a*x = b for x, using LU decomposition. + # + # a is a matrix, b is a constant vector, x is the solution vector. + # + # ps is the pivot, a vector which indicates the permutation of rows performed + # during LU decomposition. + def lusolve(a,b,ps,zero=0.0) + prec = BigDecimal.limit(nil) + n = ps.size + x = [] + for i in 0...n do + dot = zero + psin = ps[i]*n + for j in 0...i do + dot = a[psin+j].mult(x[j],prec) + dot + end + x <<= b[ps[i]] - dot + end + (n-1).downto(0) do |i| + dot = zero + psin = ps[i]*n + for j in (i+1)...n do + dot = a[psin+j].mult(x[j],prec) + dot + end + x[i] = (x[i]-dot).div(a[psin+i],prec) + end + x + end +end diff --git a/ruby/lib/ruby/2.1.0/bigdecimal/math.rb b/ruby/lib/ruby/2.1.0/bigdecimal/math.rb new file mode 100644 index 0000000..907d3b3 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/bigdecimal/math.rb @@ -0,0 +1,231 @@ +require 'bigdecimal' + +# +#-- +# Contents: +# sqrt(x, prec) +# sin (x, prec) +# cos (x, prec) +# atan(x, prec) Note: |x|<1, x=0.9999 may not converge. +# PI (prec) +# E (prec) == exp(1.0,prec) +# +# where: +# x ... BigDecimal number to be computed. +# |x| must be small enough to get convergence. +# prec ... Number of digits to be obtained. +#++ +# +# Provides mathematical functions. +# +# Example: +# +# require "bigdecimal/math" +# +# include BigMath +# +# a = BigDecimal((PI(100)/2).to_s) +# puts sin(a,100) # => 0.10000000000000000000......E1 +# +module BigMath + module_function + + # call-seq: + # sqrt(decimal, numeric) -> BigDecimal + # + # Computes the square root of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # BigMath::sqrt(BigDecimal.new('2'), 16).to_s + # #=> "0.14142135623730950488016887242096975E1" + # + def sqrt(x, prec) + x.sqrt(prec) + end + + # call-seq: + # sin(decimal, numeric) -> BigDecimal + # + # Computes the sine of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is Infinity or NaN, returns NaN. + # + # BigMath::sin(BigMath::PI(5)/4, 5).to_s + # #=> "0.70710678118654752440082036563292800375E0" + # + def sin(x, prec) + raise ArgumentError, "Zero or negative precision for sin" if prec <= 0 + return BigDecimal("NaN") if x.infinite? || x.nan? + n = prec + BigDecimal.double_fig + one = BigDecimal("1") + two = BigDecimal("2") + x = -x if neg = x < 0 + if x > (twopi = two * BigMath.PI(prec)) + if x > 30 + x %= twopi + else + x -= twopi while x > twopi + end + end + x1 = x + x2 = x.mult(x,n) + sign = 1 + y = x + d = y + i = one + z = one + while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + sign = -sign + x1 = x2.mult(x1,n) + i += two + z *= (i-one) * i + d = sign * x1.div(z,m) + y += d + end + neg ? -y : y + end + + # call-seq: + # cos(decimal, numeric) -> BigDecimal + # + # Computes the cosine of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is Infinity or NaN, returns NaN. + # + # BigMath::cos(BigMath::PI(4), 16).to_s + # #=> "-0.999999999999999999999999999999856613163740061349E0" + # + def cos(x, prec) + raise ArgumentError, "Zero or negative precision for cos" if prec <= 0 + return BigDecimal("NaN") if x.infinite? || x.nan? + n = prec + BigDecimal.double_fig + one = BigDecimal("1") + two = BigDecimal("2") + x = -x if x < 0 + if x > (twopi = two * BigMath.PI(prec)) + if x > 30 + x %= twopi + else + x -= twopi while x > twopi + end + end + x1 = one + x2 = x.mult(x,n) + sign = 1 + y = one + d = y + i = BigDecimal("0") + z = one + while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + sign = -sign + x1 = x2.mult(x1,n) + i += two + z *= (i-one) * i + d = sign * x1.div(z,m) + y += d + end + y + end + + # call-seq: + # atan(decimal, numeric) -> BigDecimal + # + # Computes the arctangent of +decimal+ to the specified number of digits of + # precision, +numeric+. + # + # If +decimal+ is NaN, returns NaN. + # + # BigMath::atan(BigDecimal.new('-1'), 16).to_s + # #=> "-0.785398163397448309615660845819878471907514682065E0" + # + def atan(x, prec) + raise ArgumentError, "Zero or negative precision for atan" if prec <= 0 + return BigDecimal("NaN") if x.nan? + pi = PI(prec) + x = -x if neg = x < 0 + return pi.div(neg ? -2 : 2, prec) if x.infinite? + return pi / (neg ? -4 : 4) if x.round(prec) == 1 + x = BigDecimal("1").div(x, prec) if inv = x > 1 + x = (-1 + sqrt(1 + x**2, prec))/x if dbl = x > 0.5 + n = prec + BigDecimal.double_fig + y = x + d = y + t = x + r = BigDecimal("3") + x2 = x.mult(x,n) + while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + t = -t.mult(x2,n) + d = t.div(r,m) + y += d + r += 2 + end + y *= 2 if dbl + y = pi / 2 - y if inv + y = -y if neg + y + end + + # call-seq: + # PI(numeric) -> BigDecimal + # + # Computes the value of pi to the specified number of digits of precision, + # +numeric+. + # + # BigMath::PI(10).to_s + # #=> "0.3141592653589793238462643388813853786957412E1" + # + def PI(prec) + raise ArgumentError, "Zero or negative argument for PI" if prec <= 0 + n = prec + BigDecimal.double_fig + zero = BigDecimal("0") + one = BigDecimal("1") + two = BigDecimal("2") + + m25 = BigDecimal("-0.04") + m57121 = BigDecimal("-57121") + + pi = zero + + d = one + k = one + t = BigDecimal("-80") + while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + t = t*m25 + d = t.div(k,m) + k = k+two + pi = pi + d + end + + d = one + k = one + t = BigDecimal("956") + while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0) + m = BigDecimal.double_fig if m < BigDecimal.double_fig + t = t.div(m57121,n) + d = t.div(k,m) + pi = pi + d + k = k+two + end + pi + end + + # call-seq: + # E(numeric) -> BigDecimal + # + # Computes e (the base of natural logarithms) to the specified number of + # digits of precision, +numeric+. + # + # BigMath::E(10).to_s + # #=> "0.271828182845904523536028752390026306410273E1" + # + def E(prec) + raise ArgumentError, "Zero or negative precision for E" if prec <= 0 + BigMath.exp(1, prec) + end +end diff --git a/ruby/lib/ruby/2.1.0/bigdecimal/newton.rb b/ruby/lib/ruby/2.1.0/bigdecimal/newton.rb new file mode 100644 index 0000000..db1a5ad --- /dev/null +++ b/ruby/lib/ruby/2.1.0/bigdecimal/newton.rb @@ -0,0 +1,79 @@ +require "bigdecimal/ludcmp" +require "bigdecimal/jacobian" + +# +# newton.rb +# +# Solves the nonlinear algebraic equation system f = 0 by Newton's method. +# This program is not dependent on BigDecimal. +# +# To call: +# n = nlsolve(f,x) +# where n is the number of iterations required, +# x is the initial value vector +# f is an Object which is used to compute the values of the equations to be solved. +# It must provide the following methods: +# +# f.values(x):: returns the values of all functions at x +# +# f.zero:: returns 0.0 +# f.one:: returns 1.0 +# f.two:: returns 2.0 +# f.ten:: returns 10.0 +# +# f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal. +# +# On exit, x is the solution vector. +# +module Newton + include LUSolve + include Jacobian + module_function + + def norm(fv,zero=0.0) # :nodoc: + s = zero + n = fv.size + for i in 0...n do + s += fv[i]*fv[i] + end + s + end + + # See also Newton + def nlsolve(f,x) + nRetry = 0 + n = x.size + + f0 = f.values(x) + zero = f.zero + one = f.one + two = f.two + p5 = one/two + d = norm(f0,zero) + minfact = f.ten*f.ten*f.ten + minfact = one/minfact + e = f.eps + while d >= e do + nRetry += 1 + # Not yet converged. => Compute Jacobian matrix + dfdx = jacobian(f,f0,x) + # Solve dfdx*dx = -f0 to estimate dx + dx = lusolve(dfdx,f0,ludecomp(dfdx,n,zero,one),zero) + fact = two + xs = x.dup + begin + fact *= p5 + if fact < minfact then + raise "Failed to reduce function values." + end + for i in 0...n do + x[i] = xs[i] - dx[i]*fact + end + f0 = f.values(x) + dn = norm(f0,zero) + end while(dn>=d) + d = dn + end + nRetry + end +end diff --git a/ruby/lib/ruby/2.1.0/bigdecimal/util.rb b/ruby/lib/ruby/2.1.0/bigdecimal/util.rb new file mode 100644 index 0000000..82c82c8 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/bigdecimal/util.rb @@ -0,0 +1,127 @@ +# BigDecimal extends the native Integer class to provide the #to_d method. +# +# When you require the BigDecimal library in your application, this methodwill +# be available on Integer objects. +class Integer < Numeric + # call-seq: + # int.to_d -> bigdecimal + # + # Convert +int+ to a BigDecimal and return it. + # + # require 'bigdecimal' + # require 'bigdecimal/util' + # + # 42.to_d + # # => # + # + def to_d + BigDecimal(self) + end +end + +# BigDecimal extends the native Float class to provide the #to_d method. +# +# When you require BigDecimal in your application, this method will be +# available on Float objects. +class Float < Numeric + # call-seq: + # flt.to_d -> bigdecimal + # + # Convert +flt+ to a BigDecimal and return it. + # + # require 'bigdecimal' + # require 'bigdecimal/util' + # + # 0.5.to_d + # # => # + # + def to_d(precision=nil) + BigDecimal(self, precision || Float::DIG) + end +end + +# BigDecimal extends the native String class to provide the #to_d method. +# +# When you require BigDecimal in your application, this method will be +# available on String objects. +class String + # call-seq: + # string.to_d -> bigdecimal + # + # Convert +string+ to a BigDecimal and return it. + # + # require 'bigdecimal' + # require 'bigdecimal/util' + # + # "0.5".to_d + # # => # + # + def to_d + BigDecimal(self) + end +end + +# BigDecimal extends the native Numeric class to provide the #to_digits and +# #to_d methods. +# +# When you require BigDecimal in your application, this method will be +# available on BigDecimal objects. +class BigDecimal < Numeric + # call-seq: + # a.to_digits -> string + # + # Converts a BigDecimal to a String of the form "nnnnnn.mmm". + # This method is deprecated; use BigDecimal#to_s("F") instead. + # + # require 'bigdecimal' + # require 'bigdecimal/util' + # + # d = BigDecimal.new("3.14") + # d.to_digits + # # => "3.14" + def to_digits + if self.nan? || self.infinite? || self.zero? + self.to_s + else + i = self.to_i.to_s + _,f,_,z = self.frac.split + i + "." + ("0"*(-z)) + f + end + end + + # call-seq: + # a.to_d -> bigdecimal + # + # Returns self. + def to_d + self + end +end + +# BigDecimal extends the native Rational class to provide the #to_d method. +# +# When you require BigDecimal in your application, this method will be +# available on Rational objects. +class Rational < Numeric + # call-seq: + # r.to_d(precision) -> bigdecimal + # + # Converts a Rational to a BigDecimal. + # + # The required +precision+ parameter is used to determine the amount of + # significant digits for the result. See BigDecimal#div for more information, + # as it is used along with the #denominator and the +precision+ for + # parameters. + # + # r = (22/7.0).to_r + # # => (7077085128725065/2251799813685248) + # r.to_d(3) + # # => # + def to_d(precision) + if precision <= 0 + raise ArgumentError, "negative precision" + end + num = self.numerator + BigDecimal(num).div(self.denominator, precision) + end +end diff --git a/ruby/lib/ruby/2.1.0/cgi.rb b/ruby/lib/ruby/2.1.0/cgi.rb new file mode 100644 index 0000000..15676ea --- /dev/null +++ b/ruby/lib/ruby/2.1.0/cgi.rb @@ -0,0 +1,297 @@ +# +# cgi.rb - cgi support library +# +# Copyright (C) 2000 Network Applied Communication Laboratory, Inc. +# +# Copyright (C) 2000 Information-technology Promotion Agency, Japan +# +# Author: Wakou Aoyama +# +# Documentation: Wakou Aoyama (RDoc'd and embellished by William Webber) +# + +raise "Please, use ruby 1.9.0 or later." if RUBY_VERSION < "1.9.0" + +# == Overview +# +# The Common Gateway Interface (CGI) is a simple protocol for passing an HTTP +# request from a web server to a standalone program, and returning the output +# to the web browser. Basically, a CGI program is called with the parameters +# of the request passed in either in the environment (GET) or via $stdin +# (POST), and everything it prints to $stdout is returned to the client. +# +# This file holds the CGI class. This class provides functionality for +# retrieving HTTP request parameters, managing cookies, and generating HTML +# output. +# +# The file CGI::Session provides session management functionality; see that +# class for more details. +# +# See http://www.w3.org/CGI/ for more information on the CGI protocol. +# +# == Introduction +# +# CGI is a large class, providing several categories of methods, many of which +# are mixed in from other modules. Some of the documentation is in this class, +# some in the modules CGI::QueryExtension and CGI::HtmlExtension. See +# CGI::Cookie for specific information on handling cookies, and cgi/session.rb +# (CGI::Session) for information on sessions. +# +# For queries, CGI provides methods to get at environmental variables, +# parameters, cookies, and multipart request data. For responses, CGI provides +# methods for writing output and generating HTML. +# +# Read on for more details. Examples are provided at the bottom. +# +# == Queries +# +# The CGI class dynamically mixes in parameter and cookie-parsing +# functionality, environmental variable access, and support for +# parsing multipart requests (including uploaded files) from the +# CGI::QueryExtension module. +# +# === Environmental Variables +# +# The standard CGI environmental variables are available as read-only +# attributes of a CGI object. The following is a list of these variables: +# +# +# AUTH_TYPE HTTP_HOST REMOTE_IDENT +# CONTENT_LENGTH HTTP_NEGOTIATE REMOTE_USER +# CONTENT_TYPE HTTP_PRAGMA REQUEST_METHOD +# GATEWAY_INTERFACE HTTP_REFERER SCRIPT_NAME +# HTTP_ACCEPT HTTP_USER_AGENT SERVER_NAME +# HTTP_ACCEPT_CHARSET PATH_INFO SERVER_PORT +# HTTP_ACCEPT_ENCODING PATH_TRANSLATED SERVER_PROTOCOL +# HTTP_ACCEPT_LANGUAGE QUERY_STRING SERVER_SOFTWARE +# HTTP_CACHE_CONTROL REMOTE_ADDR +# HTTP_FROM REMOTE_HOST +# +# +# For each of these variables, there is a corresponding attribute with the +# same name, except all lower case and without a preceding HTTP_. +# +content_length+ and +server_port+ are integers; the rest are strings. +# +# === Parameters +# +# The method #params() returns a hash of all parameters in the request as +# name/value-list pairs, where the value-list is an Array of one or more +# values. The CGI object itself also behaves as a hash of parameter names +# to values, but only returns a single value (as a String) for each +# parameter name. +# +# For instance, suppose the request contains the parameter +# "favourite_colours" with the multiple values "blue" and "green". The +# following behaviour would occur: +# +# cgi.params["favourite_colours"] # => ["blue", "green"] +# cgi["favourite_colours"] # => "blue" +# +# If a parameter does not exist, the former method will return an empty +# array, the latter an empty string. The simplest way to test for existence +# of a parameter is by the #has_key? method. +# +# === Cookies +# +# HTTP Cookies are automatically parsed from the request. They are available +# from the #cookies() accessor, which returns a hash from cookie name to +# CGI::Cookie object. +# +# === Multipart requests +# +# If a request's method is POST and its content type is multipart/form-data, +# then it may contain uploaded files. These are stored by the QueryExtension +# module in the parameters of the request. The parameter name is the name +# attribute of the file input field, as usual. However, the value is not +# a string, but an IO object, either an IOString for small files, or a +# Tempfile for larger ones. This object also has the additional singleton +# methods: +# +# #local_path():: the path of the uploaded file on the local filesystem +# #original_filename():: the name of the file on the client computer +# #content_type():: the content type of the file +# +# == Responses +# +# The CGI class provides methods for sending header and content output to +# the HTTP client, and mixes in methods for programmatic HTML generation +# from CGI::HtmlExtension and CGI::TagMaker modules. The precise version of HTML +# to use for HTML generation is specified at object creation time. +# +# === Writing output +# +# The simplest way to send output to the HTTP client is using the #out() method. +# This takes the HTTP headers as a hash parameter, and the body content +# via a block. The headers can be generated as a string using the #http_header() +# method. The output stream can be written directly to using the #print() +# method. +# +# === Generating HTML +# +# Each HTML element has a corresponding method for generating that +# element as a String. The name of this method is the same as that +# of the element, all lowercase. The attributes of the element are +# passed in as a hash, and the body as a no-argument block that evaluates +# to a String. The HTML generation module knows which elements are +# always empty, and silently drops any passed-in body. It also knows +# which elements require matching closing tags and which don't. However, +# it does not know what attributes are legal for which elements. +# +# There are also some additional HTML generation methods mixed in from +# the CGI::HtmlExtension module. These include individual methods for the +# different types of form inputs, and methods for elements that commonly +# take particular attributes where the attributes can be directly specified +# as arguments, rather than via a hash. +# +# === Utility HTML escape and other methods like a function. +# +# There are some utility tool defined in cgi/util.rb . +# And when include, you can use utility methods like a function. +# +# == Examples of use +# +# === Get form values +# +# require "cgi" +# cgi = CGI.new +# value = cgi['field_name'] # <== value string for 'field_name' +# # if not 'field_name' included, then return "". +# fields = cgi.keys # <== array of field names +# +# # returns true if form has 'field_name' +# cgi.has_key?('field_name') +# cgi.has_key?('field_name') +# cgi.include?('field_name') +# +# CAUTION! cgi['field_name'] returned an Array with the old +# cgi.rb(included in Ruby 1.6) +# +# === Get form values as hash +# +# require "cgi" +# cgi = CGI.new +# params = cgi.params +# +# cgi.params is a hash. +# +# cgi.params['new_field_name'] = ["value"] # add new param +# cgi.params['field_name'] = ["new_value"] # change value +# cgi.params.delete('field_name') # delete param +# cgi.params.clear # delete all params +# +# +# === Save form values to file +# +# require "pstore" +# db = PStore.new("query.db") +# db.transaction do +# db["params"] = cgi.params +# end +# +# +# === Restore form values from file +# +# require "pstore" +# db = PStore.new("query.db") +# db.transaction do +# cgi.params = db["params"] +# end +# +# +# === Get multipart form values +# +# require "cgi" +# cgi = CGI.new +# value = cgi['field_name'] # <== value string for 'field_name' +# value.read # <== body of value +# value.local_path # <== path to local file of value +# value.original_filename # <== original filename of value +# value.content_type # <== content_type of value +# +# and value has StringIO or Tempfile class methods. +# +# === Get cookie values +# +# require "cgi" +# cgi = CGI.new +# values = cgi.cookies['name'] # <== array of 'name' +# # if not 'name' included, then return []. +# names = cgi.cookies.keys # <== array of cookie names +# +# and cgi.cookies is a hash. +# +# === Get cookie objects +# +# require "cgi" +# cgi = CGI.new +# for name, cookie in cgi.cookies +# cookie.expires = Time.now + 30 +# end +# cgi.out("cookie" => cgi.cookies) {"string"} +# +# cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... } +# +# require "cgi" +# cgi = CGI.new +# cgi.cookies['name'].expires = Time.now + 30 +# cgi.out("cookie" => cgi.cookies['name']) {"string"} +# +# === Print http header and html string to $DEFAULT_OUTPUT ($>) +# +# require "cgi" +# cgi = CGI.new("html4") # add HTML generation methods +# cgi.out do +# cgi.html do +# cgi.head do +# cgi.title { "TITLE" } +# end + +# cgi.body do +# cgi.form("ACTION" => "uri") do +# cgi.p do +# cgi.textarea("get_text") + +# cgi.br + +# cgi.submit +# end +# end + +# cgi.pre do +# CGI::escapeHTML( +# "params: #{cgi.params.inspect}\n" + +# "cookies: #{cgi.cookies.inspect}\n" + +# ENV.collect do |key, value| +# "#{key} --> #{value}\n" +# end.join("") +# ) +# end +# end +# end +# end +# +# # add HTML generation methods +# CGI.new("html3") # html3.2 +# CGI.new("html4") # html4.01 (Strict) +# CGI.new("html4Tr") # html4.01 Transitional +# CGI.new("html4Fr") # html4.01 Frameset +# CGI.new("html5") # html5 +# +# === Some utility methods +# +# require 'cgi/util' +# CGI.escapeHTML('Usage: foo "bar" ') +# +# +# === Some utility methods like a function +# +# require 'cgi/util' +# include CGI::Util +# escapeHTML('Usage: foo "bar" ') +# h('Usage: foo "bar" ') # alias +# +# + +class CGI +end + +require 'cgi/core' +require 'cgi/cookie' +require 'cgi/util' +CGI.autoload(:HtmlExtension, 'cgi/html') diff --git a/ruby/lib/ruby/2.1.0/cgi/cookie.rb b/ruby/lib/ruby/2.1.0/cgi/cookie.rb new file mode 100644 index 0000000..3ec884d --- /dev/null +++ b/ruby/lib/ruby/2.1.0/cgi/cookie.rb @@ -0,0 +1,170 @@ +require 'cgi/util' +class CGI + # Class representing an HTTP cookie. + # + # In addition to its specific fields and methods, a Cookie instance + # is a delegator to the array of its values. + # + # See RFC 2965. + # + # == Examples of use + # cookie1 = CGI::Cookie.new("name", "value1", "value2", ...) + # cookie1 = CGI::Cookie.new("name" => "name", "value" => "value") + # cookie1 = CGI::Cookie.new('name' => 'name', + # 'value' => ['value1', 'value2', ...], + # 'path' => 'path', # optional + # 'domain' => 'domain', # optional + # 'expires' => Time.now, # optional + # 'secure' => true # optional + # ) + # + # cgi.out("cookie" => [cookie1, cookie2]) { "string" } + # + # name = cookie1.name + # values = cookie1.value + # path = cookie1.path + # domain = cookie1.domain + # expires = cookie1.expires + # secure = cookie1.secure + # + # cookie1.name = 'name' + # cookie1.value = ['value1', 'value2', ...] + # cookie1.path = 'path' + # cookie1.domain = 'domain' + # cookie1.expires = Time.now + 30 + # cookie1.secure = true + class Cookie < Array + @@accept_charset="UTF-8" unless defined?(@@accept_charset) + + # Create a new CGI::Cookie object. + # + # :call-seq: + # Cookie.new(name_string,*value) + # Cookie.new(options_hash) + # + # +name_string+:: + # The name of the cookie; in this form, there is no #domain or + # #expiration. The #path is gleaned from the +SCRIPT_NAME+ environment + # variable, and #secure is false. + # *value:: + # value or list of values of the cookie + # +options_hash+:: + # A Hash of options to initialize this Cookie. Possible options are: + # + # name:: the name of the cookie. Required. + # value:: the cookie's value or list of values. + # path:: the path for which this cookie applies. Defaults to the + # the value of the +SCRIPT_NAME+ environment variable. + # domain:: the domain for which this cookie applies. + # expires:: the time at which this cookie expires, as a +Time+ object. + # secure:: whether this cookie is a secure cookie or not (default to + # false). Secure cookies are only transmitted to HTTPS + # servers. + # + # These keywords correspond to attributes of the cookie object. + def initialize(name = "", *value) + @domain = nil + @expires = nil + if name.kind_of?(String) + @name = name + %r|^(.*/)|.match(ENV["SCRIPT_NAME"]) + @path = ($1 or "") + @secure = false + return super(value) + end + + options = name + unless options.has_key?("name") + raise ArgumentError, "`name' required" + end + + @name = options["name"] + value = Array(options["value"]) + # simple support for IE + if options["path"] + @path = options["path"] + else + %r|^(.*/)|.match(ENV["SCRIPT_NAME"]) + @path = ($1 or "") + end + @domain = options["domain"] + @expires = options["expires"] + @secure = options["secure"] == true ? true : false + + super(value) + end + + # Name of this cookie, as a +String+ + attr_accessor :name + # Path for which this cookie applies, as a +String+ + attr_accessor :path + # Domain for which this cookie applies, as a +String+ + attr_accessor :domain + # Time at which this cookie expires, as a +Time+ + attr_accessor :expires + # True if this cookie is secure; false otherwise + attr_reader("secure") + + # Returns the value or list of values for this cookie. + def value + self + end + + # Replaces the value of this cookie with a new value or list of values. + def value=(val) + replace(Array(val)) + end + + # Set whether the Cookie is a secure cookie or not. + # + # +val+ must be a boolean. + def secure=(val) + @secure = val if val == true or val == false + @secure + end + + # Convert the Cookie to its string representation. + def to_s + val = collect{|v| CGI.escape(v) }.join("&") + buf = "#{@name}=#{val}" + buf << "; domain=#{@domain}" if @domain + buf << "; path=#{@path}" if @path + buf << "; expires=#{CGI::rfc1123_date(@expires)}" if @expires + buf << "; secure" if @secure == true + buf + end + + # Parse a raw cookie string into a hash of cookie-name=>Cookie + # pairs. + # + # cookies = CGI::Cookie.parse("raw_cookie_string") + # # { "name1" => cookie1, "name2" => cookie2, ... } + # + def self.parse(raw_cookie) + cookies = Hash.new([]) + return cookies unless raw_cookie + + raw_cookie.split(/[;,]\s?/).each do |pairs| + name, values = pairs.split('=',2) + next unless name and values + name = CGI.unescape(name) + values ||= "" + values = values.split('&').collect{|v| CGI.unescape(v,@@accept_charset) } + if cookies.has_key?(name) + values = cookies[name].value + values + end + cookies[name] = Cookie.new(name, *values) + end + + cookies + end + + # A summary of cookie string. + def inspect + "#" + end + + end # class Cookie +end + + diff --git a/ruby/lib/ruby/2.1.0/cgi/core.rb b/ruby/lib/ruby/2.1.0/cgi/core.rb new file mode 100644 index 0000000..82f4648 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/cgi/core.rb @@ -0,0 +1,859 @@ +#-- +# Methods for generating HTML, parsing CGI-related parameters, and +# generating HTTP responses. +#++ +class CGI + + $CGI_ENV = ENV # for FCGI support + + # String for carriage return + CR = "\015" + + # String for linefeed + LF = "\012" + + # Standard internet newline sequence + EOL = CR + LF + + REVISION = '$Id: core.rb 44184 2013-12-13 16:11:12Z akr $' #:nodoc: + + # Whether processing will be required in binary vs text + NEEDS_BINMODE = File::BINARY != 0 + + # Path separators in different environments. + PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'} + + # HTTP status codes. + HTTP_STATUS = { + "OK" => "200 OK", + "PARTIAL_CONTENT" => "206 Partial Content", + "MULTIPLE_CHOICES" => "300 Multiple Choices", + "MOVED" => "301 Moved Permanently", + "REDIRECT" => "302 Found", + "NOT_MODIFIED" => "304 Not Modified", + "BAD_REQUEST" => "400 Bad Request", + "AUTH_REQUIRED" => "401 Authorization Required", + "FORBIDDEN" => "403 Forbidden", + "NOT_FOUND" => "404 Not Found", + "METHOD_NOT_ALLOWED" => "405 Method Not Allowed", + "NOT_ACCEPTABLE" => "406 Not Acceptable", + "LENGTH_REQUIRED" => "411 Length Required", + "PRECONDITION_FAILED" => "412 Precondition Failed", + "SERVER_ERROR" => "500 Internal Server Error", + "NOT_IMPLEMENTED" => "501 Method Not Implemented", + "BAD_GATEWAY" => "502 Bad Gateway", + "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates" + } + + # :startdoc: + + # Synonym for ENV. + def env_table + ENV + end + + # Synonym for $stdin. + def stdinput + $stdin + end + + # Synonym for $stdout. + def stdoutput + $stdout + end + + private :env_table, :stdinput, :stdoutput + + # Create an HTTP header block as a string. + # + # :call-seq: + # http_header(content_type_string="text/html") + # http_header(headers_hash) + # + # Includes the empty line that ends the header block. + # + # +content_type_string+:: + # If this form is used, this string is the Content-Type + # +headers_hash+:: + # A Hash of header values. The following header keys are recognized: + # + # type:: The Content-Type header. Defaults to "text/html" + # charset:: The charset of the body, appended to the Content-Type header. + # nph:: A boolean value. If true, prepend protocol string and status + # code, and date; and sets default values for "server" and + # "connection" if not explicitly set. + # status:: + # The HTTP status code as a String, returned as the Status header. The + # values are: + # + # OK:: 200 OK + # PARTIAL_CONTENT:: 206 Partial Content + # MULTIPLE_CHOICES:: 300 Multiple Choices + # MOVED:: 301 Moved Permanently + # REDIRECT:: 302 Found + # NOT_MODIFIED:: 304 Not Modified + # BAD_REQUEST:: 400 Bad Request + # AUTH_REQUIRED:: 401 Authorization Required + # FORBIDDEN:: 403 Forbidden + # NOT_FOUND:: 404 Not Found + # METHOD_NOT_ALLOWED:: 405 Method Not Allowed + # NOT_ACCEPTABLE:: 406 Not Acceptable + # LENGTH_REQUIRED:: 411 Length Required + # PRECONDITION_FAILED:: 412 Precondition Failed + # SERVER_ERROR:: 500 Internal Server Error + # NOT_IMPLEMENTED:: 501 Method Not Implemented + # BAD_GATEWAY:: 502 Bad Gateway + # VARIANT_ALSO_VARIES:: 506 Variant Also Negotiates + # + # server:: The server software, returned as the Server header. + # connection:: The connection type, returned as the Connection header (for + # instance, "close". + # length:: The length of the content that will be sent, returned as the + # Content-Length header. + # language:: The language of the content, returned as the Content-Language + # header. + # expires:: The time on which the current content expires, as a +Time+ + # object, returned as the Expires header. + # cookie:: + # A cookie or cookies, returned as one or more Set-Cookie headers. The + # value can be the literal string of the cookie; a CGI::Cookie object; + # an Array of literal cookie strings or Cookie objects; or a hash all of + # whose values are literal cookie strings or Cookie objects. + # + # These cookies are in addition to the cookies held in the + # @output_cookies field. + # + # Other headers can also be set; they are appended as key: value. + # + # Examples: + # + # http_header + # # Content-Type: text/html + # + # http_header("text/plain") + # # Content-Type: text/plain + # + # http_header("nph" => true, + # "status" => "OK", # == "200 OK" + # # "status" => "200 GOOD", + # "server" => ENV['SERVER_SOFTWARE'], + # "connection" => "close", + # "type" => "text/html", + # "charset" => "iso-2022-jp", + # # Content-Type: text/html; charset=iso-2022-jp + # "length" => 103, + # "language" => "ja", + # "expires" => Time.now + 30, + # "cookie" => [cookie1, cookie2], + # "my_header1" => "my_value" + # "my_header2" => "my_value") + # + # This method does not perform charset conversion. + def http_header(options='text/html') + if options.is_a?(String) + content_type = options + buf = _header_for_string(content_type) + elsif options.is_a?(Hash) + if options.size == 1 && options.has_key?('type') + content_type = options['type'] + buf = _header_for_string(content_type) + else + buf = _header_for_hash(options.dup) + end + else + raise ArgumentError.new("expected String or Hash but got #{options.class}") + end + if defined?(MOD_RUBY) + _header_for_modruby(buf) + return '' + else + buf << EOL # empty line of separator + return buf + end + end # http_header() + + # This method is an alias for #http_header, when HTML5 tag maker is inactive. + # + # NOTE: use #http_header to create HTTP header blocks, this alias is only + # provided for backwards compatibility. + # + # Using #header with the HTML5 tag maker will create a
element. + alias :header :http_header + + def _header_for_string(content_type) #:nodoc: + buf = '' + if nph?() + buf << "#{$CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'} 200 OK#{EOL}" + buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}" + buf << "Server: #{$CGI_ENV['SERVER_SOFTWARE']}#{EOL}" + buf << "Connection: close#{EOL}" + end + buf << "Content-Type: #{content_type}#{EOL}" + if @output_cookies + @output_cookies.each {|cookie| buf << "Set-Cookie: #{cookie}#{EOL}" } + end + return buf + end # _header_for_string + private :_header_for_string + + def _header_for_hash(options) #:nodoc: + buf = '' + ## add charset to option['type'] + options['type'] ||= 'text/html' + charset = options.delete('charset') + options['type'] += "; charset=#{charset}" if charset + ## NPH + options.delete('nph') if defined?(MOD_RUBY) + if options.delete('nph') || nph?() + protocol = $CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0' + status = options.delete('status') + status = HTTP_STATUS[status] || status || '200 OK' + buf << "#{protocol} #{status}#{EOL}" + buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}" + options['server'] ||= $CGI_ENV['SERVER_SOFTWARE'] || '' + options['connection'] ||= 'close' + end + ## common headers + status = options.delete('status') + buf << "Status: #{HTTP_STATUS[status] || status}#{EOL}" if status + server = options.delete('server') + buf << "Server: #{server}#{EOL}" if server + connection = options.delete('connection') + buf << "Connection: #{connection}#{EOL}" if connection + type = options.delete('type') + buf << "Content-Type: #{type}#{EOL}" #if type + length = options.delete('length') + buf << "Content-Length: #{length}#{EOL}" if length + language = options.delete('language') + buf << "Content-Language: #{language}#{EOL}" if language + expires = options.delete('expires') + buf << "Expires: #{CGI.rfc1123_date(expires)}#{EOL}" if expires + ## cookie + if cookie = options.delete('cookie') + case cookie + when String, Cookie + buf << "Set-Cookie: #{cookie}#{EOL}" + when Array + arr = cookie + arr.each {|c| buf << "Set-Cookie: #{c}#{EOL}" } + when Hash + hash = cookie + hash.each_value {|c| buf << "Set-Cookie: #{c}#{EOL}" } + end + end + if @output_cookies + @output_cookies.each {|c| buf << "Set-Cookie: #{c}#{EOL}" } + end + ## other headers + options.each do |key, value| + buf << "#{key}: #{value}#{EOL}" + end + return buf + end # _header_for_hash + private :_header_for_hash + + def nph? #:nodoc: + return /IIS\/(\d+)/.match($CGI_ENV['SERVER_SOFTWARE']) && $1.to_i < 5 + end + + def _header_for_modruby(buf) #:nodoc: + request = Apache::request + buf.scan(/([^:]+): (.+)#{EOL}/o) do |name, value| + warn sprintf("name:%s value:%s\n", name, value) if $DEBUG + case name + when 'Set-Cookie' + request.headers_out.add(name, value) + when /^status$/i + request.status_line = value + request.status = value.to_i + when /^content-type$/i + request.content_type = value + when /^content-encoding$/i + request.content_encoding = value + when /^location$/i + request.status = 302 if request.status == 200 + request.headers_out[name] = value + else + request.headers_out[name] = value + end + end + request.send_http_header + return '' + end + private :_header_for_modruby + + # Print an HTTP header and body to $DEFAULT_OUTPUT ($>) + # + # :call-seq: + # cgi.out(content_type_string='text/html') + # cgi.out(headers_hash) + # + # +content_type_string+:: + # If a string is passed, it is assumed to be the content type. + # +headers_hash+:: + # This is a Hash of headers, similar to that used by #http_header. + # +block+:: + # A block is required and should evaluate to the body of the response. + # + # Content-Length is automatically calculated from the size of + # the String returned by the content block. + # + # If ENV['REQUEST_METHOD'] == "HEAD", then only the header + # is output (the content block is still required, but it is ignored). + # + # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then the + # content is converted to this charset, and the language is set to "ja". + # + # Example: + # + # cgi = CGI.new + # cgi.out{ "string" } + # # Content-Type: text/html + # # Content-Length: 6 + # # + # # string + # + # cgi.out("text/plain") { "string" } + # # Content-Type: text/plain + # # Content-Length: 6 + # # + # # string + # + # cgi.out("nph" => true, + # "status" => "OK", # == "200 OK" + # "server" => ENV['SERVER_SOFTWARE'], + # "connection" => "close", + # "type" => "text/html", + # "charset" => "iso-2022-jp", + # # Content-Type: text/html; charset=iso-2022-jp + # "language" => "ja", + # "expires" => Time.now + (3600 * 24 * 30), + # "cookie" => [cookie1, cookie2], + # "my_header1" => "my_value", + # "my_header2" => "my_value") { "string" } + # # HTTP/1.1 200 OK + # # Date: Sun, 15 May 2011 17:35:54 GMT + # # Server: Apache 2.2.0 + # # Connection: close + # # Content-Type: text/html; charset=iso-2022-jp + # # Content-Length: 6 + # # Content-Language: ja + # # Expires: Tue, 14 Jun 2011 17:35:54 GMT + # # Set-Cookie: foo + # # Set-Cookie: bar + # # my_header1: my_value + # # my_header2: my_value + # # + # # string + def out(options = "text/html") # :yield: + + options = { "type" => options } if options.kind_of?(String) + content = yield + options["length"] = content.bytesize.to_s + output = stdoutput + output.binmode if defined? output.binmode + output.print http_header(options) + output.print content unless "HEAD" == env_table['REQUEST_METHOD'] + end + + + # Print an argument or list of arguments to the default output stream + # + # cgi = CGI.new + # cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print + def print(*options) + stdoutput.print(*options) + end + + # Parse an HTTP query string into a hash of key=>value pairs. + # + # params = CGI::parse("query_string") + # # {"name1" => ["value1", "value2", ...], + # # "name2" => ["value1", "value2", ...], ... } + # + def CGI::parse(query) + params = {} + query.split(/[&;]/).each do |pairs| + key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) } + + next unless key + + params[key] ||= [] + params[key].push(value) if value + end + + params.default=[].freeze + params + end + + # Maximum content length of post data + ##MAX_CONTENT_LENGTH = 2 * 1024 * 1024 + + # Maximum content length of multipart data + MAX_MULTIPART_LENGTH = 128 * 1024 * 1024 + + # Maximum number of request parameters when multipart + MAX_MULTIPART_COUNT = 128 + + # Mixin module that provides the following: + # + # 1. Access to the CGI environment variables as methods. See + # documentation to the CGI class for a list of these variables. The + # methods are exposed by removing the leading +HTTP_+ (if it exists) and + # downcasing the name. For example, +auth_type+ will return the + # environment variable +AUTH_TYPE+, and +accept+ will return the value + # for +HTTP_ACCEPT+. + # + # 2. Access to cookies, including the cookies attribute. + # + # 3. Access to parameters, including the params attribute, and overloading + # #[] to perform parameter value lookup by key. + # + # 4. The initialize_query method, for initializing the above + # mechanisms, handling multipart forms, and allowing the + # class to be used in "offline" mode. + # + module QueryExtension + + %w[ CONTENT_LENGTH SERVER_PORT ].each do |env| + define_method(env.sub(/^HTTP_/, '').downcase) do + (val = env_table[env]) && Integer(val) + end + end + + %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO + PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST + REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME + SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE + + HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING + HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST + HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env| + define_method(env.sub(/^HTTP_/, '').downcase) do + env_table[env] + end + end + + # Get the raw cookies as a string. + def raw_cookie + env_table["HTTP_COOKIE"] + end + + # Get the raw RFC2965 cookies as a string. + def raw_cookie2 + env_table["HTTP_COOKIE2"] + end + + # Get the cookies as a hash of cookie-name=>Cookie pairs. + attr_accessor :cookies + + # Get the parameters as a hash of name=>values pairs, where + # values is an Array. + attr_reader :params + + # Get the uploaded files as a hash of name=>values pairs + attr_reader :files + + # Set all the parameters. + def params=(hash) + @params.clear + @params.update(hash) + end + + ## + # Parses multipart form elements according to + # http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 + # + # Returns a hash of multipart form parameters with bodies of type StringIO or + # Tempfile depending on whether the multipart form element exceeds 10 KB + # + # params[name => body] + # + def read_multipart(boundary, content_length) + ## read first boundary + stdin = stdinput + first_line = "--#{boundary}#{EOL}" + content_length -= first_line.bytesize + status = stdin.read(first_line.bytesize) + raise EOFError.new("no content body") unless status + raise EOFError.new("bad content body") unless first_line == status + ## parse and set params + params = {} + @files = {} + boundary_rexp = /--#{Regexp.quote(boundary)}(#{EOL}|--)/ + boundary_size = "#{EOL}--#{boundary}#{EOL}".bytesize + boundary_end = nil + buf = '' + bufsize = 10 * 1024 + max_count = MAX_MULTIPART_COUNT + n = 0 + tempfiles = [] + while true + (n += 1) < max_count or raise StandardError.new("too many parameters.") + ## create body (StringIO or Tempfile) + body = create_body(bufsize < content_length) + tempfiles << body if defined?(Tempfile) && body.kind_of?(Tempfile) + class << body + if method_defined?(:path) + alias local_path path + else + def local_path + nil + end + end + attr_reader :original_filename, :content_type + end + ## find head and boundary + head = nil + separator = EOL * 2 + until head && matched = boundary_rexp.match(buf) + if !head && pos = buf.index(separator) + len = pos + EOL.bytesize + head = buf[0, len] + buf = buf[(pos+separator.bytesize)..-1] + else + if head && buf.size > boundary_size + len = buf.size - boundary_size + body.print(buf[0, len]) + buf[0, len] = '' + end + c = stdin.read(bufsize < content_length ? bufsize : content_length) + raise EOFError.new("bad content body") if c.nil? || c.empty? + buf << c + content_length -= c.bytesize + end + end + ## read to end of boundary + m = matched + len = m.begin(0) + s = buf[0, len] + if s =~ /(\r?\n)\z/ + s = buf[0, len - $1.bytesize] + end + body.print(s) + buf = buf[m.end(0)..-1] + boundary_end = m[1] + content_length = -1 if boundary_end == '--' + ## reset file cursor position + body.rewind + ## original filename + /Content-Disposition:.* filename=(?:"(.*?)"|([^;\r\n]*))/i.match(head) + filename = $1 || $2 || '' + filename = CGI.unescape(filename) if unescape_filename?() + body.instance_variable_set(:@original_filename, filename.taint) + ## content type + /Content-Type: (.*)/i.match(head) + (content_type = $1 || '').chomp! + body.instance_variable_set(:@content_type, content_type.taint) + ## query parameter name + /Content-Disposition:.* name=(?:"(.*?)"|([^;\r\n]*))/i.match(head) + name = $1 || $2 || '' + if body.original_filename.empty? + value=body.read.dup.force_encoding(@accept_charset) + body.unlink if defined?(Tempfile) && body.kind_of?(Tempfile) + (params[name] ||= []) << value + unless value.valid_encoding? + if @accept_charset_error_block + @accept_charset_error_block.call(name,value) + else + raise InvalidEncoding,"Accept-Charset encoding error" + end + end + class << params[name].last;self;end.class_eval do + define_method(:read){self} + define_method(:original_filename){""} + define_method(:content_type){""} + end + else + (params[name] ||= []) << body + @files[name]=body + end + ## break loop + break if content_length == -1 + end + raise EOFError, "bad boundary end of body part" unless boundary_end =~ /--/ + params.default = [] + params + rescue Exception + if tempfiles + tempfiles.each {|t| + if t.path + t.unlink + end + } + end + raise + end # read_multipart + private :read_multipart + def create_body(is_large) #:nodoc: + if is_large + require 'tempfile' + body = Tempfile.new('CGI', encoding: Encoding::ASCII_8BIT) + else + begin + require 'stringio' + body = StringIO.new("".force_encoding(Encoding::ASCII_8BIT)) + rescue LoadError + require 'tempfile' + body = Tempfile.new('CGI', encoding: Encoding::ASCII_8BIT) + end + end + body.binmode if defined? body.binmode + return body + end + def unescape_filename? #:nodoc: + user_agent = $CGI_ENV['HTTP_USER_AGENT'] + return /Mac/i.match(user_agent) && /Mozilla/i.match(user_agent) && !/MSIE/i.match(user_agent) + end + + # offline mode. read name=value pairs on standard input. + def read_from_cmdline + require "shellwords" + + string = unless ARGV.empty? + ARGV.join(' ') + else + if STDIN.tty? + STDERR.print( + %|(offline mode: enter name=value pairs on standard input)\n| + ) + end + array = readlines rescue nil + if not array.nil? + array.join(' ').gsub(/\n/n, '') + else + "" + end + end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26') + + words = Shellwords.shellwords(string) + + if words.find{|x| /=/n.match(x) } + words.join('&') + else + words.join('+') + end + end + private :read_from_cmdline + + # A wrapper class to use a StringIO object as the body and switch + # to a TempFile when the passed threshold is passed. + # Initialize the data from the query. + # + # Handles multipart forms (in particular, forms that involve file uploads). + # Reads query parameters in the @params field, and cookies into @cookies. + def initialize_query() + if ("POST" == env_table['REQUEST_METHOD']) and + %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|.match(env_table['CONTENT_TYPE']) + raise StandardError.new("too large multipart data.") if env_table['CONTENT_LENGTH'].to_i > MAX_MULTIPART_LENGTH + boundary = $1.dup + @multipart = true + @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH'])) + else + @multipart = false + @params = CGI::parse( + case env_table['REQUEST_METHOD'] + when "GET", "HEAD" + if defined?(MOD_RUBY) + Apache::request.args or "" + else + env_table['QUERY_STRING'] or "" + end + when "POST" + stdinput.binmode if defined? stdinput.binmode + stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or '' + else + read_from_cmdline + end.dup.force_encoding(@accept_charset) + ) + unless Encoding.find(@accept_charset) == Encoding::ASCII_8BIT + @params.each do |key,values| + values.each do |value| + unless value.valid_encoding? + if @accept_charset_error_block + @accept_charset_error_block.call(key,value) + else + raise InvalidEncoding,"Accept-Charset encoding error" + end + end + end + end + end + end + + @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE'])) + end + private :initialize_query + + # Returns whether the form contained multipart/form-data + def multipart? + @multipart + end + + # Get the value for the parameter with a given key. + # + # If the parameter has multiple values, only the first will be + # retrieved; use #params to get the array of values. + def [](key) + params = @params[key] + return '' unless params + value = params[0] + if @multipart + if value + return value + elsif defined? StringIO + StringIO.new("".force_encoding(Encoding::ASCII_8BIT)) + else + Tempfile.new("CGI",encoding: Encoding::ASCII_8BIT) + end + else + str = if value then value.dup else "" end + str + end + end + + # Return all query parameter names as an array of String. + def keys(*args) + @params.keys(*args) + end + + # Returns true if a given query string parameter exists. + def has_key?(*args) + @params.has_key?(*args) + end + alias key? has_key? + alias include? has_key? + + end # QueryExtension + + # Exception raised when there is an invalid encoding detected + class InvalidEncoding < Exception; end + + # @@accept_charset is default accept character set. + # This default value default is "UTF-8" + # If you want to change the default accept character set + # when create a new CGI instance, set this: + # + # CGI.accept_charset = "EUC-JP" + # + @@accept_charset="UTF-8" + + # Return the accept character set for all new CGI instances. + def self.accept_charset + @@accept_charset + end + + # Set the accept character set for all new CGI instances. + def self.accept_charset=(accept_charset) + @@accept_charset=accept_charset + end + + # Return the accept character set for this CGI instance. + attr_reader :accept_charset + + # Create a new CGI instance. + # + # :call-seq: + # CGI.new(tag_maker) { block } + # CGI.new(options_hash = {}) { block } + # + # + # tag_maker:: + # This is the same as using the +options_hash+ form with the value { + # :tag_maker => tag_maker } Note that it is recommended to use the + # +options_hash+ form, since it also allows you specify the charset you + # will accept. + # options_hash:: + # A Hash that recognizes two options: + # + # :accept_charset:: + # specifies encoding of received query string. If omitted, + # @@accept_charset is used. If the encoding is not valid, a + # CGI::InvalidEncoding will be raised. + # + # Example. Suppose @@accept_charset is "UTF-8" + # + # when not specified: + # + # cgi=CGI.new # @accept_charset # => "UTF-8" + # + # when specified as "EUC-JP": + # + # cgi=CGI.new(:accept_charset => "EUC-JP") # => "EUC-JP" + # + # :tag_maker:: + # String that specifies which version of the HTML generation methods to + # use. If not specified, no HTML generation methods will be loaded. + # + # The following values are supported: + # + # "html3":: HTML 3.x + # "html4":: HTML 4.0 + # "html4Tr":: HTML 4.0 Transitional + # "html4Fr":: HTML 4.0 with Framesets + # "html5":: HTML 5 + # + # block:: + # If provided, the block is called when an invalid encoding is + # encountered. For example: + # + # encoding_errors={} + # cgi=CGI.new(:accept_charset=>"EUC-JP") do |name,value| + # encoding_errors[name] = value + # end + # + # Finally, if the CGI object is not created in a standard CGI call + # environment (that is, it can't locate REQUEST_METHOD in its environment), + # then it will run in "offline" mode. In this mode, it reads its parameters + # from the command line or (failing that) from standard input. Otherwise, + # cookies and other parameters are parsed automatically from the standard + # CGI locations, which varies according to the REQUEST_METHOD. + def initialize(options = {}, &block) # :yields: name, value + @accept_charset_error_block = block_given? ? block : nil + @options={:accept_charset=>@@accept_charset} + case options + when Hash + @options.merge!(options) + when String + @options[:tag_maker]=options + end + @accept_charset=@options[:accept_charset] + if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE") + Apache.request.setup_cgi_env + end + + extend QueryExtension + @multipart = false + + initialize_query() # set @params, @cookies + @output_cookies = nil + @output_hidden = nil + + case @options[:tag_maker] + when "html3" + require 'cgi/html' + extend Html3 + extend HtmlExtension + when "html4" + require 'cgi/html' + extend Html4 + extend HtmlExtension + when "html4Tr" + require 'cgi/html' + extend Html4Tr + extend HtmlExtension + when "html4Fr" + require 'cgi/html' + extend Html4Tr + extend Html4Fr + extend HtmlExtension + when "html5" + require 'cgi/html' + extend Html5 + extend HtmlExtension + end + end + +end # class CGI + + diff --git a/ruby/lib/ruby/2.1.0/cgi/html.rb b/ruby/lib/ruby/2.1.0/cgi/html.rb new file mode 100644 index 0000000..4c4f698 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/cgi/html.rb @@ -0,0 +1,1034 @@ +class CGI + # Base module for HTML-generation mixins. + # + # Provides methods for code generation for tags following + # the various DTD element types. + module TagMaker # :nodoc: + + # Generate code for an element with required start and end tags. + # + # - - + def nn_element(element, attributes = {}) + s = nOE_element(element, attributes) + if block_given? + s << yield.to_s + end + s << "" + end + + def nn_element_def(attributes = {}, &block) + nn_element(__callee__, attributes, &block) + end + + # Generate code for an empty element. + # + # - O EMPTY + def nOE_element(element, attributes = {}) + attributes={attributes=>nil} if attributes.kind_of?(String) + s = "<#{element.upcase}" + attributes.each do|name, value| + next unless value + s << " " + s << CGI::escapeHTML(name.to_s) + if value != true + s << '="' + s << CGI::escapeHTML(value.to_s) + s << '"' + end + end + s << ">" + end + + def nOE_element_def(attributes = {}, &block) + nOE_element(__callee__, attributes, &block) + end + + + # Generate code for an element for which the end (and possibly the + # start) tag is optional. + # + # O O or - O + def nO_element(element, attributes = {}) + s = nOE_element(element, attributes) + if block_given? + s << yield.to_s + s << "" + end + s + end + + def nO_element_def(attributes = {}, &block) + nO_element(__callee__, attributes, &block) + end + + end # TagMaker + + + # Mixin module providing HTML generation methods. + # + # For example, + # cgi.a("http://www.example.com") { "Example" } + # # => "Example" + # + # Modules Html3, Html4, etc., contain more basic HTML-generation methods + # (+#title+, +#h1+, etc.). + # + # See class CGI for a detailed example. + # + module HtmlExtension + + + # Generate an Anchor element as a string. + # + # +href+ can either be a string, giving the URL + # for the HREF attribute, or it can be a hash of + # the element's attributes. + # + # The body of the element is the string returned by the no-argument + # block passed in. + # + # a("http://www.example.com") { "Example" } + # # => "Example" + # + # a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" } + # # => "Example" + # + def a(href = "") # :yield: + attributes = if href.kind_of?(String) + { "HREF" => href } + else + href + end + super(attributes) + end + + # Generate a Document Base URI element as a String. + # + # +href+ can either by a string, giving the base URL for the HREF + # attribute, or it can be a has of the element's attributes. + # + # The passed-in no-argument block is ignored. + # + # base("http://www.example.com/cgi") + # # => "" + def base(href = "") # :yield: + attributes = if href.kind_of?(String) + { "HREF" => href } + else + href + end + super(attributes) + end + + # Generate a BlockQuote element as a string. + # + # +cite+ can either be a string, give the URI for the source of + # the quoted text, or a hash, giving all attributes of the element, + # or it can be omitted, in which case the element has no attributes. + # + # The body is provided by the passed-in no-argument block + # + # blockquote("http://www.example.com/quotes/foo.html") { "Foo!" } + # #=> "
Foo!
+ def blockquote(cite = {}) # :yield: + attributes = if cite.kind_of?(String) + { "CITE" => cite } + else + cite + end + super(attributes) + end + + + # Generate a Table Caption element as a string. + # + # +align+ can be a string, giving the alignment of the caption + # (one of top, bottom, left, or right). It can be a hash of + # all the attributes of the element. Or it can be omitted. + # + # The body of the element is provided by the passed-in no-argument block. + # + # caption("left") { "Capital Cities" } + # # => Capital Cities + def caption(align = {}) # :yield: + attributes = if align.kind_of?(String) + { "ALIGN" => align } + else + align + end + super(attributes) + end + + + # Generate a Checkbox Input element as a string. + # + # The attributes of the element can be specified as three arguments, + # +name+, +value+, and +checked+. +checked+ is a boolean value; + # if true, the CHECKED attribute will be included in the element. + # + # Alternatively, the attributes can be specified as a hash. + # + # checkbox("name") + # # = checkbox("NAME" => "name") + # + # checkbox("name", "value") + # # = checkbox("NAME" => "name", "VALUE" => "value") + # + # checkbox("name", "value", true) + # # = checkbox("NAME" => "name", "VALUE" => "value", "CHECKED" => true) + def checkbox(name = "", value = nil, checked = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "checkbox", "NAME" => name, + "VALUE" => value, "CHECKED" => checked } + else + name["TYPE"] = "checkbox" + name + end + input(attributes) + end + + # Generate a sequence of checkbox elements, as a String. + # + # The checkboxes will all have the same +name+ attribute. + # Each checkbox is followed by a label. + # There will be one checkbox for each value. Each value + # can be specified as a String, which will be used both + # as the value of the VALUE attribute and as the label + # for that checkbox. A single-element array has the + # same effect. + # + # Each value can also be specified as a three-element array. + # The first element is the VALUE attribute; the second is the + # label; and the third is a boolean specifying whether this + # checkbox is CHECKED. + # + # Each value can also be specified as a two-element + # array, by omitting either the value element (defaults + # to the same as the label), or the boolean checked element + # (defaults to false). + # + # checkbox_group("name", "foo", "bar", "baz") + # # foo + # # bar + # # baz + # + # checkbox_group("name", ["foo"], ["bar", true], "baz") + # # foo + # # bar + # # baz + # + # checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz") + # # Foo + # # Bar + # # Baz + # + # checkbox_group("NAME" => "name", + # "VALUES" => ["foo", "bar", "baz"]) + # + # checkbox_group("NAME" => "name", + # "VALUES" => [["foo"], ["bar", true], "baz"]) + # + # checkbox_group("NAME" => "name", + # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"]) + def checkbox_group(name = "", *values) + if name.kind_of?(Hash) + values = name["VALUES"] + name = name["NAME"] + end + values.collect{|value| + if value.kind_of?(String) + checkbox(name, value) + value + else + if value[-1] == true || value[-1] == false + checkbox(name, value[0], value[-1]) + + value[-2] + else + checkbox(name, value[0]) + + value[-1] + end + end + }.join + end + + + # Generate an File Upload Input element as a string. + # + # The attributes of the element can be specified as three arguments, + # +name+, +size+, and +maxlength+. +maxlength+ is the maximum length + # of the file's _name_, not of the file's _contents_. + # + # Alternatively, the attributes can be specified as a hash. + # + # See #multipart_form() for forms that include file uploads. + # + # file_field("name") + # # + # + # file_field("name", 40) + # # + # + # file_field("name", 40, 100) + # # + # + # file_field("NAME" => "name", "SIZE" => 40) + # # + def file_field(name = "", size = 20, maxlength = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "file", "NAME" => name, + "SIZE" => size.to_s } + else + name["TYPE"] = "file" + name + end + attributes["MAXLENGTH"] = maxlength.to_s if maxlength + input(attributes) + end + + + # Generate a Form element as a string. + # + # +method+ should be either "get" or "post", and defaults to the latter. + # +action+ defaults to the current CGI script name. +enctype+ + # defaults to "application/x-www-form-urlencoded". + # + # Alternatively, the attributes can be specified as a hash. + # + # See also #multipart_form() for forms that include file uploads. + # + # form{ "string" } + # #
string
+ # + # form("get") { "string" } + # #
string
+ # + # form("get", "url") { "string" } + # #
string
+ # + # form("METHOD" => "post", "ENCTYPE" => "enctype") { "string" } + # #
string
+ def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded") + attributes = if method.kind_of?(String) + { "METHOD" => method, "ACTION" => action, + "ENCTYPE" => enctype } + else + unless method.has_key?("METHOD") + method["METHOD"] = "post" + end + unless method.has_key?("ENCTYPE") + method["ENCTYPE"] = enctype + end + method + end + if block_given? + body = yield + else + body = "" + end + if @output_hidden + body << @output_hidden.collect{|k,v| + "" + }.join + end + super(attributes){body} + end + + # Generate a Hidden Input element as a string. + # + # The attributes of the element can be specified as two arguments, + # +name+ and +value+. + # + # Alternatively, the attributes can be specified as a hash. + # + # hidden("name") + # # + # + # hidden("name", "value") + # # + # + # hidden("NAME" => "name", "VALUE" => "reset", "ID" => "foo") + # # + def hidden(name = "", value = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "hidden", "NAME" => name, "VALUE" => value } + else + name["TYPE"] = "hidden" + name + end + input(attributes) + end + + # Generate a top-level HTML element as a string. + # + # The attributes of the element are specified as a hash. The + # pseudo-attribute "PRETTY" can be used to specify that the generated + # HTML string should be indented. "PRETTY" can also be specified as + # a string as the sole argument to this method. The pseudo-attribute + # "DOCTYPE", if given, is used as the leading DOCTYPE SGML tag; it + # should include the entire text of this tag, including angle brackets. + # + # The body of the html element is supplied as a block. + # + # html{ "string" } + # # string + # + # html("LANG" => "ja") { "string" } + # # string + # + # html("DOCTYPE" => false) { "string" } + # # string + # + # html("DOCTYPE" => '') { "string" } + # # string + # + # html("PRETTY" => " ") { "" } + # # + # # + # # + # # + # # + # + # html("PRETTY" => "\t") { "" } + # # + # # + # # + # # + # # + # + # html("PRETTY") { "" } + # # = html("PRETTY" => " ") { "" } + # + # html(if $VERBOSE then "PRETTY" end) { "HTML string" } + # + def html(attributes = {}) # :yield: + if nil == attributes + attributes = {} + elsif "PRETTY" == attributes + attributes = { "PRETTY" => true } + end + pretty = attributes.delete("PRETTY") + pretty = " " if true == pretty + buf = "" + + if attributes.has_key?("DOCTYPE") + if attributes["DOCTYPE"] + buf << attributes.delete("DOCTYPE") + else + attributes.delete("DOCTYPE") + end + else + buf << doctype + end + + buf << super(attributes) + + if pretty + CGI::pretty(buf, pretty) + else + buf + end + + end + + # Generate an Image Button Input element as a string. + # + # +src+ is the URL of the image to use for the button. +name+ + # is the input name. +alt+ is the alternative text for the image. + # + # Alternatively, the attributes can be specified as a hash. + # + # image_button("url") + # # + # + # image_button("url", "name", "string") + # # + # + # image_button("SRC" => "url", "ALT" => "string") + # # + def image_button(src = "", name = nil, alt = nil) + attributes = if src.kind_of?(String) + { "TYPE" => "image", "SRC" => src, "NAME" => name, + "ALT" => alt } + else + src["TYPE"] = "image" + src["SRC"] ||= "" + src + end + input(attributes) + end + + + # Generate an Image element as a string. + # + # +src+ is the URL of the image. +alt+ is the alternative text for + # the image. +width+ is the width of the image, and +height+ is + # its height. + # + # Alternatively, the attributes can be specified as a hash. + # + # img("src", "alt", 100, 50) + # # alt + # + # img("SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50) + # # alt + def img(src = "", alt = "", width = nil, height = nil) + attributes = if src.kind_of?(String) + { "SRC" => src, "ALT" => alt } + else + src + end + attributes["WIDTH"] = width.to_s if width + attributes["HEIGHT"] = height.to_s if height + super(attributes) + end + + + # Generate a Form element with multipart encoding as a String. + # + # Multipart encoding is used for forms that include file uploads. + # + # +action+ is the action to perform. +enctype+ is the encoding + # type, which defaults to "multipart/form-data". + # + # Alternatively, the attributes can be specified as a hash. + # + # multipart_form{ "string" } + # #
string
+ # + # multipart_form("url") { "string" } + # #
string
+ def multipart_form(action = nil, enctype = "multipart/form-data") + attributes = if action == nil + { "METHOD" => "post", "ENCTYPE" => enctype } + elsif action.kind_of?(String) + { "METHOD" => "post", "ACTION" => action, + "ENCTYPE" => enctype } + else + unless action.has_key?("METHOD") + action["METHOD"] = "post" + end + unless action.has_key?("ENCTYPE") + action["ENCTYPE"] = enctype + end + action + end + if block_given? + form(attributes){ yield } + else + form(attributes) + end + end + + + # Generate a Password Input element as a string. + # + # +name+ is the name of the input field. +value+ is its default + # value. +size+ is the size of the input field display. +maxlength+ + # is the maximum length of the inputted password. + # + # Alternatively, attributes can be specified as a hash. + # + # password_field("name") + # # + # + # password_field("name", "value") + # # + # + # password_field("password", "value", 80, 200) + # # + # + # password_field("NAME" => "name", "VALUE" => "value") + # # + def password_field(name = "", value = nil, size = 40, maxlength = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "password", "NAME" => name, + "VALUE" => value, "SIZE" => size.to_s } + else + name["TYPE"] = "password" + name + end + attributes["MAXLENGTH"] = maxlength.to_s if maxlength + input(attributes) + end + + # Generate a Select element as a string. + # + # +name+ is the name of the element. The +values+ are the options that + # can be selected from the Select menu. Each value can be a String or + # a one, two, or three-element Array. If a String or a one-element + # Array, this is both the value of that option and the text displayed for + # it. If a three-element Array, the elements are the option value, displayed + # text, and a boolean value specifying whether this option starts as selected. + # The two-element version omits either the option value (defaults to the same + # as the display text) or the boolean selected specifier (defaults to false). + # + # The attributes and options can also be specified as a hash. In this + # case, options are specified as an array of values as described above, + # with the hash key of "VALUES". + # + # popup_menu("name", "foo", "bar", "baz") + # # + # + # popup_menu("name", ["foo"], ["bar", true], "baz") + # # + # + # popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz") + # # + # + # popup_menu("NAME" => "name", "SIZE" => 2, "MULTIPLE" => true, + # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"]) + # # + def popup_menu(name = "", *values) + + if name.kind_of?(Hash) + values = name["VALUES"] + size = name["SIZE"].to_s if name["SIZE"] + multiple = name["MULTIPLE"] + name = name["NAME"] + else + size = nil + multiple = nil + end + + select({ "NAME" => name, "SIZE" => size, + "MULTIPLE" => multiple }){ + values.collect{|value| + if value.kind_of?(String) + option({ "VALUE" => value }){ value } + else + if value[value.size - 1] == true + option({ "VALUE" => value[0], "SELECTED" => true }){ + value[value.size - 2] + } + else + option({ "VALUE" => value[0] }){ + value[value.size - 1] + } + end + end + }.join + } + + end + + # Generates a radio-button Input element. + # + # +name+ is the name of the input field. +value+ is the value of + # the field if checked. +checked+ specifies whether the field + # starts off checked. + # + # Alternatively, the attributes can be specified as a hash. + # + # radio_button("name", "value") + # # + # + # radio_button("name", "value", true) + # # + # + # radio_button("NAME" => "name", "VALUE" => "value", "ID" => "foo") + # # + def radio_button(name = "", value = nil, checked = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "radio", "NAME" => name, + "VALUE" => value, "CHECKED" => checked } + else + name["TYPE"] = "radio" + name + end + input(attributes) + end + + # Generate a sequence of radio button Input elements, as a String. + # + # This works the same as #checkbox_group(). However, it is not valid + # to have more than one radiobutton in a group checked. + # + # radio_group("name", "foo", "bar", "baz") + # # foo + # # bar + # # baz + # + # radio_group("name", ["foo"], ["bar", true], "baz") + # # foo + # # bar + # # baz + # + # radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz") + # # Foo + # # Bar + # # Baz + # + # radio_group("NAME" => "name", + # "VALUES" => ["foo", "bar", "baz"]) + # + # radio_group("NAME" => "name", + # "VALUES" => [["foo"], ["bar", true], "baz"]) + # + # radio_group("NAME" => "name", + # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"]) + def radio_group(name = "", *values) + if name.kind_of?(Hash) + values = name["VALUES"] + name = name["NAME"] + end + values.collect{|value| + if value.kind_of?(String) + radio_button(name, value) + value + else + if value[-1] == true || value[-1] == false + radio_button(name, value[0], value[-1]) + + value[-2] + else + radio_button(name, value[0]) + + value[-1] + end + end + }.join + end + + # Generate a reset button Input element, as a String. + # + # This resets the values on a form to their initial values. +value+ + # is the text displayed on the button. +name+ is the name of this button. + # + # Alternatively, the attributes can be specified as a hash. + # + # reset + # # + # + # reset("reset") + # # + # + # reset("VALUE" => "reset", "ID" => "foo") + # # + def reset(value = nil, name = nil) + attributes = if (not value) or value.kind_of?(String) + { "TYPE" => "reset", "VALUE" => value, "NAME" => name } + else + value["TYPE"] = "reset" + value + end + input(attributes) + end + + alias scrolling_list popup_menu + + # Generate a submit button Input element, as a String. + # + # +value+ is the text to display on the button. +name+ is the name + # of the input. + # + # Alternatively, the attributes can be specified as a hash. + # + # submit + # # + # + # submit("ok") + # # + # + # submit("ok", "button1") + # # + # + # submit("VALUE" => "ok", "NAME" => "button1", "ID" => "foo") + # # + def submit(value = nil, name = nil) + attributes = if (not value) or value.kind_of?(String) + { "TYPE" => "submit", "VALUE" => value, "NAME" => name } + else + value["TYPE"] = "submit" + value + end + input(attributes) + end + + # Generate a text field Input element, as a String. + # + # +name+ is the name of the input field. +value+ is its initial + # value. +size+ is the size of the input area. +maxlength+ + # is the maximum length of input accepted. + # + # Alternatively, the attributes can be specified as a hash. + # + # text_field("name") + # # + # + # text_field("name", "value") + # # + # + # text_field("name", "value", 80) + # # + # + # text_field("name", "value", 80, 200) + # # + # + # text_field("NAME" => "name", "VALUE" => "value") + # # + def text_field(name = "", value = nil, size = 40, maxlength = nil) + attributes = if name.kind_of?(String) + { "TYPE" => "text", "NAME" => name, "VALUE" => value, + "SIZE" => size.to_s } + else + name["TYPE"] = "text" + name + end + attributes["MAXLENGTH"] = maxlength.to_s if maxlength + input(attributes) + end + + # Generate a TextArea element, as a String. + # + # +name+ is the name of the textarea. +cols+ is the number of + # columns and +rows+ is the number of rows in the display. + # + # Alternatively, the attributes can be specified as a hash. + # + # The body is provided by the passed-in no-argument block + # + # textarea("name") + # # = textarea("NAME" => "name", "COLS" => 70, "ROWS" => 10) + # + # textarea("name", 40, 5) + # # = textarea("NAME" => "name", "COLS" => 40, "ROWS" => 5) + def textarea(name = "", cols = 70, rows = 10) # :yield: + attributes = if name.kind_of?(String) + { "NAME" => name, "COLS" => cols.to_s, + "ROWS" => rows.to_s } + else + name + end + super(attributes) + end + + end # HtmlExtension + + + # Mixin module for HTML version 3 generation methods. + module Html3 # :nodoc: + include TagMaker + + # The DOCTYPE declaration for this version of HTML + def doctype + %|| + end + + instance_method(:nn_element_def).tap do |m| + # - - + for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG + DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV CENTER MAP + APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT TABLE TITLE + STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE + CAPTION ] + define_method(element.downcase, m) + end + end + + instance_method(:nOE_element_def).tap do |m| + # - O EMPTY + for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT + ISINDEX META ] + define_method(element.downcase, m) + end + end + + instance_method(:nO_element_def).tap do |m| + # O O or - O + for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION TR + TH TD ] + define_method(element.downcase, m) + end + end + + end # Html3 + + + # Mixin module for HTML version 4 generation methods. + module Html4 # :nodoc: + include TagMaker + + # The DOCTYPE declaration for this version of HTML + def doctype + %|| + end + + # Initialise the HTML generation methods for this version. + # - - + instance_method(:nn_element_def).tap do |m| + for element in %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD + VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT + H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP + FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT + TEXTAREA FORM A BLOCKQUOTE CAPTION ] + define_method(element.downcase, m) + end + end + + # - O EMPTY + instance_method(:nOE_element_def).tap do |m| + for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ] + define_method(element.downcase, m) + end + end + + # O O or - O + instance_method(:nO_element_def).tap do |m| + for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY + COLGROUP TR TH TD HEAD ] + define_method(element.downcase, m) + end + end + + end # Html4 + + + # Mixin module for HTML version 4 transitional generation methods. + module Html4Tr # :nodoc: + include TagMaker + + # The DOCTYPE declaration for this version of HTML + def doctype + %|| + end + + # Initialise the HTML generation methods for this version. + # - - + instance_method(:nn_element_def).tap do |m| + for element in %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN + CODE SAMP KBD VAR CITE ABBR ACRONYM FONT SUB SUP SPAN BDO + ADDRESS DIV CENTER MAP OBJECT APPLET H1 H2 H3 H4 H5 H6 PRE Q + INS DEL DL OL UL DIR MENU LABEL SELECT OPTGROUP FIELDSET + LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE SCRIPT + NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ] + define_method(element.downcase, m) + end + end + + # - O EMPTY + instance_method(:nOE_element_def).tap do |m| + for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT + COL ISINDEX META ] + define_method(element.downcase, m) + end + end + + # O O or - O + instance_method(:nO_element_def).tap do |m| + for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY + COLGROUP TR TH TD HEAD ] + define_method(element.downcase, m) + end + end + + end # Html4Tr + + + # Mixin module for generating HTML version 4 with framesets. + module Html4Fr # :nodoc: + include TagMaker + + # The DOCTYPE declaration for this version of HTML + def doctype + %|| + end + + # Initialise the HTML generation methods for this version. + # - - + instance_method(:nn_element_def).tap do |m| + for element in %w[ FRAMESET ] + define_method(element.downcase, m) + end + end + + # - O EMPTY + instance_method(:nOE_element_def).tap do |m| + for element in %w[ FRAME ] + define_method(element.downcase, m) + end + end + + end # Html4Fr + + + # Mixin module for HTML version 5 generation methods. + module Html5 # :nodoc: + include TagMaker + + # The DOCTYPE declaration for this version of HTML + def doctype + %|| + end + + # Initialise the HTML generation methods for this version. + # - - + instance_method(:nn_element_def).tap do |m| + for element in %w[ SECTION NAV ARTICLE ASIDE HGROUP HEADER + FOOTER FIGURE FIGCAPTION S TIME U MARK RUBY BDI IFRAME + VIDEO AUDIO CANVAS DATALIST OUTPUT PROGRESS METER DETAILS + SUMMARY MENU DIALOG I B SMALL EM STRONG DFN CODE SAMP KBD + VAR CITE ABBR SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT + H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT + FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT + TEXTAREA FORM A BLOCKQUOTE CAPTION ] + define_method(element.downcase, m) + end + end + + # - O EMPTY + instance_method(:nOE_element_def).tap do |m| + for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META + COMMAND EMBED KEYGEN SOURCE TRACK WBR ] + define_method(element.downcase, m) + end + end + + # O O or - O + instance_method(:nO_element_def).tap do |m| + for element in %w[ HTML HEAD BODY P DT DD LI OPTION THEAD TFOOT TBODY + OPTGROUP COLGROUP RT RP TR TH TD ] + define_method(element.downcase, m) + end + end + + end # Html5 + + class HTML3 + include Html3 + include HtmlExtension + end + + class HTML4 + include Html4 + include HtmlExtension + end + + class HTML4Tr + include Html4Tr + include HtmlExtension + end + + class HTML4Fr + include Html4Tr + include Html4Fr + include HtmlExtension + end + + class HTML5 + include Html5 + include HtmlExtension + end + +end diff --git a/ruby/lib/ruby/2.1.0/cgi/session.rb b/ruby/lib/ruby/2.1.0/cgi/session.rb new file mode 100644 index 0000000..606d5bc --- /dev/null +++ b/ruby/lib/ruby/2.1.0/cgi/session.rb @@ -0,0 +1,531 @@ +# +# cgi/session.rb - session support for cgi scripts +# +# Copyright (C) 2001 Yukihiro "Matz" Matsumoto +# Copyright (C) 2000 Network Applied Communication Laboratory, Inc. +# Copyright (C) 2000 Information-technology Promotion Agency, Japan +# +# Author: Yukihiro "Matz" Matsumoto +# +# Documentation: William Webber (william@williamwebber.com) + +require 'cgi' +require 'tmpdir' + +class CGI + + # == Overview + # + # This file provides the CGI::Session class, which provides session + # support for CGI scripts. A session is a sequence of HTTP requests + # and responses linked together and associated with a single client. + # Information associated with the session is stored + # on the server between requests. A session id is passed between client + # and server with every request and response, transparently + # to the user. This adds state information to the otherwise stateless + # HTTP request/response protocol. + # + # == Lifecycle + # + # A CGI::Session instance is created from a CGI object. By default, + # this CGI::Session instance will start a new session if none currently + # exists, or continue the current session for this client if one does + # exist. The +new_session+ option can be used to either always or + # never create a new session. See #new() for more details. + # + # #delete() deletes a session from session storage. It + # does not however remove the session id from the client. If the client + # makes another request with the same id, the effect will be to start + # a new session with the old session's id. + # + # == Setting and retrieving session data. + # + # The Session class associates data with a session as key-value pairs. + # This data can be set and retrieved by indexing the Session instance + # using '[]', much the same as hashes (although other hash methods + # are not supported). + # + # When session processing has been completed for a request, the + # session should be closed using the close() method. This will + # store the session's state to persistent storage. If you want + # to store the session's state to persistent storage without + # finishing session processing for this request, call the update() + # method. + # + # == Storing session state + # + # The caller can specify what form of storage to use for the session's + # data with the +database_manager+ option to CGI::Session::new. The + # following storage classes are provided as part of the standard library: + # + # CGI::Session::FileStore:: stores data as plain text in a flat file. Only + # works with String data. This is the default + # storage type. + # CGI::Session::MemoryStore:: stores data in an in-memory hash. The data + # only persists for as long as the current Ruby + # interpreter instance does. + # CGI::Session::PStore:: stores data in Marshalled format. Provided by + # cgi/session/pstore.rb. Supports data of any type, + # and provides file-locking and transaction support. + # + # Custom storage types can also be created by defining a class with + # the following methods: + # + # new(session, options) + # restore # returns hash of session data. + # update + # close + # delete + # + # Changing storage type mid-session does not work. Note in particular + # that by default the FileStore and PStore session data files have the + # same name. If your application switches from one to the other without + # making sure that filenames will be different + # and clients still have old sessions lying around in cookies, then + # things will break nastily! + # + # == Maintaining the session id. + # + # Most session state is maintained on the server. However, a session + # id must be passed backwards and forwards between client and server + # to maintain a reference to this session state. + # + # The simplest way to do this is via cookies. The CGI::Session class + # provides transparent support for session id communication via cookies + # if the client has cookies enabled. + # + # If the client has cookies disabled, the session id must be included + # as a parameter of all requests sent by the client to the server. The + # CGI::Session class in conjunction with the CGI class will transparently + # add the session id as a hidden input field to all forms generated + # using the CGI#form() HTML generation method. No built-in support is + # provided for other mechanisms, such as URL re-writing. The caller is + # responsible for extracting the session id from the session_id + # attribute and manually encoding it in URLs and adding it as a hidden + # input to HTML forms created by other mechanisms. Also, session expiry + # is not automatically handled. + # + # == Examples of use + # + # === Setting the user's name + # + # require 'cgi' + # require 'cgi/session' + # require 'cgi/session/pstore' # provides CGI::Session::PStore + # + # cgi = CGI.new("html4") + # + # session = CGI::Session.new(cgi, + # 'database_manager' => CGI::Session::PStore, # use PStore + # 'session_key' => '_rb_sess_id', # custom session key + # 'session_expires' => Time.now + 30 * 60, # 30 minute timeout + # 'prefix' => 'pstore_sid_') # PStore option + # if cgi.has_key?('user_name') and cgi['user_name'] != '' + # # coerce to String: cgi[] returns the + # # string-like CGI::QueryExtension::Value + # session['user_name'] = cgi['user_name'].to_s + # elsif !session['user_name'] + # session['user_name'] = "guest" + # end + # session.close + # + # === Creating a new session safely + # + # require 'cgi' + # require 'cgi/session' + # + # cgi = CGI.new("html4") + # + # # We make sure to delete an old session if one exists, + # # not just to free resources, but to prevent the session + # # from being maliciously hijacked later on. + # begin + # session = CGI::Session.new(cgi, 'new_session' => false) + # session.delete + # rescue ArgumentError # if no old session + # end + # session = CGI::Session.new(cgi, 'new_session' => true) + # session.close + # + class Session + + class NoSession < RuntimeError #:nodoc: + end + + # The id of this session. + attr_reader :session_id, :new_session + + def Session::callback(dbman) #:nodoc: + Proc.new{ + dbman[0].close unless dbman.empty? + } + end + + # Create a new session id. + # + # The session id is an MD5 hash based upon the time, + # a random number, and a constant string. This routine + # is used internally for automatically generated + # session ids. + def create_new_id + require 'securerandom' + begin + session_id = SecureRandom.hex(16) + rescue NotImplementedError + require 'digest/md5' + md5 = Digest::MD5::new + now = Time::now + md5.update(now.to_s) + md5.update(String(now.usec)) + md5.update(String(rand(0))) + md5.update(String($$)) + md5.update('foobar') + session_id = md5.hexdigest + end + session_id + end + private :create_new_id + + # Create a new CGI::Session object for +request+. + # + # +request+ is an instance of the +CGI+ class (see cgi.rb). + # +option+ is a hash of options for initialising this + # CGI::Session instance. The following options are + # recognised: + # + # session_key:: the parameter name used for the session id. + # Defaults to '_session_id'. + # session_id:: the session id to use. If not provided, then + # it is retrieved from the +session_key+ parameter + # of the request, or automatically generated for + # a new session. + # new_session:: if true, force creation of a new session. If not set, + # a new session is only created if none currently + # exists. If false, a new session is never created, + # and if none currently exists and the +session_id+ + # option is not set, an ArgumentError is raised. + # database_manager:: the name of the class providing storage facilities + # for session state persistence. Built-in support + # is provided for +FileStore+ (the default), + # +MemoryStore+, and +PStore+ (from + # cgi/session/pstore.rb). See the documentation for + # these classes for more details. + # + # The following options are also recognised, but only apply if the + # session id is stored in a cookie. + # + # session_expires:: the time the current session expires, as a + # +Time+ object. If not set, the session will terminate + # when the user's browser is closed. + # session_domain:: the hostname domain for which this session is valid. + # If not set, defaults to the hostname of the server. + # session_secure:: if +true+, this session will only work over HTTPS. + # session_path:: the path for which this session applies. Defaults + # to the directory of the CGI script. + # + # +option+ is also passed on to the session storage class initializer; see + # the documentation for each session storage class for the options + # they support. + # + # The retrieved or created session is automatically added to +request+ + # as a cookie, and also to its +output_hidden+ table, which is used + # to add hidden input elements to forms. + # + # *WARNING* the +output_hidden+ + # fields are surrounded by a
tag in HTML 4 generation, which + # is _not_ invisible on many browsers; you may wish to disable the + # use of fieldsets with code similar to the following + # (see http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/37805) + # + # cgi = CGI.new("html4") + # class << cgi + # undef_method :fieldset + # end + # + def initialize(request, option={}) + @new_session = false + session_key = option['session_key'] || '_session_id' + session_id = option['session_id'] + unless session_id + if option['new_session'] + session_id = create_new_id + @new_session = true + end + end + unless session_id + if request.key?(session_key) + session_id = request[session_key] + session_id = session_id.read if session_id.respond_to?(:read) + end + unless session_id + session_id, = request.cookies[session_key] + end + unless session_id + unless option.fetch('new_session', true) + raise ArgumentError, "session_key `%s' should be supplied"%session_key + end + session_id = create_new_id + @new_session = true + end + end + @session_id = session_id + dbman = option['database_manager'] || FileStore + begin + @dbman = dbman::new(self, option) + rescue NoSession + unless option.fetch('new_session', true) + raise ArgumentError, "invalid session_id `%s'"%session_id + end + session_id = @session_id = create_new_id unless session_id + @new_session=true + retry + end + request.instance_eval do + @output_hidden = {session_key => session_id} unless option['no_hidden'] + @output_cookies = [ + Cookie::new("name" => session_key, + "value" => session_id, + "expires" => option['session_expires'], + "domain" => option['session_domain'], + "secure" => option['session_secure'], + "path" => + if option['session_path'] + option['session_path'] + elsif ENV["SCRIPT_NAME"] + File::dirname(ENV["SCRIPT_NAME"]) + else + "" + end) + ] unless option['no_cookies'] + end + @dbprot = [@dbman] + ObjectSpace::define_finalizer(self, Session::callback(@dbprot)) + end + + # Retrieve the session data for key +key+. + def [](key) + @data ||= @dbman.restore + @data[key] + end + + # Set the session data for key +key+. + def []=(key, val) + @write_lock ||= true + @data ||= @dbman.restore + @data[key] = val + end + + # Store session data on the server. For some session storage types, + # this is a no-op. + def update + @dbman.update + end + + # Store session data on the server and close the session storage. + # For some session storage types, this is a no-op. + def close + @dbman.close + @dbprot.clear + end + + # Delete the session from storage. Also closes the storage. + # + # Note that the session's data is _not_ automatically deleted + # upon the session expiring. + def delete + @dbman.delete + @dbprot.clear + end + + # File-based session storage class. + # + # Implements session storage as a flat file of 'key=value' values. + # This storage type only works directly with String values; the + # user is responsible for converting other types to Strings when + # storing and from Strings when retrieving. + class FileStore + # Create a new FileStore instance. + # + # This constructor is used internally by CGI::Session. The + # user does not generally need to call it directly. + # + # +session+ is the session for which this instance is being + # created. The session id must only contain alphanumeric + # characters; automatically generated session ids observe + # this requirement. + # + # +option+ is a hash of options for the initializer. The + # following options are recognised: + # + # tmpdir:: the directory to use for storing the FileStore + # file. Defaults to Dir::tmpdir (generally "/tmp" + # on Unix systems). + # prefix:: the prefix to add to the session id when generating + # the filename for this session's FileStore file. + # Defaults to "cgi_sid_". + # suffix:: the prefix to add to the session id when generating + # the filename for this session's FileStore file. + # Defaults to the empty string. + # + # This session's FileStore file will be created if it does + # not exist, or opened if it does. + def initialize(session, option={}) + dir = option['tmpdir'] || Dir::tmpdir + prefix = option['prefix'] || 'cgi_sid_' + suffix = option['suffix'] || '' + id = session.session_id + require 'digest/md5' + md5 = Digest::MD5.hexdigest(id)[0,16] + @path = dir+"/"+prefix+md5+suffix + if File::exist? @path + @hash = nil + else + unless session.new_session + raise CGI::Session::NoSession, "uninitialized session" + end + @hash = {} + end + end + + # Restore session state from the session's FileStore file. + # + # Returns the session state as a hash. + def restore + unless @hash + @hash = {} + begin + lockf = File.open(@path+".lock", "r") + lockf.flock File::LOCK_SH + f = File.open(@path, 'r') + for line in f + line.chomp! + k, v = line.split('=',2) + @hash[CGI::unescape(k)] = Marshal.restore(CGI::unescape(v)) + end + ensure + f.close unless f.nil? + lockf.close if lockf + end + end + @hash + end + + # Save session state to the session's FileStore file. + def update + return unless @hash + begin + lockf = File.open(@path+".lock", File::CREAT|File::RDWR, 0600) + lockf.flock File::LOCK_EX + f = File.open(@path+".new", File::CREAT|File::TRUNC|File::WRONLY, 0600) + for k,v in @hash + f.printf "%s=%s\n", CGI::escape(k), CGI::escape(String(Marshal.dump(v))) + end + f.close + File.rename @path+".new", @path + ensure + f.close if f and !f.closed? + lockf.close if lockf + end + end + + # Update and close the session's FileStore file. + def close + update + end + + # Close and delete the session's FileStore file. + def delete + File::unlink @path+".lock" rescue nil + File::unlink @path+".new" rescue nil + File::unlink @path rescue nil + end + end + + # In-memory session storage class. + # + # Implements session storage as a global in-memory hash. Session + # data will only persist for as long as the Ruby interpreter + # instance does. + class MemoryStore + GLOBAL_HASH_TABLE = {} #:nodoc: + + # Create a new MemoryStore instance. + # + # +session+ is the session this instance is associated with. + # +option+ is a list of initialisation options. None are + # currently recognised. + def initialize(session, option=nil) + @session_id = session.session_id + unless GLOBAL_HASH_TABLE.key?(@session_id) + unless session.new_session + raise CGI::Session::NoSession, "uninitialized session" + end + GLOBAL_HASH_TABLE[@session_id] = {} + end + end + + # Restore session state. + # + # Returns session data as a hash. + def restore + GLOBAL_HASH_TABLE[@session_id] + end + + # Update session state. + # + # A no-op. + def update + # don't need to update; hash is shared + end + + # Close session storage. + # + # A no-op. + def close + # don't need to close + end + + # Delete the session state. + def delete + GLOBAL_HASH_TABLE.delete(@session_id) + end + end + + # Dummy session storage class. + # + # Implements session storage place holder. No actual storage + # will be done. + class NullStore + # Create a new NullStore instance. + # + # +session+ is the session this instance is associated with. + # +option+ is a list of initialisation options. None are + # currently recognised. + def initialize(session, option=nil) + end + + # Restore (empty) session state. + def restore + {} + end + + # Update session state. + # + # A no-op. + def update + end + + # Close session storage. + # + # A no-op. + def close + end + + # Delete the session state. + # + # A no-op. + def delete + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/cgi/session/pstore.rb b/ruby/lib/ruby/2.1.0/cgi/session/pstore.rb new file mode 100644 index 0000000..a63d7d3 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/cgi/session/pstore.rb @@ -0,0 +1,111 @@ +# +# cgi/session/pstore.rb - persistent storage of marshalled session data +# +# Documentation: William Webber (william@williamwebber.com) +# +# == Overview +# +# This file provides the CGI::Session::PStore class, which builds +# persistent of session data on top of the pstore library. See +# cgi/session.rb for more details on session storage managers. + +require 'cgi/session' +require 'pstore' + +class CGI + class Session + # PStore-based session storage class. + # + # This builds upon the top-level PStore class provided by the + # library file pstore.rb. Session data is marshalled and stored + # in a file. File locking and transaction services are provided. + class PStore + # Create a new CGI::Session::PStore instance + # + # This constructor is used internally by CGI::Session. The + # user does not generally need to call it directly. + # + # +session+ is the session for which this instance is being + # created. The session id must only contain alphanumeric + # characters; automatically generated session ids observe + # this requirement. + # + # +option+ is a hash of options for the initializer. The + # following options are recognised: + # + # tmpdir:: the directory to use for storing the PStore + # file. Defaults to Dir::tmpdir (generally "/tmp" + # on Unix systems). + # prefix:: the prefix to add to the session id when generating + # the filename for this session's PStore file. + # Defaults to the empty string. + # + # This session's PStore file will be created if it does + # not exist, or opened if it does. + def initialize(session, option={}) + dir = option['tmpdir'] || Dir::tmpdir + prefix = option['prefix'] || '' + id = session.session_id + require 'digest/md5' + md5 = Digest::MD5.hexdigest(id)[0,16] + path = dir+"/"+prefix+md5 + path.untaint + if File::exist?(path) + @hash = nil + else + unless session.new_session + raise CGI::Session::NoSession, "uninitialized session" + end + @hash = {} + end + @p = ::PStore.new(path) + @p.transaction do |p| + File.chmod(0600, p.path) + end + end + + # Restore session state from the session's PStore file. + # + # Returns the session state as a hash. + def restore + unless @hash + @p.transaction do + @hash = @p['hash'] || {} + end + end + @hash + end + + # Save session state to the session's PStore file. + def update + @p.transaction do + @p['hash'] = @hash + end + end + + # Update and close the session's PStore file. + def close + update + end + + # Close and delete the session's PStore file. + def delete + path = @p.path + File::unlink path + end + + end + end +end + +if $0 == __FILE__ + # :enddoc: + STDIN.reopen("/dev/null") + cgi = CGI.new + session = CGI::Session.new(cgi, 'database_manager' => CGI::Session::PStore) + session['key'] = {'k' => 'v'} + puts session['key'].class + fail unless Hash === session['key'] + puts session['key'].inspect + fail unless session['key'].inspect == '{"k"=>"v"}' +end diff --git a/ruby/lib/ruby/2.1.0/cgi/util.rb b/ruby/lib/ruby/2.1.0/cgi/util.rb new file mode 100644 index 0000000..199e17b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/cgi/util.rb @@ -0,0 +1,202 @@ +class CGI; module Util; end; extend Util; end +module CGI::Util + @@accept_charset="UTF-8" unless defined?(@@accept_charset) + # URL-encode a string. + # url_encoded_string = CGI::escape("'Stop!' said Fred") + # # => "%27Stop%21%27+said+Fred" + def escape(string) + encoding = string.encoding + string.b.gsub(/([^ a-zA-Z0-9_.-]+)/) do |m| + '%' + m.unpack('H2' * m.bytesize).join('%').upcase + end.tr(' ', '+').force_encoding(encoding) + end + + # URL-decode a string with encoding(optional). + # string = CGI::unescape("%27Stop%21%27+said+Fred") + # # => "'Stop!' said Fred" + def unescape(string,encoding=@@accept_charset) + str=string.tr('+', ' ').b.gsub(/((?:%[0-9a-fA-F]{2})+)/) do |m| + [m.delete('%')].pack('H*') + end.force_encoding(encoding) + str.valid_encoding? ? str : str.force_encoding(string.encoding) + end + + # The set of special characters and their escaped values + TABLE_FOR_ESCAPE_HTML__ = { + "'" => ''', + '&' => '&', + '"' => '"', + '<' => '<', + '>' => '>', + } + + # Escape special characters in HTML, namely &\"<> + # CGI::escapeHTML('Usage: foo "bar" ') + # # => "Usage: foo "bar" <baz>" + def escapeHTML(string) + string.gsub(/['&\"<>]/, TABLE_FOR_ESCAPE_HTML__) + end + + # Unescape a string that has been HTML-escaped + # CGI::unescapeHTML("Usage: foo "bar" <baz>") + # # => "Usage: foo \"bar\" " + def unescapeHTML(string) + return string unless string.include? '&' + enc = string.encoding + if enc != Encoding::UTF_8 && [Encoding::UTF_16BE, Encoding::UTF_16LE, Encoding::UTF_32BE, Encoding::UTF_32LE].include?(enc) + return string.gsub(Regexp.new('&(apos|amp|quot|gt|lt|#[0-9]+|#x[0-9A-Fa-f]+);'.encode(enc))) do + case $1.encode(Encoding::US_ASCII) + when 'apos' then "'".encode(enc) + when 'amp' then '&'.encode(enc) + when 'quot' then '"'.encode(enc) + when 'gt' then '>'.encode(enc) + when 'lt' then '<'.encode(enc) + when /\A#0*(\d+)\z/ then $1.to_i.chr(enc) + when /\A#x([0-9a-f]+)\z/i then $1.hex.chr(enc) + end + end + end + asciicompat = Encoding.compatible?(string, "a") + string.gsub(/&(apos|amp|quot|gt|lt|\#[0-9]+|\#[xX][0-9A-Fa-f]+);/) do + match = $1.dup + case match + when 'apos' then "'" + when 'amp' then '&' + when 'quot' then '"' + when 'gt' then '>' + when 'lt' then '<' + when /\A#0*(\d+)\z/ + n = $1.to_i + if enc == Encoding::UTF_8 or + enc == Encoding::ISO_8859_1 && n < 256 or + asciicompat && n < 128 + n.chr(enc) + else + "&##{$1};" + end + when /\A#x([0-9a-f]+)\z/i + n = $1.hex + if enc == Encoding::UTF_8 or + enc == Encoding::ISO_8859_1 && n < 256 or + asciicompat && n < 128 + n.chr(enc) + else + "&#x#{$1};" + end + else + "&#{match};" + end + end + end + + # Synonym for CGI::escapeHTML(str) + def escape_html(str) + escapeHTML(str) + end + + # Synonym for CGI::unescapeHTML(str) + def unescape_html(str) + unescapeHTML(str) + end + + # Escape only the tags of certain HTML elements in +string+. + # + # Takes an element or elements or array of elements. Each element + # is specified by the name of the element, without angle brackets. + # This matches both the start and the end tag of that element. + # The attribute list of the open tag will also be escaped (for + # instance, the double-quotes surrounding attribute values). + # + # print CGI::escapeElement('
', "A", "IMG") + # # "
<A HREF="url"></A>" + # + # print CGI::escapeElement('
', ["A", "IMG"]) + # # "
<A HREF="url"></A>" + def escapeElement(string, *elements) + elements = elements[0] if elements[0].kind_of?(Array) + unless elements.empty? + string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do + CGI::escapeHTML($&) + end + else + string + end + end + + # Undo escaping such as that done by CGI::escapeElement() + # + # print CGI::unescapeElement( + # CGI::escapeHTML('
'), "A", "IMG") + # # "<BR>" + # + # print CGI::unescapeElement( + # CGI::escapeHTML('
'), ["A", "IMG"]) + # # "<BR>" + def unescapeElement(string, *elements) + elements = elements[0] if elements[0].kind_of?(Array) + unless elements.empty? + string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/i) do + unescapeHTML($&) + end + else + string + end + end + + # Synonym for CGI::escapeElement(str) + def escape_element(str) + escapeElement(str) + end + + # Synonym for CGI::unescapeElement(str) + def unescape_element(str) + unescapeElement(str) + end + + # Abbreviated day-of-week names specified by RFC 822 + RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ] + + # Abbreviated month names specified by RFC 822 + RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ] + + # Format a +Time+ object as a String using the format specified by RFC 1123. + # + # CGI::rfc1123_date(Time.now) + # # Sat, 01 Jan 2000 00:00:00 GMT + def rfc1123_date(time) + t = time.clone.gmtime + return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", + RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year, + t.hour, t.min, t.sec) + end + + # Prettify (indent) an HTML string. + # + # +string+ is the HTML string to indent. +shift+ is the indentation + # unit to use; it defaults to two spaces. + # + # print CGI::pretty("") + # # + # # + # # + # # + # + # print CGI::pretty("", "\t") + # # + # # + # # + # # + # + def pretty(string, shift = " ") + lines = string.gsub(/(?!\A)<.*?>/m, "\n\\0").gsub(/<.*?>(?!\n)/m, "\\0\n") + end_pos = 0 + while end_pos = lines.index(/^<\/(\w+)/, end_pos) + element = $1.dup + start_pos = lines.rindex(/^\s*<#{element}/i, end_pos) + lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/, "\n" + shift) + "__" + end + lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/, '\1') + end + + alias h escapeHTML +end diff --git a/ruby/lib/ruby/2.1.0/cmath.rb b/ruby/lib/ruby/2.1.0/cmath.rb new file mode 100644 index 0000000..337c2e0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/cmath.rb @@ -0,0 +1,400 @@ +## +# CMath is a library that provides trigonometric and transcendental +# functions for complex numbers. +# +# == Usage +# +# To start using this library, simply: +# +# require "cmath" +# +# Square root of a negative number is a complex number. +# +# CMath.sqrt(-9) #=> 0+3.0i +# + +module CMath + + include Math + + alias exp! exp + alias log! log + alias log2! log2 + alias log10! log10 + alias sqrt! sqrt + alias cbrt! cbrt + + alias sin! sin + alias cos! cos + alias tan! tan + + alias sinh! sinh + alias cosh! cosh + alias tanh! tanh + + alias asin! asin + alias acos! acos + alias atan! atan + alias atan2! atan2 + + alias asinh! asinh + alias acosh! acosh + alias atanh! atanh + + ## + # Math::E raised to the +z+ power + # + # exp(Complex(0,0)) #=> 1.0+0.0i + # exp(Complex(0,PI)) #=> -1.0+1.2246467991473532e-16i + # exp(Complex(0,PI/2.0)) #=> 6.123233995736766e-17+1.0i + def exp(z) + begin + if z.real? + exp!(z) + else + ere = exp!(z.real) + Complex(ere * cos!(z.imag), + ere * sin!(z.imag)) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # Returns the natural logarithm of Complex. If a second argument is given, + # it will be the base of logarithm. + # + # log(Complex(0,0)) #=> -Infinity+0.0i + def log(*args) + begin + z, b = args + unless b.nil? || b.kind_of?(Numeric) + raise TypeError, "Numeric Number required" + end + if z.real? and z >= 0 and (b.nil? or b >= 0) + log!(*args) + else + a = Complex(log!(z.abs), z.arg) + if b + a /= log(b) + end + a + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the base 2 logarithm of +z+ + def log2(z) + begin + if z.real? and z >= 0 + log2!(z) + else + log(z) / log!(2) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the base 10 logarithm of +z+ + def log10(z) + begin + if z.real? and z >= 0 + log10!(z) + else + log(z) / log!(10) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # Returns the non-negative square root of Complex. + # sqrt(-1) #=> 0+1.0i + # sqrt(Complex(-1,0)) #=> 0.0+1.0i + # sqrt(Complex(0,8)) #=> 2.0+2.0i + def sqrt(z) + begin + if z.real? + if z < 0 + Complex(0, sqrt!(-z)) + else + sqrt!(z) + end + else + if z.imag < 0 || + (z.imag == 0 && z.imag.to_s[0] == '-') + sqrt(z.conjugate).conjugate + else + r = z.abs + x = z.real + Complex(sqrt!((r + x) / 2.0), sqrt!((r - x) / 2.0)) + end + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the principal value of the cube root of +z+ + def cbrt(z) + z ** (1.0/3) + end + + ## + # returns the sine of +z+, where +z+ is given in radians + def sin(z) + begin + if z.real? + sin!(z) + else + Complex(sin!(z.real) * cosh!(z.imag), + cos!(z.real) * sinh!(z.imag)) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the cosine of +z+, where +z+ is given in radians + def cos(z) + begin + if z.real? + cos!(z) + else + Complex(cos!(z.real) * cosh!(z.imag), + -sin!(z.real) * sinh!(z.imag)) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the tangent of +z+, where +z+ is given in radians + def tan(z) + begin + if z.real? + tan!(z) + else + sin(z) / cos(z) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the hyperbolic sine of +z+, where +z+ is given in radians + def sinh(z) + begin + if z.real? + sinh!(z) + else + Complex(sinh!(z.real) * cos!(z.imag), + cosh!(z.real) * sin!(z.imag)) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the hyperbolic cosine of +z+, where +z+ is given in radians + def cosh(z) + begin + if z.real? + cosh!(z) + else + Complex(cosh!(z.real) * cos!(z.imag), + sinh!(z.real) * sin!(z.imag)) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the hyperbolic tangent of +z+, where +z+ is given in radians + def tanh(z) + begin + if z.real? + tanh!(z) + else + sinh(z) / cosh(z) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the arc sine of +z+ + def asin(z) + begin + if z.real? and z >= -1 and z <= 1 + asin!(z) + else + (-1.0).i * log(1.0.i * z + sqrt(1.0 - z * z)) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the arc cosine of +z+ + def acos(z) + begin + if z.real? and z >= -1 and z <= 1 + acos!(z) + else + (-1.0).i * log(z + 1.0.i * sqrt(1.0 - z * z)) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the arc tangent of +z+ + def atan(z) + begin + if z.real? + atan!(z) + else + 1.0.i * log((1.0.i + z) / (1.0.i - z)) / 2.0 + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the arc tangent of +y+ divided by +x+ using the signs of +y+ and + # +x+ to determine the quadrant + def atan2(y,x) + begin + if y.real? and x.real? + atan2!(y,x) + else + (-1.0).i * log((x + 1.0.i * y) / sqrt(x * x + y * y)) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the inverse hyperbolic sine of +z+ + def asinh(z) + begin + if z.real? + asinh!(z) + else + log(z + sqrt(1.0 + z * z)) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the inverse hyperbolic cosine of +z+ + def acosh(z) + begin + if z.real? and z >= 1 + acosh!(z) + else + log(z + sqrt(z * z - 1.0)) + end + rescue NoMethodError + handle_no_method_error + end + end + + ## + # returns the inverse hyperbolic tangent of +z+ + def atanh(z) + begin + if z.real? and z >= -1 and z <= 1 + atanh!(z) + else + log((1.0 + z) / (1.0 - z)) / 2.0 + end + rescue NoMethodError + handle_no_method_error + end + end + + module_function :exp! + module_function :exp + module_function :log! + module_function :log + module_function :log2! + module_function :log2 + module_function :log10! + module_function :log10 + module_function :sqrt! + module_function :sqrt + module_function :cbrt! + module_function :cbrt + + module_function :sin! + module_function :sin + module_function :cos! + module_function :cos + module_function :tan! + module_function :tan + + module_function :sinh! + module_function :sinh + module_function :cosh! + module_function :cosh + module_function :tanh! + module_function :tanh + + module_function :asin! + module_function :asin + module_function :acos! + module_function :acos + module_function :atan! + module_function :atan + module_function :atan2! + module_function :atan2 + + module_function :asinh! + module_function :asinh + module_function :acosh! + module_function :acosh + module_function :atanh! + module_function :atanh + + module_function :frexp + module_function :ldexp + module_function :hypot + module_function :erf + module_function :erfc + module_function :gamma + module_function :lgamma + + private + def handle_no_method_error # :nodoc: + if $!.name == :real? + raise TypeError, "Numeric Number required" + else + raise + end + end + module_function :handle_no_method_error + +end + diff --git a/ruby/lib/ruby/2.1.0/complex.rb b/ruby/lib/ruby/2.1.0/complex.rb new file mode 100644 index 0000000..9c57ecd --- /dev/null +++ b/ruby/lib/ruby/2.1.0/complex.rb @@ -0,0 +1,28 @@ +# :enddoc: + +warn('lib/complex.rb is deprecated') if $VERBOSE + +require 'cmath' + +unless defined?(Math.exp!) + Object.instance_eval{remove_const :Math} + Math = CMath +end + +def Complex.generic? (other) + other.kind_of?(Integer) || + other.kind_of?(Float) || + other.kind_of?(Rational) +end + +class Complex + + alias image imag + +end + +class Numeric + + def im() Complex(0, self) end + +end diff --git a/ruby/lib/ruby/2.1.0/csv.rb b/ruby/lib/ruby/2.1.0/csv.rb new file mode 100644 index 0000000..b8a9b97 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/csv.rb @@ -0,0 +1,2323 @@ +# encoding: US-ASCII +# = csv.rb -- CSV Reading and Writing +# +# Created by James Edward Gray II on 2005-10-31. +# Copyright 2005 James Edward Gray II. You can redistribute or modify this code +# under the terms of Ruby's license. +# +# See CSV for documentation. +# +# == Description +# +# Welcome to the new and improved CSV. +# +# This version of the CSV library began its life as FasterCSV. FasterCSV was +# intended as a replacement to Ruby's then standard CSV library. It was +# designed to address concerns users of that library had and it had three +# primary goals: +# +# 1. Be significantly faster than CSV while remaining a pure Ruby library. +# 2. Use a smaller and easier to maintain code base. (FasterCSV eventually +# grew larger, was also but considerably richer in features. The parsing +# core remains quite small.) +# 3. Improve on the CSV interface. +# +# Obviously, the last one is subjective. I did try to defer to the original +# interface whenever I didn't have a compelling reason to change it though, so +# hopefully this won't be too radically different. +# +# We must have met our goals because FasterCSV was renamed to CSV and replaced +# the original library as of Ruby 1.9. If you are migrating code from 1.8 or +# earlier, you may have to change your code to comply with the new interface. +# +# == What's Different From the Old CSV? +# +# I'm sure I'll miss something, but I'll try to mention most of the major +# differences I am aware of, to help others quickly get up to speed: +# +# === CSV Parsing +# +# * This parser is m17n aware. See CSV for full details. +# * This library has a stricter parser and will throw MalformedCSVErrors on +# problematic data. +# * This library has a less liberal idea of a line ending than CSV. What you +# set as the :row_sep is law. It can auto-detect your line endings +# though. +# * The old library returned empty lines as [nil]. This library calls +# them []. +# * This library has a much faster parser. +# +# === Interface +# +# * CSV now uses Hash-style parameters to set options. +# * CSV no longer has generate_row() or parse_row(). +# * The old CSV's Reader and Writer classes have been dropped. +# * CSV::open() is now more like Ruby's open(). +# * CSV objects now support most standard IO methods. +# * CSV now has a new() method used to wrap objects like String and IO for +# reading and writing. +# * CSV::generate() is different from the old method. +# * CSV no longer supports partial reads. It works line-by-line. +# * CSV no longer allows the instance methods to override the separators for +# performance reasons. They must be set in the constructor. +# +# If you use this library and find yourself missing any functionality I have +# trimmed, please {let me know}[mailto:james@grayproductions.net]. +# +# == Documentation +# +# See CSV for documentation. +# +# == What is CSV, really? +# +# CSV maintains a pretty strict definition of CSV taken directly from +# {the RFC}[http://www.ietf.org/rfc/rfc4180.txt]. I relax the rules in only one +# place and that is to make using this library easier. CSV will parse all valid +# CSV. +# +# What you don't want to do is feed CSV invalid data. Because of the way the +# CSV format works, it's common for a parser to need to read until the end of +# the file to be sure a field is invalid. This eats a lot of time and memory. +# +# Luckily, when working with invalid CSV, Ruby's built-in methods will almost +# always be superior in every way. For example, parsing non-quoted fields is as +# easy as: +# +# data.split(",") +# +# == Questions and/or Comments +# +# Feel free to email {James Edward Gray II}[mailto:james@grayproductions.net] +# with any questions. + +require "forwardable" +require "English" +require "date" +require "stringio" + +# +# This class provides a complete interface to CSV files and data. It offers +# tools to enable you to read and write to and from Strings or IO objects, as +# needed. +# +# == Reading +# +# === From a File +# +# ==== A Line at a Time +# +# CSV.foreach("path/to/file.csv") do |row| +# # use row here... +# end +# +# ==== All at Once +# +# arr_of_arrs = CSV.read("path/to/file.csv") +# +# === From a String +# +# ==== A Line at a Time +# +# CSV.parse("CSV,data,String") do |row| +# # use row here... +# end +# +# ==== All at Once +# +# arr_of_arrs = CSV.parse("CSV,data,String") +# +# == Writing +# +# === To a File +# +# CSV.open("path/to/file.csv", "wb") do |csv| +# csv << ["row", "of", "CSV", "data"] +# csv << ["another", "row"] +# # ... +# end +# +# === To a String +# +# csv_string = CSV.generate do |csv| +# csv << ["row", "of", "CSV", "data"] +# csv << ["another", "row"] +# # ... +# end +# +# == Convert a Single Line +# +# csv_string = ["CSV", "data"].to_csv # to CSV +# csv_array = "CSV,String".parse_csv # from CSV +# +# == Shortcut Interface +# +# CSV { |csv_out| csv_out << %w{my data here} } # to $stdout +# CSV(csv = "") { |csv_str| csv_str << %w{my data here} } # to a String +# CSV($stderr) { |csv_err| csv_err << %w{my data here} } # to $stderr +# CSV($stdin) { |csv_in| csv_in.each { |row| p row } } # from $stdin +# +# == Advanced Usage +# +# === Wrap an IO Object +# +# csv = CSV.new(io, options) +# # ... read (with gets() or each()) from and write (with <<) to csv here ... +# +# == CSV and Character Encodings (M17n or Multilingualization) +# +# This new CSV parser is m17n savvy. The parser works in the Encoding of the IO +# or String object being read from or written to. Your data is never transcoded +# (unless you ask Ruby to transcode it for you) and will literally be parsed in +# the Encoding it is in. Thus CSV will return Arrays or Rows of Strings in the +# Encoding of your data. This is accomplished by transcoding the parser itself +# into your Encoding. +# +# Some transcoding must take place, of course, to accomplish this multiencoding +# support. For example, :col_sep, :row_sep, and +# :quote_char must be transcoded to match your data. Hopefully this +# makes the entire process feel transparent, since CSV's defaults should just +# magically work for you data. However, you can set these values manually in +# the target Encoding to avoid the translation. +# +# It's also important to note that while all of CSV's core parser is now +# Encoding agnostic, some features are not. For example, the built-in +# converters will try to transcode data to UTF-8 before making conversions. +# Again, you can provide custom converters that are aware of your Encodings to +# avoid this translation. It's just too hard for me to support native +# conversions in all of Ruby's Encodings. +# +# Anyway, the practical side of this is simple: make sure IO and String objects +# passed into CSV have the proper Encoding set and everything should just work. +# CSV methods that allow you to open IO objects (CSV::foreach(), CSV::open(), +# CSV::read(), and CSV::readlines()) do allow you to specify the Encoding. +# +# One minor exception comes when generating CSV into a String with an Encoding +# that is not ASCII compatible. There's no existing data for CSV to use to +# prepare itself and thus you will probably need to manually specify the desired +# Encoding for most of those cases. It will try to guess using the fields in a +# row of output though, when using CSV::generate_line() or Array#to_csv(). +# +# I try to point out any other Encoding issues in the documentation of methods +# as they come up. +# +# This has been tested to the best of my ability with all non-"dummy" Encodings +# Ruby ships with. However, it is brave new code and may have some bugs. +# Please feel free to {report}[mailto:james@grayproductions.net] any issues you +# find with it. +# +class CSV + # The version of the installed library. + VERSION = "2.4.8".freeze + + # + # A CSV::Row is part Array and part Hash. It retains an order for the fields + # and allows duplicates just as an Array would, but also allows you to access + # fields by name just as you could if they were in a Hash. + # + # All rows returned by CSV will be constructed from this class, if header row + # processing is activated. + # + class Row + # + # Construct a new CSV::Row from +headers+ and +fields+, which are expected + # to be Arrays. If one Array is shorter than the other, it will be padded + # with +nil+ objects. + # + # The optional +header_row+ parameter can be set to +true+ to indicate, via + # CSV::Row.header_row?() and CSV::Row.field_row?(), that this is a header + # row. Otherwise, the row is assumes to be a field row. + # + # A CSV::Row object supports the following Array methods through delegation: + # + # * empty?() + # * length() + # * size() + # + def initialize(headers, fields, header_row = false) + @header_row = header_row + headers.each { |h| h.freeze if h.is_a? String } + + # handle extra headers or fields + @row = if headers.size > fields.size + headers.zip(fields) + else + fields.zip(headers).map { |pair| pair.reverse } + end + end + + # Internal data format used to compare equality. + attr_reader :row + protected :row + + ### Array Delegation ### + + extend Forwardable + def_delegators :@row, :empty?, :length, :size + + # Returns +true+ if this is a header row. + def header_row? + @header_row + end + + # Returns +true+ if this is a field row. + def field_row? + not header_row? + end + + # Returns the headers of this row. + def headers + @row.map { |pair| pair.first } + end + + # + # :call-seq: + # field( header ) + # field( header, offset ) + # field( index ) + # + # This method will return the field value by +header+ or +index+. If a field + # is not found, +nil+ is returned. + # + # When provided, +offset+ ensures that a header match occurs on or later + # than the +offset+ index. You can use this to find duplicate headers, + # without resorting to hard-coding exact indices. + # + def field(header_or_index, minimum_index = 0) + # locate the pair + finder = header_or_index.is_a?(Integer) ? :[] : :assoc + pair = @row[minimum_index..-1].send(finder, header_or_index) + + # return the field if we have a pair + pair.nil? ? nil : pair.last + end + alias_method :[], :field + + # + # :call-seq: + # fetch( header ) + # fetch( header ) { |row| ... } + # fetch( header, default ) + # + # This method will fetch the field value by +header+. It has the same + # behavior as Hash#fetch: if there is a field with the given +header+, its + # value is returned. Otherwise, if a block is given, it is yielded the + # +header+ and its result is returned; if a +default+ is given as the + # second argument, it is returned; otherwise a KeyError is raised. + # + def fetch(header, *varargs) + raise ArgumentError, "Too many arguments" if varargs.length > 1 + pair = @row.assoc(header) + if pair + pair.last + else + if block_given? + yield header + elsif varargs.empty? + raise KeyError, "key not found: #{header}" + else + varargs.first + end + end + end + + # Returns +true+ if there is a field with the given +header+. + def has_key?(header) + !!@row.assoc(header) + end + alias_method :include?, :has_key? + alias_method :key?, :has_key? + alias_method :member?, :has_key? + + # + # :call-seq: + # []=( header, value ) + # []=( header, offset, value ) + # []=( index, value ) + # + # Looks up the field by the semantics described in CSV::Row.field() and + # assigns the +value+. + # + # Assigning past the end of the row with an index will set all pairs between + # to [nil, nil]. Assigning to an unused header appends the new + # pair. + # + def []=(*args) + value = args.pop + + if args.first.is_a? Integer + if @row[args.first].nil? # extending past the end with index + @row[args.first] = [nil, value] + @row.map! { |pair| pair.nil? ? [nil, nil] : pair } + else # normal index assignment + @row[args.first][1] = value + end + else + index = index(*args) + if index.nil? # appending a field + self << [args.first, value] + else # normal header assignment + @row[index][1] = value + end + end + end + + # + # :call-seq: + # <<( field ) + # <<( header_and_field_array ) + # <<( header_and_field_hash ) + # + # If a two-element Array is provided, it is assumed to be a header and field + # and the pair is appended. A Hash works the same way with the key being + # the header and the value being the field. Anything else is assumed to be + # a lone field which is appended with a +nil+ header. + # + # This method returns the row for chaining. + # + def <<(arg) + if arg.is_a?(Array) and arg.size == 2 # appending a header and name + @row << arg + elsif arg.is_a?(Hash) # append header and name pairs + arg.each { |pair| @row << pair } + else # append field value + @row << [nil, arg] + end + + self # for chaining + end + + # + # A shortcut for appending multiple fields. Equivalent to: + # + # args.each { |arg| csv_row << arg } + # + # This method returns the row for chaining. + # + def push(*args) + args.each { |arg| self << arg } + + self # for chaining + end + + # + # :call-seq: + # delete( header ) + # delete( header, offset ) + # delete( index ) + # + # Used to remove a pair from the row by +header+ or +index+. The pair is + # located as described in CSV::Row.field(). The deleted pair is returned, + # or +nil+ if a pair could not be found. + # + def delete(header_or_index, minimum_index = 0) + if header_or_index.is_a? Integer # by index + @row.delete_at(header_or_index) + elsif i = index(header_or_index, minimum_index) # by header + @row.delete_at(i) + else + [ ] + end + end + + # + # The provided +block+ is passed a header and field for each pair in the row + # and expected to return +true+ or +false+, depending on whether the pair + # should be deleted. + # + # This method returns the row for chaining. + # + def delete_if(&block) + @row.delete_if(&block) + + self # for chaining + end + + # + # This method accepts any number of arguments which can be headers, indices, + # Ranges of either, or two-element Arrays containing a header and offset. + # Each argument will be replaced with a field lookup as described in + # CSV::Row.field(). + # + # If called with no arguments, all fields are returned. + # + def fields(*headers_and_or_indices) + if headers_and_or_indices.empty? # return all fields--no arguments + @row.map { |pair| pair.last } + else # or work like values_at() + headers_and_or_indices.inject(Array.new) do |all, h_or_i| + all + if h_or_i.is_a? Range + index_begin = h_or_i.begin.is_a?(Integer) ? h_or_i.begin : + index(h_or_i.begin) + index_end = h_or_i.end.is_a?(Integer) ? h_or_i.end : + index(h_or_i.end) + new_range = h_or_i.exclude_end? ? (index_begin...index_end) : + (index_begin..index_end) + fields.values_at(new_range) + else + [field(*Array(h_or_i))] + end + end + end + end + alias_method :values_at, :fields + + # + # :call-seq: + # index( header ) + # index( header, offset ) + # + # This method will return the index of a field with the provided +header+. + # The +offset+ can be used to locate duplicate header names, as described in + # CSV::Row.field(). + # + def index(header, minimum_index = 0) + # find the pair + index = headers[minimum_index..-1].index(header) + # return the index at the right offset, if we found one + index.nil? ? nil : index + minimum_index + end + + # Returns +true+ if +name+ is a header for this row, and +false+ otherwise. + def header?(name) + headers.include? name + end + alias_method :include?, :header? + + # + # Returns +true+ if +data+ matches a field in this row, and +false+ + # otherwise. + # + def field?(data) + fields.include? data + end + + include Enumerable + + # + # Yields each pair of the row as header and field tuples (much like + # iterating over a Hash). + # + # Support for Enumerable. + # + # This method returns the row for chaining. + # + def each(&block) + @row.each(&block) + + self # for chaining + end + + # + # Returns +true+ if this row contains the same headers and fields in the + # same order as +other+. + # + def ==(other) + return @row == other.row if other.is_a? CSV::Row + @row == other + end + + # + # Collapses the row into a simple Hash. Be warning that this discards field + # order and clobbers duplicate fields. + # + def to_hash + # flatten just one level of the internal Array + Hash[*@row.inject(Array.new) { |ary, pair| ary.push(*pair) }] + end + + # + # Returns the row as a CSV String. Headers are not used. Equivalent to: + # + # csv_row.fields.to_csv( options ) + # + def to_csv(options = Hash.new) + fields.to_csv(options) + end + alias_method :to_s, :to_csv + + # A summary of fields, by header, in an ASCII compatible String. + def inspect + str = ["#<", self.class.to_s] + each do |header, field| + str << " " << (header.is_a?(Symbol) ? header.to_s : header.inspect) << + ":" << field.inspect + end + str << ">" + begin + str.join('') + rescue # any encoding error + str.map do |s| + e = Encoding::Converter.asciicompat_encoding(s.encoding) + e ? s.encode(e) : s.force_encoding("ASCII-8BIT") + end.join('') + end + end + end + + # + # A CSV::Table is a two-dimensional data structure for representing CSV + # documents. Tables allow you to work with the data by row or column, + # manipulate the data, and even convert the results back to CSV, if needed. + # + # All tables returned by CSV will be constructed from this class, if header + # row processing is activated. + # + class Table + # + # Construct a new CSV::Table from +array_of_rows+, which are expected + # to be CSV::Row objects. All rows are assumed to have the same headers. + # + # A CSV::Table object supports the following Array methods through + # delegation: + # + # * empty?() + # * length() + # * size() + # + def initialize(array_of_rows) + @table = array_of_rows + @mode = :col_or_row + end + + # The current access mode for indexing and iteration. + attr_reader :mode + + # Internal data format used to compare equality. + attr_reader :table + protected :table + + ### Array Delegation ### + + extend Forwardable + def_delegators :@table, :empty?, :length, :size + + # + # Returns a duplicate table object, in column mode. This is handy for + # chaining in a single call without changing the table mode, but be aware + # that this method can consume a fair amount of memory for bigger data sets. + # + # This method returns the duplicate table for chaining. Don't chain + # destructive methods (like []=()) this way though, since you are working + # with a duplicate. + # + def by_col + self.class.new(@table.dup).by_col! + end + + # + # Switches the mode of this table to column mode. All calls to indexing and + # iteration methods will work with columns until the mode is changed again. + # + # This method returns the table and is safe to chain. + # + def by_col! + @mode = :col + + self + end + + # + # Returns a duplicate table object, in mixed mode. This is handy for + # chaining in a single call without changing the table mode, but be aware + # that this method can consume a fair amount of memory for bigger data sets. + # + # This method returns the duplicate table for chaining. Don't chain + # destructive methods (like []=()) this way though, since you are working + # with a duplicate. + # + def by_col_or_row + self.class.new(@table.dup).by_col_or_row! + end + + # + # Switches the mode of this table to mixed mode. All calls to indexing and + # iteration methods will use the default intelligent indexing system until + # the mode is changed again. In mixed mode an index is assumed to be a row + # reference while anything else is assumed to be column access by headers. + # + # This method returns the table and is safe to chain. + # + def by_col_or_row! + @mode = :col_or_row + + self + end + + # + # Returns a duplicate table object, in row mode. This is handy for chaining + # in a single call without changing the table mode, but be aware that this + # method can consume a fair amount of memory for bigger data sets. + # + # This method returns the duplicate table for chaining. Don't chain + # destructive methods (like []=()) this way though, since you are working + # with a duplicate. + # + def by_row + self.class.new(@table.dup).by_row! + end + + # + # Switches the mode of this table to row mode. All calls to indexing and + # iteration methods will work with rows until the mode is changed again. + # + # This method returns the table and is safe to chain. + # + def by_row! + @mode = :row + + self + end + + # + # Returns the headers for the first row of this table (assumed to match all + # other rows). An empty Array is returned for empty tables. + # + def headers + if @table.empty? + Array.new + else + @table.first.headers + end + end + + # + # In the default mixed mode, this method returns rows for index access and + # columns for header access. You can force the index association by first + # calling by_col!() or by_row!(). + # + # Columns are returned as an Array of values. Altering that Array has no + # effect on the table. + # + def [](index_or_header) + if @mode == :row or # by index + (@mode == :col_or_row and index_or_header.is_a? Integer) + @table[index_or_header] + else # by header + @table.map { |row| row[index_or_header] } + end + end + + # + # In the default mixed mode, this method assigns rows for index access and + # columns for header access. You can force the index association by first + # calling by_col!() or by_row!(). + # + # Rows may be set to an Array of values (which will inherit the table's + # headers()) or a CSV::Row. + # + # Columns may be set to a single value, which is copied to each row of the + # column, or an Array of values. Arrays of values are assigned to rows top + # to bottom in row major order. Excess values are ignored and if the Array + # does not have a value for each row the extra rows will receive a +nil+. + # + # Assigning to an existing column or row clobbers the data. Assigning to + # new columns creates them at the right end of the table. + # + def []=(index_or_header, value) + if @mode == :row or # by index + (@mode == :col_or_row and index_or_header.is_a? Integer) + if value.is_a? Array + @table[index_or_header] = Row.new(headers, value) + else + @table[index_or_header] = value + end + else # set column + if value.is_a? Array # multiple values + @table.each_with_index do |row, i| + if row.header_row? + row[index_or_header] = index_or_header + else + row[index_or_header] = value[i] + end + end + else # repeated value + @table.each do |row| + if row.header_row? + row[index_or_header] = index_or_header + else + row[index_or_header] = value + end + end + end + end + end + + # + # The mixed mode default is to treat a list of indices as row access, + # returning the rows indicated. Anything else is considered columnar + # access. For columnar access, the return set has an Array for each row + # with the values indicated by the headers in each Array. You can force + # column or row mode using by_col!() or by_row!(). + # + # You cannot mix column and row access. + # + def values_at(*indices_or_headers) + if @mode == :row or # by indices + ( @mode == :col_or_row and indices_or_headers.all? do |index| + index.is_a?(Integer) or + ( index.is_a?(Range) and + index.first.is_a?(Integer) and + index.last.is_a?(Integer) ) + end ) + @table.values_at(*indices_or_headers) + else # by headers + @table.map { |row| row.values_at(*indices_or_headers) } + end + end + + # + # Adds a new row to the bottom end of this table. You can provide an Array, + # which will be converted to a CSV::Row (inheriting the table's headers()), + # or a CSV::Row. + # + # This method returns the table for chaining. + # + def <<(row_or_array) + if row_or_array.is_a? Array # append Array + @table << Row.new(headers, row_or_array) + else # append Row + @table << row_or_array + end + + self # for chaining + end + + # + # A shortcut for appending multiple rows. Equivalent to: + # + # rows.each { |row| self << row } + # + # This method returns the table for chaining. + # + def push(*rows) + rows.each { |row| self << row } + + self # for chaining + end + + # + # Removes and returns the indicated column or row. In the default mixed + # mode indices refer to rows and everything else is assumed to be a column + # header. Use by_col!() or by_row!() to force the lookup. + # + def delete(index_or_header) + if @mode == :row or # by index + (@mode == :col_or_row and index_or_header.is_a? Integer) + @table.delete_at(index_or_header) + else # by header + @table.map { |row| row.delete(index_or_header).last } + end + end + + # + # Removes any column or row for which the block returns +true+. In the + # default mixed mode or row mode, iteration is the standard row major + # walking of rows. In column mode, iteration will +yield+ two element + # tuples containing the column name and an Array of values for that column. + # + # This method returns the table for chaining. + # + def delete_if(&block) + if @mode == :row or @mode == :col_or_row # by index + @table.delete_if(&block) + else # by header + to_delete = Array.new + headers.each_with_index do |header, i| + to_delete << header if block[[header, self[header]]] + end + to_delete.map { |header| delete(header) } + end + + self # for chaining + end + + include Enumerable + + # + # In the default mixed mode or row mode, iteration is the standard row major + # walking of rows. In column mode, iteration will +yield+ two element + # tuples containing the column name and an Array of values for that column. + # + # This method returns the table for chaining. + # + def each(&block) + if @mode == :col + headers.each { |header| block[[header, self[header]]] } + else + @table.each(&block) + end + + self # for chaining + end + + # Returns +true+ if all rows of this table ==() +other+'s rows. + def ==(other) + @table == other.table + end + + # + # Returns the table as an Array of Arrays. Headers will be the first row, + # then all of the field rows will follow. + # + def to_a + @table.inject([headers]) do |array, row| + if row.header_row? + array + else + array + [row.fields] + end + end + end + + # + # Returns the table as a complete CSV String. Headers will be listed first, + # then all of the field rows. + # + # This method assumes you want the Table.headers(), unless you explicitly + # pass :write_headers => false. + # + def to_csv(options = Hash.new) + wh = options.fetch(:write_headers, true) + @table.inject(wh ? [headers.to_csv(options)] : [ ]) do |rows, row| + if row.header_row? + rows + else + rows + [row.fields.to_csv(options)] + end + end.join('') + end + alias_method :to_s, :to_csv + + # Shows the mode and size of this table in a US-ASCII String. + def inspect + "#<#{self.class} mode:#{@mode} row_count:#{to_a.size}>".encode("US-ASCII") + end + end + + # The error thrown when the parser encounters illegal CSV formatting. + class MalformedCSVError < RuntimeError; end + + # + # A FieldInfo Struct contains details about a field's position in the data + # source it was read from. CSV will pass this Struct to some blocks that make + # decisions based on field structure. See CSV.convert_fields() for an + # example. + # + # index:: The zero-based index of the field in its row. + # line:: The line of the data source this row is from. + # header:: The header for the column, when available. + # + FieldInfo = Struct.new(:index, :line, :header) + + # A Regexp used to find and convert some common Date formats. + DateMatcher = / \A(?: (\w+,?\s+)?\w+\s+\d{1,2},?\s+\d{2,4} | + \d{4}-\d{2}-\d{2} )\z /x + # A Regexp used to find and convert some common DateTime formats. + DateTimeMatcher = + / \A(?: (\w+,?\s+)?\w+\s+\d{1,2}\s+\d{1,2}:\d{1,2}:\d{1,2},?\s+\d{2,4} | + \d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2} )\z /x + + # The encoding used by all converters. + ConverterEncoding = Encoding.find("UTF-8") + + # + # This Hash holds the built-in converters of CSV that can be accessed by name. + # You can select Converters with CSV.convert() or through the +options+ Hash + # passed to CSV::new(). + # + # :integer:: Converts any field Integer() accepts. + # :float:: Converts any field Float() accepts. + # :numeric:: A combination of :integer + # and :float. + # :date:: Converts any field Date::parse() accepts. + # :date_time:: Converts any field DateTime::parse() accepts. + # :all:: All built-in converters. A combination of + # :date_time and :numeric. + # + # All built-in converters transcode field data to UTF-8 before attempting a + # conversion. If your data cannot be transcoded to UTF-8 the conversion will + # fail and the field will remain unchanged. + # + # This Hash is intentionally left unfrozen and users should feel free to add + # values to it that can be accessed by all CSV objects. + # + # To add a combo field, the value should be an Array of names. Combo fields + # can be nested with other combo fields. + # + Converters = { integer: lambda { |f| + Integer(f.encode(ConverterEncoding)) rescue f + }, + float: lambda { |f| + Float(f.encode(ConverterEncoding)) rescue f + }, + numeric: [:integer, :float], + date: lambda { |f| + begin + e = f.encode(ConverterEncoding) + e =~ DateMatcher ? Date.parse(e) : f + rescue # encoding conversion or date parse errors + f + end + }, + date_time: lambda { |f| + begin + e = f.encode(ConverterEncoding) + e =~ DateTimeMatcher ? DateTime.parse(e) : f + rescue # encoding conversion or date parse errors + f + end + }, + all: [:date_time, :numeric] } + + # + # This Hash holds the built-in header converters of CSV that can be accessed + # by name. You can select HeaderConverters with CSV.header_convert() or + # through the +options+ Hash passed to CSV::new(). + # + # :downcase:: Calls downcase() on the header String. + # :symbol:: The header String is downcased, spaces are + # replaced with underscores, non-word characters + # are dropped, and finally to_sym() is called. + # + # All built-in header converters transcode header data to UTF-8 before + # attempting a conversion. If your data cannot be transcoded to UTF-8 the + # conversion will fail and the header will remain unchanged. + # + # This Hash is intentionally left unfrozen and users should feel free to add + # values to it that can be accessed by all CSV objects. + # + # To add a combo field, the value should be an Array of names. Combo fields + # can be nested with other combo fields. + # + HeaderConverters = { + downcase: lambda { |h| h.encode(ConverterEncoding).downcase }, + symbol: lambda { |h| + h.encode(ConverterEncoding).downcase.gsub(/\s+/, "_"). + gsub(/\W+/, "").to_sym + } + } + + # + # The options used when no overrides are given by calling code. They are: + # + # :col_sep:: "," + # :row_sep:: :auto + # :quote_char:: '"' + # :field_size_limit:: +nil+ + # :converters:: +nil+ + # :unconverted_fields:: +nil+ + # :headers:: +false+ + # :return_headers:: +false+ + # :header_converters:: +nil+ + # :skip_blanks:: +false+ + # :force_quotes:: +false+ + # :skip_lines:: +nil+ + # + DEFAULT_OPTIONS = { col_sep: ",", + row_sep: :auto, + quote_char: '"', + field_size_limit: nil, + converters: nil, + unconverted_fields: nil, + headers: false, + return_headers: false, + header_converters: nil, + skip_blanks: false, + force_quotes: false, + skip_lines: nil }.freeze + + # + # This method will return a CSV instance, just like CSV::new(), but the + # instance will be cached and returned for all future calls to this method for + # the same +data+ object (tested by Object#object_id()) with the same + # +options+. + # + # If a block is given, the instance is passed to the block and the return + # value becomes the return value of the block. + # + def self.instance(data = $stdout, options = Hash.new) + # create a _signature_ for this method call, data object and options + sig = [data.object_id] + + options.values_at(*DEFAULT_OPTIONS.keys.sort_by { |sym| sym.to_s }) + + # fetch or create the instance for this signature + @@instances ||= Hash.new + instance = (@@instances[sig] ||= new(data, options)) + + if block_given? + yield instance # run block, if given, returning result + else + instance # or return the instance + end + end + + # + # :call-seq: + # filter( options = Hash.new ) { |row| ... } + # filter( input, options = Hash.new ) { |row| ... } + # filter( input, output, options = Hash.new ) { |row| ... } + # + # This method is a convenience for building Unix-like filters for CSV data. + # Each row is yielded to the provided block which can alter it as needed. + # After the block returns, the row is appended to +output+ altered or not. + # + # The +input+ and +output+ arguments can be anything CSV::new() accepts + # (generally String or IO objects). If not given, they default to + # ARGF and $stdout. + # + # The +options+ parameter is also filtered down to CSV::new() after some + # clever key parsing. Any key beginning with :in_ or + # :input_ will have that leading identifier stripped and will only + # be used in the +options+ Hash for the +input+ object. Keys starting with + # :out_ or :output_ affect only +output+. All other keys + # are assigned to both objects. + # + # The :output_row_sep +option+ defaults to + # $INPUT_RECORD_SEPARATOR ($/). + # + def self.filter(*args) + # parse options for input, output, or both + in_options, out_options = Hash.new, {row_sep: $INPUT_RECORD_SEPARATOR} + if args.last.is_a? Hash + args.pop.each do |key, value| + case key.to_s + when /\Ain(?:put)?_(.+)\Z/ + in_options[$1.to_sym] = value + when /\Aout(?:put)?_(.+)\Z/ + out_options[$1.to_sym] = value + else + in_options[key] = value + out_options[key] = value + end + end + end + # build input and output wrappers + input = new(args.shift || ARGF, in_options) + output = new(args.shift || $stdout, out_options) + + # read, yield, write + input.each do |row| + yield row + output << row + end + end + + # + # This method is intended as the primary interface for reading CSV files. You + # pass a +path+ and any +options+ you wish to set for the read. Each row of + # file will be passed to the provided +block+ in turn. + # + # The +options+ parameter can be anything CSV::new() understands. This method + # also understands an additional :encoding parameter that you can use + # to specify the Encoding of the data in the file to be read. You must provide + # this unless your data is in Encoding::default_external(). CSV will use this + # to determine how to parse the data. You may provide a second Encoding to + # have the data transcoded as it is read. For example, + # encoding: "UTF-32BE:UTF-8" would read UTF-32BE data from the file + # but transcode it to UTF-8 before CSV parses it. + # + def self.foreach(path, options = Hash.new, &block) + return to_enum(__method__, path, options) unless block + open(path, options) do |csv| + csv.each(&block) + end + end + + # + # :call-seq: + # generate( str, options = Hash.new ) { |csv| ... } + # generate( options = Hash.new ) { |csv| ... } + # + # This method wraps a String you provide, or an empty default String, in a + # CSV object which is passed to the provided block. You can use the block to + # append CSV rows to the String and when the block exits, the final String + # will be returned. + # + # Note that a passed String *is* modified by this method. Call dup() before + # passing if you need a new String. + # + # The +options+ parameter can be anything CSV::new() understands. This method + # understands an additional :encoding parameter when not passed a + # String to set the base Encoding for the output. CSV needs this hint if you + # plan to output non-ASCII compatible data. + # + def self.generate(*args) + # add a default empty String, if none was given + if args.first.is_a? String + io = StringIO.new(args.shift) + io.seek(0, IO::SEEK_END) + args.unshift(io) + else + encoding = args[-1][:encoding] if args.last.is_a?(Hash) + str = "" + str.force_encoding(encoding) if encoding + args.unshift(str) + end + csv = new(*args) # wrap + yield csv # yield for appending + csv.string # return final String + end + + # + # This method is a shortcut for converting a single row (Array) into a CSV + # String. + # + # The +options+ parameter can be anything CSV::new() understands. This method + # understands an additional :encoding parameter to set the base + # Encoding for the output. This method will try to guess your Encoding from + # the first non-+nil+ field in +row+, if possible, but you may need to use + # this parameter as a backup plan. + # + # The :row_sep +option+ defaults to $INPUT_RECORD_SEPARATOR + # ($/) when calling this method. + # + def self.generate_line(row, options = Hash.new) + options = {row_sep: $INPUT_RECORD_SEPARATOR}.merge(options) + encoding = options.delete(:encoding) + str = "" + if encoding + str.force_encoding(encoding) + elsif field = row.find { |f| not f.nil? } + str.force_encoding(String(field).encoding) + end + (new(str, options) << row).string + end + + # + # :call-seq: + # open( filename, mode = "rb", options = Hash.new ) { |faster_csv| ... } + # open( filename, options = Hash.new ) { |faster_csv| ... } + # open( filename, mode = "rb", options = Hash.new ) + # open( filename, options = Hash.new ) + # + # This method opens an IO object, and wraps that with CSV. This is intended + # as the primary interface for writing a CSV file. + # + # You must pass a +filename+ and may optionally add a +mode+ for Ruby's + # open(). You may also pass an optional Hash containing any +options+ + # CSV::new() understands as the final argument. + # + # This method works like Ruby's open() call, in that it will pass a CSV object + # to a provided block and close it when the block terminates, or it will + # return the CSV object when no block is provided. (*Note*: This is different + # from the Ruby 1.8 CSV library which passed rows to the block. Use + # CSV::foreach() for that behavior.) + # + # You must provide a +mode+ with an embedded Encoding designator unless your + # data is in Encoding::default_external(). CSV will check the Encoding of the + # underlying IO object (set by the +mode+ you pass) to determine how to parse + # the data. You may provide a second Encoding to have the data transcoded as + # it is read just as you can with a normal call to IO::open(). For example, + # "rb:UTF-32BE:UTF-8" would read UTF-32BE data from the file but + # transcode it to UTF-8 before CSV parses it. + # + # An opened CSV object will delegate to many IO methods for convenience. You + # may call: + # + # * binmode() + # * binmode?() + # * close() + # * close_read() + # * close_write() + # * closed?() + # * eof() + # * eof?() + # * external_encoding() + # * fcntl() + # * fileno() + # * flock() + # * flush() + # * fsync() + # * internal_encoding() + # * ioctl() + # * isatty() + # * path() + # * pid() + # * pos() + # * pos=() + # * reopen() + # * seek() + # * stat() + # * sync() + # * sync=() + # * tell() + # * to_i() + # * to_io() + # * truncate() + # * tty?() + # + def self.open(*args) + # find the +options+ Hash + options = if args.last.is_a? Hash then args.pop else Hash.new end + # wrap a File opened with the remaining +args+ with no newline + # decorator + file_opts = {universal_newline: false}.merge(options) + begin + f = File.open(*args, file_opts) + rescue ArgumentError => e + raise unless /needs binmode/ =~ e.message and args.size == 1 + args << "rb" + file_opts = {encoding: Encoding.default_external}.merge(file_opts) + retry + end + csv = new(f, options) + + # handle blocks like Ruby's open(), not like the CSV library + if block_given? + begin + yield csv + ensure + csv.close + end + else + csv + end + end + + # + # :call-seq: + # parse( str, options = Hash.new ) { |row| ... } + # parse( str, options = Hash.new ) + # + # This method can be used to easily parse CSV out of a String. You may either + # provide a +block+ which will be called with each row of the String in turn, + # or just use the returned Array of Arrays (when no +block+ is given). + # + # You pass your +str+ to read from, and an optional +options+ Hash containing + # anything CSV::new() understands. + # + def self.parse(*args, &block) + csv = new(*args) + if block.nil? # slurp contents, if no block is given + begin + csv.read + ensure + csv.close + end + else # or pass each row to a provided block + csv.each(&block) + end + end + + # + # This method is a shortcut for converting a single line of a CSV String into + # an Array. Note that if +line+ contains multiple rows, anything beyond the + # first row is ignored. + # + # The +options+ parameter can be anything CSV::new() understands. + # + def self.parse_line(line, options = Hash.new) + new(line, options).shift + end + + # + # Use to slurp a CSV file into an Array of Arrays. Pass the +path+ to the + # file and any +options+ CSV::new() understands. This method also understands + # an additional :encoding parameter that you can use to specify the + # Encoding of the data in the file to be read. You must provide this unless + # your data is in Encoding::default_external(). CSV will use this to determine + # how to parse the data. You may provide a second Encoding to have the data + # transcoded as it is read. For example, + # encoding: "UTF-32BE:UTF-8" would read UTF-32BE data from the file + # but transcode it to UTF-8 before CSV parses it. + # + def self.read(path, *options) + open(path, *options) { |csv| csv.read } + end + + # Alias for CSV::read(). + def self.readlines(*args) + read(*args) + end + + # + # A shortcut for: + # + # CSV.read( path, { headers: true, + # converters: :numeric, + # header_converters: :symbol }.merge(options) ) + # + def self.table(path, options = Hash.new) + read( path, { headers: true, + converters: :numeric, + header_converters: :symbol }.merge(options) ) + end + + # + # This constructor will wrap either a String or IO object passed in +data+ for + # reading and/or writing. In addition to the CSV instance methods, several IO + # methods are delegated. (See CSV::open() for a complete list.) If you pass + # a String for +data+, you can later retrieve it (after writing to it, for + # example) with CSV.string(). + # + # Note that a wrapped String will be positioned at at the beginning (for + # reading). If you want it at the end (for writing), use CSV::generate(). + # If you want any other positioning, pass a preset StringIO object instead. + # + # You may set any reading and/or writing preferences in the +options+ Hash. + # Available options are: + # + # :col_sep:: The String placed between each field. + # This String will be transcoded into + # the data's Encoding before parsing. + # :row_sep:: The String appended to the end of each + # row. This can be set to the special + # :auto setting, which requests + # that CSV automatically discover this + # from the data. Auto-discovery reads + # ahead in the data looking for the next + # "\r\n", "\n", or + # "\r" sequence. A sequence + # will be selected even if it occurs in + # a quoted field, assuming that you + # would have the same line endings + # there. If none of those sequences is + # found, +data+ is ARGF, + # STDIN, STDOUT, or + # STDERR, or the stream is only + # available for output, the default + # $INPUT_RECORD_SEPARATOR + # ($/) is used. Obviously, + # discovery takes a little time. Set + # manually if speed is important. Also + # note that IO objects should be opened + # in binary mode on Windows if this + # feature will be used as the + # line-ending translation can cause + # problems with resetting the document + # position to where it was before the + # read ahead. This String will be + # transcoded into the data's Encoding + # before parsing. + # :quote_char:: The character used to quote fields. + # This has to be a single character + # String. This is useful for + # application that incorrectly use + # ' as the quote character + # instead of the correct ". + # CSV will always consider a double + # sequence this character to be an + # escaped quote. This String will be + # transcoded into the data's Encoding + # before parsing. + # :field_size_limit:: This is a maximum size CSV will read + # ahead looking for the closing quote + # for a field. (In truth, it reads to + # the first line ending beyond this + # size.) If a quote cannot be found + # within the limit CSV will raise a + # MalformedCSVError, assuming the data + # is faulty. You can use this limit to + # prevent what are effectively DoS + # attacks on the parser. However, this + # limit can cause a legitimate parse to + # fail and thus is set to +nil+, or off, + # by default. + # :converters:: An Array of names from the Converters + # Hash and/or lambdas that handle custom + # conversion. A single converter + # doesn't have to be in an Array. All + # built-in converters try to transcode + # fields to UTF-8 before converting. + # The conversion will fail if the data + # cannot be transcoded, leaving the + # field unchanged. + # :unconverted_fields:: If set to +true+, an + # unconverted_fields() method will be + # added to all returned rows (Array or + # CSV::Row) that will return the fields + # as they were before conversion. Note + # that :headers supplied by + # Array or String were not fields of the + # document and thus will have an empty + # Array attached. + # :headers:: If set to :first_row or + # +true+, the initial row of the CSV + # file will be treated as a row of + # headers. If set to an Array, the + # contents will be used as the headers. + # If set to a String, the String is run + # through a call of CSV::parse_line() + # with the same :col_sep, + # :row_sep, and + # :quote_char as this instance + # to produce an Array of headers. This + # setting causes CSV#shift() to return + # rows as CSV::Row objects instead of + # Arrays and CSV#read() to return + # CSV::Table objects instead of an Array + # of Arrays. + # :return_headers:: When +false+, header rows are silently + # swallowed. If set to +true+, header + # rows are returned in a CSV::Row object + # with identical headers and + # fields (save that the fields do not go + # through the converters). + # :write_headers:: When +true+ and :headers is + # set, a header row will be added to the + # output. + # :header_converters:: Identical in functionality to + # :converters save that the + # conversions are only made to header + # rows. All built-in converters try to + # transcode headers to UTF-8 before + # converting. The conversion will fail + # if the data cannot be transcoded, + # leaving the header unchanged. + # :skip_blanks:: When set to a +true+ value, CSV will + # skip over any rows with no content. + # :force_quotes:: When set to a +true+ value, CSV will + # quote all CSV fields it creates. + # :skip_lines:: When set to an object responding to + # match, every line matching + # it is considered a comment and ignored + # during parsing. When set to a String, + # it is first converted to a Regexp. + # When set to +nil+ no line is considered + # a comment. If the passed object does + # not respond to match, + # ArgumentError is thrown. + # + # See CSV::DEFAULT_OPTIONS for the default settings. + # + # Options cannot be overridden in the instance methods for performance reasons, + # so be sure to set what you want here. + # + def initialize(data, options = Hash.new) + # build the options for this read/write + options = DEFAULT_OPTIONS.merge(options) + + # create the IO object we will read from + @io = data.is_a?(String) ? StringIO.new(data) : data + # honor the IO encoding if we can, otherwise default to ASCII-8BIT + @encoding = raw_encoding(nil) || + ( if encoding = options.delete(:internal_encoding) + case encoding + when Encoding; encoding + else Encoding.find(encoding) + end + end ) || + ( case encoding = options.delete(:encoding) + when Encoding; encoding + when /\A[^:]+/; Encoding.find($&) + end ) || + Encoding.default_internal || Encoding.default_external + # + # prepare for building safe regular expressions in the target encoding, + # if we can transcode the needed characters + # + @re_esc = "\\".encode(@encoding) rescue "" + @re_chars = /#{%"[-\\]\\[\\.^$?*+{}()|# \r\n\t\f\v]".encode(@encoding)}/ + + init_separators(options) + init_parsers(options) + init_converters(options) + init_headers(options) + init_comments(options) + + @force_encoding = !!(encoding || options.delete(:encoding)) + options.delete(:internal_encoding) + options.delete(:external_encoding) + unless options.empty? + raise ArgumentError, "Unknown options: #{options.keys.join(', ')}." + end + + # track our own lineno since IO gets confused about line-ends is CSV fields + @lineno = 0 + end + + # + # The encoded :col_sep used in parsing and writing. See CSV::new + # for details. + # + attr_reader :col_sep + # + # The encoded :row_sep used in parsing and writing. See CSV::new + # for details. + # + attr_reader :row_sep + # + # The encoded :quote_char used in parsing and writing. See CSV::new + # for details. + # + attr_reader :quote_char + # The limit for field size, if any. See CSV::new for details. + attr_reader :field_size_limit + + # The regex marking a line as a comment. See CSV::new for details + attr_reader :skip_lines + + # + # Returns the current list of converters in effect. See CSV::new for details. + # Built-in converters will be returned by name, while others will be returned + # as is. + # + def converters + @converters.map do |converter| + name = Converters.rassoc(converter) + name ? name.first : converter + end + end + # + # Returns +true+ if unconverted_fields() to parsed results. See CSV::new + # for details. + # + def unconverted_fields?() @unconverted_fields end + # + # Returns +nil+ if headers will not be used, +true+ if they will but have not + # yet been read, or the actual headers after they have been read. See + # CSV::new for details. + # + def headers + @headers || true if @use_headers + end + # + # Returns +true+ if headers will be returned as a row of results. + # See CSV::new for details. + # + def return_headers?() @return_headers end + # Returns +true+ if headers are written in output. See CSV::new for details. + def write_headers?() @write_headers end + # + # Returns the current list of converters in effect for headers. See CSV::new + # for details. Built-in converters will be returned by name, while others + # will be returned as is. + # + def header_converters + @header_converters.map do |converter| + name = HeaderConverters.rassoc(converter) + name ? name.first : converter + end + end + # + # Returns +true+ blank lines are skipped by the parser. See CSV::new + # for details. + # + def skip_blanks?() @skip_blanks end + # Returns +true+ if all output fields are quoted. See CSV::new for details. + def force_quotes?() @force_quotes end + + # + # The Encoding CSV is parsing or writing in. This will be the Encoding you + # receive parsed data in and/or the Encoding data will be written in. + # + attr_reader :encoding + + # + # The line number of the last row read from this file. Fields with nested + # line-end characters will not affect this count. + # + attr_reader :lineno + + ### IO and StringIO Delegation ### + + extend Forwardable + def_delegators :@io, :binmode, :binmode?, :close, :close_read, :close_write, + :closed?, :eof, :eof?, :external_encoding, :fcntl, + :fileno, :flock, :flush, :fsync, :internal_encoding, + :ioctl, :isatty, :path, :pid, :pos, :pos=, :reopen, + :seek, :stat, :string, :sync, :sync=, :tell, :to_i, + :to_io, :truncate, :tty? + + # Rewinds the underlying IO object and resets CSV's lineno() counter. + def rewind + @headers = nil + @lineno = 0 + + @io.rewind + end + + ### End Delegation ### + + # + # The primary write method for wrapped Strings and IOs, +row+ (an Array or + # CSV::Row) is converted to CSV and appended to the data source. When a + # CSV::Row is passed, only the row's fields() are appended to the output. + # + # The data source must be open for writing. + # + def <<(row) + # make sure headers have been assigned + if header_row? and [Array, String].include? @use_headers.class + parse_headers # won't read data for Array or String + self << @headers if @write_headers + end + + # handle CSV::Row objects and Hashes + row = case row + when self.class::Row then row.fields + when Hash then @headers.map { |header| row[header] } + else row + end + + @headers = row if header_row? + @lineno += 1 + + output = row.map(&@quote).join(@col_sep) + @row_sep # quote and separate + if @io.is_a?(StringIO) and + output.encoding != (encoding = raw_encoding) + if @force_encoding + output = output.encode(encoding) + elsif (compatible_encoding = Encoding.compatible?(@io.string, output)) + @io.set_encoding(compatible_encoding) + @io.seek(0, IO::SEEK_END) + end + end + @io << output + + self # for chaining + end + alias_method :add_row, :<< + alias_method :puts, :<< + + # + # :call-seq: + # convert( name ) + # convert { |field| ... } + # convert { |field, field_info| ... } + # + # You can use this method to install a CSV::Converters built-in, or provide a + # block that handles a custom conversion. + # + # If you provide a block that takes one argument, it will be passed the field + # and is expected to return the converted value or the field itself. If your + # block takes two arguments, it will also be passed a CSV::FieldInfo Struct, + # containing details about the field. Again, the block should return a + # converted field or the field itself. + # + def convert(name = nil, &converter) + add_converter(:converters, self.class::Converters, name, &converter) + end + + # + # :call-seq: + # header_convert( name ) + # header_convert { |field| ... } + # header_convert { |field, field_info| ... } + # + # Identical to CSV#convert(), but for header rows. + # + # Note that this method must be called before header rows are read to have any + # effect. + # + def header_convert(name = nil, &converter) + add_converter( :header_converters, + self.class::HeaderConverters, + name, + &converter ) + end + + include Enumerable + + # + # Yields each row of the data source in turn. + # + # Support for Enumerable. + # + # The data source must be open for reading. + # + def each + if block_given? + while row = shift + yield row + end + else + to_enum + end + end + + # + # Slurps the remaining rows and returns an Array of Arrays. + # + # The data source must be open for reading. + # + def read + rows = to_a + if @use_headers + Table.new(rows) + else + rows + end + end + alias_method :readlines, :read + + # Returns +true+ if the next row read will be a header row. + def header_row? + @use_headers and @headers.nil? + end + + # + # The primary read method for wrapped Strings and IOs, a single row is pulled + # from the data source, parsed and returned as an Array of fields (if header + # rows are not used) or a CSV::Row (when header rows are used). + # + # The data source must be open for reading. + # + def shift + ######################################################################### + ### This method is purposefully kept a bit long as simple conditional ### + ### checks are faster than numerous (expensive) method calls. ### + ######################################################################### + + # handle headers not based on document content + if header_row? and @return_headers and + [Array, String].include? @use_headers.class + if @unconverted_fields + return add_unconverted_fields(parse_headers, Array.new) + else + return parse_headers + end + end + + # + # it can take multiple calls to @io.gets() to get a full line, + # because of \r and/or \n characters embedded in quoted fields + # + in_extended_col = false + csv = Array.new + + loop do + # add another read to the line + unless parse = @io.gets(@row_sep) + return nil + end + + parse.sub!(@parsers[:line_end], "") + + if csv.empty? + # + # I believe a blank line should be an Array.new, not Ruby 1.8 + # CSV's [nil] + # + if parse.empty? + @lineno += 1 + if @skip_blanks + next + elsif @unconverted_fields + return add_unconverted_fields(Array.new, Array.new) + elsif @use_headers + return self.class::Row.new(Array.new, Array.new) + else + return Array.new + end + end + end + + next if @skip_lines and @skip_lines.match parse + + parts = parse.split(@col_sep, -1) + if parts.empty? + if in_extended_col + csv[-1] << @col_sep # will be replaced with a @row_sep after the parts.each loop + else + csv << nil + end + end + + # This loop is the hot path of csv parsing. Some things may be non-dry + # for a reason. Make sure to benchmark when refactoring. + parts.each do |part| + if in_extended_col + # If we are continuing a previous column + if part[-1] == @quote_char && part.count(@quote_char) % 2 != 0 + # extended column ends + csv.last << part[0..-2] + if csv.last =~ @parsers[:stray_quote] + raise MalformedCSVError, + "Missing or stray quote in line #{lineno + 1}" + end + csv.last.gsub!(@quote_char * 2, @quote_char) + in_extended_col = false + else + csv.last << part + csv.last << @col_sep + end + elsif part[0] == @quote_char + # If we are staring a new quoted column + if part[-1] != @quote_char || part.count(@quote_char) % 2 != 0 + # start an extended column + csv << part[1..-1] + csv.last << @col_sep + in_extended_col = true + else + # regular quoted column + csv << part[1..-2] + if csv.last =~ @parsers[:stray_quote] + raise MalformedCSVError, + "Missing or stray quote in line #{lineno + 1}" + end + csv.last.gsub!(@quote_char * 2, @quote_char) + end + elsif part =~ @parsers[:quote_or_nl] + # Unquoted field with bad characters. + if part =~ @parsers[:nl_or_lf] + raise MalformedCSVError, "Unquoted fields do not allow " + + "\\r or \\n (line #{lineno + 1})." + else + raise MalformedCSVError, "Illegal quoting in line #{lineno + 1}." + end + else + # Regular ole unquoted field. + csv << (part.empty? ? nil : part) + end + end + + # Replace tacked on @col_sep with @row_sep if we are still in an extended + # column. + csv[-1][-1] = @row_sep if in_extended_col + + if in_extended_col + # if we're at eof?(), a quoted field wasn't closed... + if @io.eof? + raise MalformedCSVError, + "Unclosed quoted field on line #{lineno + 1}." + elsif @field_size_limit and csv.last.size >= @field_size_limit + raise MalformedCSVError, "Field size exceeded on line #{lineno + 1}." + end + # otherwise, we need to loop and pull some more data to complete the row + else + @lineno += 1 + + # save fields unconverted fields, if needed... + unconverted = csv.dup if @unconverted_fields + + # convert fields, if needed... + csv = convert_fields(csv) unless @use_headers or @converters.empty? + # parse out header rows and handle CSV::Row conversions... + csv = parse_headers(csv) if @use_headers + + # inject unconverted fields and accessor, if requested... + if @unconverted_fields and not csv.respond_to? :unconverted_fields + add_unconverted_fields(csv, unconverted) + end + + # return the results + break csv + end + end + end + alias_method :gets, :shift + alias_method :readline, :shift + + # + # Returns a simplified description of the key CSV attributes in an + # ASCII compatible String. + # + def inspect + str = ["<#", self.class.to_s, " io_type:"] + # show type of wrapped IO + if @io == $stdout then str << "$stdout" + elsif @io == $stdin then str << "$stdin" + elsif @io == $stderr then str << "$stderr" + else str << @io.class.to_s + end + # show IO.path(), if available + if @io.respond_to?(:path) and (p = @io.path) + str << " io_path:" << p.inspect + end + # show encoding + str << " encoding:" << @encoding.name + # show other attributes + %w[ lineno col_sep row_sep + quote_char skip_blanks ].each do |attr_name| + if a = instance_variable_get("@#{attr_name}") + str << " " << attr_name << ":" << a.inspect + end + end + if @use_headers + str << " headers:" << headers.inspect + end + str << ">" + begin + str.join('') + rescue # any encoding error + str.map do |s| + e = Encoding::Converter.asciicompat_encoding(s.encoding) + e ? s.encode(e) : s.force_encoding("ASCII-8BIT") + end.join('') + end + end + + private + + # + # Stores the indicated separators for later use. + # + # If auto-discovery was requested for @row_sep, this method will read + # ahead in the @io and try to find one. +ARGF+, +STDIN+, +STDOUT+, + # +STDERR+ and any stream open for output only with a default + # @row_sep of $INPUT_RECORD_SEPARATOR ($/). + # + # This method also establishes the quoting rules used for CSV output. + # + def init_separators(options) + # store the selected separators + @col_sep = options.delete(:col_sep).to_s.encode(@encoding) + @row_sep = options.delete(:row_sep) # encode after resolving :auto + @quote_char = options.delete(:quote_char).to_s.encode(@encoding) + + if @quote_char.length != 1 + raise ArgumentError, ":quote_char has to be a single character String" + end + + # + # automatically discover row separator when requested + # (not fully encoding safe) + # + if @row_sep == :auto + if [ARGF, STDIN, STDOUT, STDERR].include?(@io) or + (defined?(Zlib) and @io.class == Zlib::GzipWriter) + @row_sep = $INPUT_RECORD_SEPARATOR + else + begin + # + # remember where we were (pos() will raise an exception if @io is pipe + # or not opened for reading) + # + saved_pos = @io.pos + while @row_sep == :auto + # + # if we run out of data, it's probably a single line + # (ensure will set default value) + # + break unless sample = @io.gets(nil, 1024) + # extend sample if we're unsure of the line ending + if sample.end_with? encode_str("\r") + sample << (@io.gets(nil, 1) || "") + end + + # try to find a standard separator + if sample =~ encode_re("\r\n?|\n") + @row_sep = $& + break + end + end + + # tricky seek() clone to work around GzipReader's lack of seek() + @io.rewind + # reset back to the remembered position + while saved_pos > 1024 # avoid loading a lot of data into memory + @io.read(1024) + saved_pos -= 1024 + end + @io.read(saved_pos) if saved_pos.nonzero? + rescue IOError # not opened for reading + # do nothing: ensure will set default + rescue NoMethodError # Zlib::GzipWriter doesn't have some IO methods + # do nothing: ensure will set default + rescue SystemCallError # pipe + # do nothing: ensure will set default + ensure + # + # set default if we failed to detect + # (stream not opened for reading, a pipe, or a single line of data) + # + @row_sep = $INPUT_RECORD_SEPARATOR if @row_sep == :auto + end + end + end + @row_sep = @row_sep.to_s.encode(@encoding) + + # establish quoting rules + @force_quotes = options.delete(:force_quotes) + do_quote = lambda do |field| + field = String(field) + encoded_quote = @quote_char.encode(field.encoding) + encoded_quote + + field.gsub(encoded_quote, encoded_quote * 2) + + encoded_quote + end + quotable_chars = encode_str("\r\n", @col_sep, @quote_char) + @quote = if @force_quotes + do_quote + else + lambda do |field| + if field.nil? # represent +nil+ fields as empty unquoted fields + "" + else + field = String(field) # Stringify fields + # represent empty fields as empty quoted fields + if field.empty? or + field.count(quotable_chars).nonzero? + do_quote.call(field) + else + field # unquoted field + end + end + end + end + end + + # Pre-compiles parsers and stores them by name for access during reads. + def init_parsers(options) + # store the parser behaviors + @skip_blanks = options.delete(:skip_blanks) + @field_size_limit = options.delete(:field_size_limit) + + # prebuild Regexps for faster parsing + esc_row_sep = escape_re(@row_sep) + esc_quote = escape_re(@quote_char) + @parsers = { + # for detecting parse errors + quote_or_nl: encode_re("[", esc_quote, "\r\n]"), + nl_or_lf: encode_re("[\r\n]"), + stray_quote: encode_re( "[^", esc_quote, "]", esc_quote, + "[^", esc_quote, "]" ), + # safer than chomp!() + line_end: encode_re(esc_row_sep, "\\z"), + # illegal unquoted characters + return_newline: encode_str("\r\n") + } + end + + # + # Loads any converters requested during construction. + # + # If +field_name+ is set :converters (the default) field converters + # are set. When +field_name+ is :header_converters header converters + # are added instead. + # + # The :unconverted_fields option is also actived for + # :converters calls, if requested. + # + def init_converters(options, field_name = :converters) + if field_name == :converters + @unconverted_fields = options.delete(:unconverted_fields) + end + + instance_variable_set("@#{field_name}", Array.new) + + # find the correct method to add the converters + convert = method(field_name.to_s.sub(/ers\Z/, "")) + + # load converters + unless options[field_name].nil? + # allow a single converter not wrapped in an Array + unless options[field_name].is_a? Array + options[field_name] = [options[field_name]] + end + # load each converter... + options[field_name].each do |converter| + if converter.is_a? Proc # custom code block + convert.call(&converter) + else # by name + convert.call(converter) + end + end + end + + options.delete(field_name) + end + + # Stores header row settings and loads header converters, if needed. + def init_headers(options) + @use_headers = options.delete(:headers) + @return_headers = options.delete(:return_headers) + @write_headers = options.delete(:write_headers) + + # headers must be delayed until shift(), in case they need a row of content + @headers = nil + + init_converters(options, :header_converters) + end + + # Stores the pattern of comments to skip from the provided options. + # + # The pattern must respond to +.match+, else ArgumentError is raised. + # Strings are converted to a Regexp. + # + # See also CSV.new + def init_comments(options) + @skip_lines = options.delete(:skip_lines) + @skip_lines = Regexp.new(@skip_lines) if @skip_lines.is_a? String + if @skip_lines and not @skip_lines.respond_to?(:match) + raise ArgumentError, ":skip_lines has to respond to matches" + end + end + # + # The actual work method for adding converters, used by both CSV.convert() and + # CSV.header_convert(). + # + # This method requires the +var_name+ of the instance variable to place the + # converters in, the +const+ Hash to lookup named converters in, and the + # normal parameters of the CSV.convert() and CSV.header_convert() methods. + # + def add_converter(var_name, const, name = nil, &converter) + if name.nil? # custom converter + instance_variable_get("@#{var_name}") << converter + else # named converter + combo = const[name] + case combo + when Array # combo converter + combo.each do |converter_name| + add_converter(var_name, const, converter_name) + end + else # individual named converter + instance_variable_get("@#{var_name}") << combo + end + end + end + + # + # Processes +fields+ with @converters, or @header_converters + # if +headers+ is passed as +true+, returning the converted field set. Any + # converter that changes the field into something other than a String halts + # the pipeline of conversion for that field. This is primarily an efficiency + # shortcut. + # + def convert_fields(fields, headers = false) + # see if we are converting headers or fields + converters = headers ? @header_converters : @converters + + fields.map.with_index do |field, index| + converters.each do |converter| + field = if converter.arity == 1 # straight field converter + converter[field] + else # FieldInfo converter + header = @use_headers && !headers ? @headers[index] : nil + converter[field, FieldInfo.new(index, lineno, header)] + end + break unless field.is_a? String # short-circuit pipeline for speed + end + field # final state of each field, converted or original + end + end + + # + # This method is used to turn a finished +row+ into a CSV::Row. Header rows + # are also dealt with here, either by returning a CSV::Row with identical + # headers and fields (save that the fields do not go through the converters) + # or by reading past them to return a field row. Headers are also saved in + # @headers for use in future rows. + # + # When +nil+, +row+ is assumed to be a header row not based on an actual row + # of the stream. + # + def parse_headers(row = nil) + if @headers.nil? # header row + @headers = case @use_headers # save headers + # Array of headers + when Array then @use_headers + # CSV header String + when String + self.class.parse_line( @use_headers, + col_sep: @col_sep, + row_sep: @row_sep, + quote_char: @quote_char ) + # first row is headers + else row + end + + # prepare converted and unconverted copies + row = @headers if row.nil? + @headers = convert_fields(@headers, true) + @headers.each { |h| h.freeze if h.is_a? String } + + if @return_headers # return headers + return self.class::Row.new(@headers, row, true) + elsif not [Array, String].include? @use_headers.class # skip to field row + return shift + end + end + + self.class::Row.new(@headers, convert_fields(row)) # field row + end + + # + # This method injects an instance variable unconverted_fields into + # +row+ and an accessor method for +row+ called unconverted_fields(). The + # variable is set to the contents of +fields+. + # + def add_unconverted_fields(row, fields) + class << row + attr_reader :unconverted_fields + end + row.instance_eval { @unconverted_fields = fields } + row + end + + # + # This method is an encoding safe version of Regexp::escape(). It will escape + # any characters that would change the meaning of a regular expression in the + # encoding of +str+. Regular expression characters that cannot be transcoded + # to the target encoding will be skipped and no escaping will be performed if + # a backslash cannot be transcoded. + # + def escape_re(str) + str.gsub(@re_chars) {|c| @re_esc + c} + end + + # + # Builds a regular expression in @encoding. All +chunks+ will be + # transcoded to that encoding. + # + def encode_re(*chunks) + Regexp.new(encode_str(*chunks)) + end + + # + # Builds a String in @encoding. All +chunks+ will be transcoded to + # that encoding. + # + def encode_str(*chunks) + chunks.map { |chunk| chunk.encode(@encoding.name) }.join('') + end + + private + + # + # Returns the encoding of the internal IO object or the +default+ if the + # encoding cannot be determined. + # + def raw_encoding(default = Encoding::ASCII_8BIT) + if @io.respond_to? :internal_encoding + @io.internal_encoding || @io.external_encoding + elsif @io.is_a? StringIO + @io.string.encoding + elsif @io.respond_to? :encoding + @io.encoding + else + default + end + end +end + +# Passes +args+ to CSV::instance. +# +# CSV("CSV,data").read +# #=> [["CSV", "data"]] +# +# If a block is given, the instance is passed the block and the return value +# becomes the return value of the block. +# +# CSV("CSV,data") { |c| +# c.read.any? { |a| a.include?("data") } +# } #=> true +# +# CSV("CSV,data") { |c| +# c.read.any? { |a| a.include?("zombies") } +# } #=> false +# +def CSV(*args, &block) + CSV.instance(*args, &block) +end + +class Array # :nodoc: + # Equivalent to CSV::generate_line(self, options) + # + # ["CSV", "data"].to_csv + # #=> "CSV,data\n" + def to_csv(options = Hash.new) + CSV.generate_line(self, options) + end +end + +class String # :nodoc: + # Equivalent to CSV::parse_line(self, options) + # + # "CSV,data".parse_csv + # #=> ["CSV", "data"] + def parse_csv(options = Hash.new) + CSV.parse_line(self, options) + end +end diff --git a/ruby/lib/ruby/2.1.0/date.rb b/ruby/lib/ruby/2.1.0/date.rb new file mode 100644 index 0000000..d235d76 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/date.rb @@ -0,0 +1,61 @@ +# date.rb: Written by Tadayoshi Funaba 1998-2011 + +require 'date_core' +require 'date/format' + +class Date + + class Infinity < Numeric # :nodoc: + + include Comparable + + def initialize(d=1) @d = d <=> 0 end + + def d() @d end + + protected :d + + def zero? () false end + def finite? () false end + def infinite? () d.nonzero? end + def nan? () d.zero? end + + def abs() self.class.new end + + def -@ () self.class.new(-d) end + def +@ () self.class.new(+d) end + + def <=> (other) + case other + when Infinity; return d <=> other.d + when Numeric; return d + else + begin + l, r = other.coerce(self) + return l <=> r + rescue NoMethodError + end + end + nil + end + + def coerce(other) + case other + when Numeric; return -d, d + else + super + end + end + + def to_f + return 0 if @d == 0 + if @d > 0 + Float::INFINITY + else + -Float::INFINITY + end + end + + end + +end diff --git a/ruby/lib/ruby/2.1.0/date/format.rb b/ruby/lib/ruby/2.1.0/date/format.rb new file mode 100644 index 0000000..892e7aa --- /dev/null +++ b/ruby/lib/ruby/2.1.0/date/format.rb @@ -0,0 +1 @@ +# format.rb: Written by Tadayoshi Funaba 1999-2011 diff --git a/ruby/lib/ruby/2.1.0/debug.rb b/ruby/lib/ruby/2.1.0/debug.rb new file mode 100644 index 0000000..fcbf849 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/debug.rb @@ -0,0 +1,1087 @@ +# Copyright (C) 2000 Network Applied Communication Laboratory, Inc. +# Copyright (C) 2000 Information-technology Promotion Agency, Japan +# Copyright (C) 2000-2003 NAKAMURA, Hiroshi + +require 'continuation' + +if $SAFE > 0 + STDERR.print "-r debug.rb is not available in safe mode\n" + exit 1 +end + +require 'tracer' +require 'pp' + +class Tracer # :nodoc: + def Tracer.trace_func(*vars) + Single.trace_func(*vars) + end +end + +SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__ # :nodoc: + +## +# This library provides debugging functionality to Ruby. +# +# To add a debugger to your code, start by requiring +debug+ in your +# program: +# +# def say(word) +# require 'debug' +# puts word +# end +# +# This will cause Ruby to interrupt execution and show a prompt when the +say+ +# method is run. +# +# Once you're inside the prompt, you can start debugging your program. +# +# (rdb:1) p word +# "hello" +# +# == Getting help +# +# You can get help at any time by pressing +h+. +# +# (rdb:1) h +# Debugger help v.-0.002b +# Commands +# b[reak] [file:|class:] +# b[reak] [class.] +# set breakpoint to some position +# wat[ch] set watchpoint to some expression +# cat[ch] (|off) set catchpoint to an exception +# b[reak] list breakpoints +# cat[ch] show catchpoint +# del[ete][ nnn] delete some or all breakpoints +# disp[lay] add expression into display expression list +# undisp[lay][ nnn] delete one particular or all display expressions +# c[ont] run until program ends or hit breakpoint +# s[tep][ nnn] step (into methods) one line or till line nnn +# n[ext][ nnn] go over one line or till line nnn +# w[here] display frames +# f[rame] alias for where +# l[ist][ (-|nn-mm)] list program, - lists backwards +# nn-mm lists given lines +# up[ nn] move to higher frame +# down[ nn] move to lower frame +# fin[ish] return to outer frame +# tr[ace] (on|off) set trace mode of current thread +# tr[ace] (on|off) all set trace mode of all threads +# q[uit] exit from debugger +# v[ar] g[lobal] show global variables +# v[ar] l[ocal] show local variables +# v[ar] i[nstance] show instance variables of object +# v[ar] c[onst] show constants of object +# m[ethod] i[nstance] show methods of object +# m[ethod] show instance methods of class or module +# th[read] l[ist] list all threads +# th[read] c[ur[rent]] show current thread +# th[read] [sw[itch]] switch thread context to nnn +# th[read] stop stop thread nnn +# th[read] resume resume thread nnn +# p expression evaluate expression and print its value +# h[elp] print this help +# evaluate +# +# == Usage +# +# The following is a list of common functionalities that the debugger +# provides. +# +# === Navigating through your code +# +# In general, a debugger is used to find bugs in your program, which +# often means pausing execution and inspecting variables at some point +# in time. +# +# Let's look at an example: +# +# def my_method(foo) +# require 'debug' +# foo = get_foo if foo.nil? +# raise if foo.nil? +# end +# +# When you run this program, the debugger will kick in just before the +# +foo+ assignment. +# +# (rdb:1) p foo +# nil +# +# In this example, it'd be interesting to move to the next line and +# inspect the value of +foo+ again. You can do that by pressing +n+: +# +# (rdb:1) n # goes to next line +# (rdb:1) p foo +# nil +# +# You now know that the original value of +foo+ was nil, and that it +# still was nil after calling +get_foo+. +# +# Other useful commands for navigating through your code are: +# +# +c+:: +# Runs the program until it either exists or encounters another breakpoint. +# You usually press +c+ when you are finished debugging your program and +# want to resume its execution. +# +s+:: +# Steps into method definition. In the previous example, +s+ would take you +# inside the method definition of +get_foo+. +# +r+:: +# Restart the program. +# +q+:: +# Quit the program. +# +# === Inspecting variables +# +# You can use the debugger to easily inspect both local and global variables. +# We've seen how to inspect local variables before: +# +# (rdb:1) p my_arg +# 42 +# +# You can also pretty print the result of variables or expressions: +# +# (rdb:1) pp %w{a very long long array containing many words} +# ["a", +# "very", +# "long", +# ... +# ] +# +# You can list all local variables with +v l+: +# +# (rdb:1) v l +# foo => "hello" +# +# Similarly, you can show all global variables with +v g+: +# +# (rdb:1) v g +# all global variables +# +# Finally, you can omit +p+ if you simply want to evaluate a variable or +# expression +# +# (rdb:1) 5**2 +# 25 +# +# === Going beyond basics +# +# Ruby Debug provides more advanced functionalities like switching +# between threads, setting breakpoints and watch expressions, and more. +# The full list of commands is available at any time by pressing +h+. +# +# == Staying out of trouble +# +# Make sure you remove every instance of +require 'debug'+ before +# shipping your code. Failing to do so may result in your program +# hanging unpredictably. +# +# Debug is not available in safe mode. + +class DEBUGGER__ + MUTEX = Mutex.new # :nodoc: + + class Context # :nodoc: + DEBUG_LAST_CMD = [] + + begin + require 'readline' + def readline(prompt, hist) + Readline::readline(prompt, hist) + end + rescue LoadError + def readline(prompt, hist) + STDOUT.print prompt + STDOUT.flush + line = STDIN.gets + exit unless line + line.chomp! + line + end + USE_READLINE = false + end + + def initialize + if Thread.current == Thread.main + @stop_next = 1 + else + @stop_next = 0 + end + @last_file = nil + @file = nil + @line = nil + @no_step = nil + @frames = [] + @finish_pos = 0 + @trace = false + @catch = "StandardError" + @suspend_next = false + end + + def stop_next(n=1) + @stop_next = n + end + + def set_suspend + @suspend_next = true + end + + def clear_suspend + @suspend_next = false + end + + def suspend_all + DEBUGGER__.suspend + end + + def resume_all + DEBUGGER__.resume + end + + def check_suspend + while MUTEX.synchronize { + if @suspend_next + DEBUGGER__.waiting.push Thread.current + @suspend_next = false + true + end + } + end + end + + def trace? + @trace + end + + def set_trace(arg) + @trace = arg + end + + def stdout + DEBUGGER__.stdout + end + + def break_points + DEBUGGER__.break_points + end + + def display + DEBUGGER__.display + end + + def context(th) + DEBUGGER__.context(th) + end + + def set_trace_all(arg) + DEBUGGER__.set_trace(arg) + end + + def set_last_thread(th) + DEBUGGER__.set_last_thread(th) + end + + def debug_eval(str, binding) + begin + eval(str, binding) + rescue StandardError, ScriptError => e + at = eval("caller(1)", binding) + stdout.printf "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '') + for i in at + stdout.printf "\tfrom %s\n", i + end + throw :debug_error + end + end + + def debug_silent_eval(str, binding) + begin + eval(str, binding) + rescue StandardError, ScriptError + nil + end + end + + def var_list(ary, binding) + ary.sort! + for v in ary + stdout.printf " %s => %s\n", v, eval(v.to_s, binding).inspect + end + end + + def debug_variable_info(input, binding) + case input + when /^\s*g(?:lobal)?\s*$/ + var_list(global_variables, binding) + + when /^\s*l(?:ocal)?\s*$/ + var_list(eval("local_variables", binding), binding) + + when /^\s*i(?:nstance)?\s+/ + obj = debug_eval($', binding) + var_list(obj.instance_variables, obj.instance_eval{binding()}) + + when /^\s*c(?:onst(?:ant)?)?\s+/ + obj = debug_eval($', binding) + unless obj.kind_of? Module + stdout.print "Should be Class/Module: ", $', "\n" + else + var_list(obj.constants, obj.module_eval{binding()}) + end + end + end + + def debug_method_info(input, binding) + case input + when /^i(:?nstance)?\s+/ + obj = debug_eval($', binding) + + len = 0 + for v in obj.methods.sort + len += v.size + 1 + if len > 70 + len = v.size + 1 + stdout.print "\n" + end + stdout.print v, " " + end + stdout.print "\n" + + else + obj = debug_eval(input, binding) + unless obj.kind_of? Module + stdout.print "Should be Class/Module: ", input, "\n" + else + len = 0 + for v in obj.instance_methods(false).sort + len += v.size + 1 + if len > 70 + len = v.size + 1 + stdout.print "\n" + end + stdout.print v, " " + end + stdout.print "\n" + end + end + end + + def thnum + num = DEBUGGER__.instance_eval{@thread_list[Thread.current]} + unless num + DEBUGGER__.make_thread_list + num = DEBUGGER__.instance_eval{@thread_list[Thread.current]} + end + num + end + + def debug_command(file, line, id, binding) + MUTEX.lock + unless defined?($debugger_restart) and $debugger_restart + callcc{|c| $debugger_restart = c} + end + set_last_thread(Thread.current) + frame_pos = 0 + binding_file = file + binding_line = line + previous_line = nil + if ENV['EMACS'] + stdout.printf "\032\032%s:%d:\n", binding_file, binding_line + else + stdout.printf "%s:%d:%s", binding_file, binding_line, + line_at(binding_file, binding_line) + end + @frames[0] = [binding, file, line, id] + display_expressions(binding) + prompt = true + while prompt and input = readline("(rdb:%d) "%thnum(), true) + catch(:debug_error) do + if input == "" + next unless DEBUG_LAST_CMD[0] + input = DEBUG_LAST_CMD[0] + stdout.print input, "\n" + else + DEBUG_LAST_CMD[0] = input + end + + case input + when /^\s*tr(?:ace)?(?:\s+(on|off))?(?:\s+(all))?$/ + if defined?( $2 ) + if $1 == 'on' + set_trace_all true + else + set_trace_all false + end + elsif defined?( $1 ) + if $1 == 'on' + set_trace true + else + set_trace false + end + end + if trace? + stdout.print "Trace on.\n" + else + stdout.print "Trace off.\n" + end + + when /^\s*b(?:reak)?\s+(?:(.+):)?([^.:]+)$/ + pos = $2 + if $1 + klass = debug_silent_eval($1, binding) + file = $1 + end + if pos =~ /^\d+$/ + pname = pos + pos = pos.to_i + else + pname = pos = pos.intern.id2name + end + break_points.push [true, 0, klass || file, pos] + stdout.printf "Set breakpoint %d at %s:%s\n", break_points.size, klass || file, pname + + when /^\s*b(?:reak)?\s+(.+)[#.]([^.:]+)$/ + pos = $2.intern.id2name + klass = debug_eval($1, binding) + break_points.push [true, 0, klass, pos] + stdout.printf "Set breakpoint %d at %s.%s\n", break_points.size, klass, pos + + when /^\s*wat(?:ch)?\s+(.+)$/ + exp = $1 + break_points.push [true, 1, exp] + stdout.printf "Set watchpoint %d:%s\n", break_points.size, exp + + when /^\s*b(?:reak)?$/ + if break_points.find{|b| b[1] == 0} + n = 1 + stdout.print "Breakpoints:\n" + break_points.each do |b| + if b[0] and b[1] == 0 + stdout.printf " %d %s:%s\n", n, b[2], b[3] + end + n += 1 + end + end + if break_points.find{|b| b[1] == 1} + n = 1 + stdout.print "\n" + stdout.print "Watchpoints:\n" + for b in break_points + if b[0] and b[1] == 1 + stdout.printf " %d %s\n", n, b[2] + end + n += 1 + end + end + if break_points.size == 0 + stdout.print "No breakpoints\n" + else + stdout.print "\n" + end + + when /^\s*del(?:ete)?(?:\s+(\d+))?$/ + pos = $1 + unless pos + input = readline("Clear all breakpoints? (y/n) ", false) + if input == "y" + for b in break_points + b[0] = false + end + end + else + pos = pos.to_i + if break_points[pos-1] + break_points[pos-1][0] = false + else + stdout.printf "Breakpoint %d is not defined\n", pos + end + end + + when /^\s*disp(?:lay)?\s+(.+)$/ + exp = $1 + display.push [true, exp] + stdout.printf "%d: ", display.size + display_expression(exp, binding) + + when /^\s*disp(?:lay)?$/ + display_expressions(binding) + + when /^\s*undisp(?:lay)?(?:\s+(\d+))?$/ + pos = $1 + unless pos + input = readline("Clear all expressions? (y/n) ", false) + if input == "y" + for d in display + d[0] = false + end + end + else + pos = pos.to_i + if display[pos-1] + display[pos-1][0] = false + else + stdout.printf "Display expression %d is not defined\n", pos + end + end + + when /^\s*c(?:ont)?$/ + prompt = false + + when /^\s*s(?:tep)?(?:\s+(\d+))?$/ + if $1 + lev = $1.to_i + else + lev = 1 + end + @stop_next = lev + prompt = false + + when /^\s*n(?:ext)?(?:\s+(\d+))?$/ + if $1 + lev = $1.to_i + else + lev = 1 + end + @stop_next = lev + @no_step = @frames.size - frame_pos + prompt = false + + when /^\s*w(?:here)?$/, /^\s*f(?:rame)?$/ + display_frames(frame_pos) + + when /^\s*l(?:ist)?(?:\s+(.+))?$/ + if not $1 + b = previous_line ? previous_line + 10 : binding_line - 5 + e = b + 9 + elsif $1 == '-' + b = previous_line ? previous_line - 10 : binding_line - 5 + e = b + 9 + else + b, e = $1.split(/[-,]/) + if e + b = b.to_i + e = e.to_i + else + b = b.to_i - 5 + e = b + 9 + end + end + previous_line = b + display_list(b, e, binding_file, binding_line) + + when /^\s*up(?:\s+(\d+))?$/ + previous_line = nil + if $1 + lev = $1.to_i + else + lev = 1 + end + frame_pos += lev + if frame_pos >= @frames.size + frame_pos = @frames.size - 1 + stdout.print "At toplevel\n" + end + binding, binding_file, binding_line = @frames[frame_pos] + stdout.print format_frame(frame_pos) + + when /^\s*down(?:\s+(\d+))?$/ + previous_line = nil + if $1 + lev = $1.to_i + else + lev = 1 + end + frame_pos -= lev + if frame_pos < 0 + frame_pos = 0 + stdout.print "At stack bottom\n" + end + binding, binding_file, binding_line = @frames[frame_pos] + stdout.print format_frame(frame_pos) + + when /^\s*fin(?:ish)?$/ + if frame_pos == @frames.size + stdout.print "\"finish\" not meaningful in the outermost frame.\n" + else + @finish_pos = @frames.size - frame_pos + frame_pos = 0 + prompt = false + end + + when /^\s*cat(?:ch)?(?:\s+(.+))?$/ + if $1 + excn = $1 + if excn == 'off' + @catch = nil + stdout.print "Clear catchpoint.\n" + else + @catch = excn + stdout.printf "Set catchpoint %s.\n", @catch + end + else + if @catch + stdout.printf "Catchpoint %s.\n", @catch + else + stdout.print "No catchpoint.\n" + end + end + + when /^\s*q(?:uit)?$/ + input = readline("Really quit? (y/n) ", false) + if input == "y" + exit! # exit -> exit!: No graceful way to stop threads... + end + + when /^\s*v(?:ar)?\s+/ + debug_variable_info($', binding) + + when /^\s*m(?:ethod)?\s+/ + debug_method_info($', binding) + + when /^\s*th(?:read)?\s+/ + if DEBUGGER__.debug_thread_info($', binding) == :cont + prompt = false + end + + when /^\s*pp\s+/ + PP.pp(debug_eval($', binding), stdout) + + when /^\s*p\s+/ + stdout.printf "%s\n", debug_eval($', binding).inspect + + when /^\s*r(?:estart)?$/ + $debugger_restart.call + + when /^\s*h(?:elp)?$/ + debug_print_help() + + else + v = debug_eval(input, binding) + stdout.printf "%s\n", v.inspect + end + end + end + MUTEX.unlock + resume_all + end + + def debug_print_help + stdout.print < + b[reak] [class.] + set breakpoint to some position + wat[ch] set watchpoint to some expression + cat[ch] (|off) set catchpoint to an exception + b[reak] list breakpoints + cat[ch] show catchpoint + del[ete][ nnn] delete some or all breakpoints + disp[lay] add expression into display expression list + undisp[lay][ nnn] delete one particular or all display expressions + c[ont] run until program ends or hit breakpoint + s[tep][ nnn] step (into methods) one line or till line nnn + n[ext][ nnn] go over one line or till line nnn + w[here] display frames + f[rame] alias for where + l[ist][ (-|nn-mm)] list program, - lists backwards + nn-mm lists given lines + up[ nn] move to higher frame + down[ nn] move to lower frame + fin[ish] return to outer frame + tr[ace] (on|off) set trace mode of current thread + tr[ace] (on|off) all set trace mode of all threads + q[uit] exit from debugger + v[ar] g[lobal] show global variables + v[ar] l[ocal] show local variables + v[ar] i[nstance] show instance variables of object + v[ar] c[onst] show constants of object + m[ethod] i[nstance] show methods of object + m[ethod] show instance methods of class or module + th[read] l[ist] list all threads + th[read] c[ur[rent]] show current thread + th[read] [sw[itch]] switch thread context to nnn + th[read] stop stop thread nnn + th[read] resume resume thread nnn + pp expression evaluate expression and pretty_print its value + p expression evaluate expression and print its value + r[estart] restart program + h[elp] print this help + evaluate +EOHELP + end + + def display_expressions(binding) + n = 1 + for d in display + if d[0] + stdout.printf "%d: ", n + display_expression(d[1], binding) + end + n += 1 + end + end + + def display_expression(exp, binding) + stdout.printf "%s = %s\n", exp, debug_silent_eval(exp, binding).to_s + end + + def frame_set_pos(file, line) + if @frames[0] + @frames[0][1] = file + @frames[0][2] = line + end + end + + def display_frames(pos) + 0.upto(@frames.size - 1) do |n| + if n == pos + stdout.print "--> " + else + stdout.print " " + end + stdout.print format_frame(n) + end + end + + def format_frame(pos) + _, file, line, id = @frames[pos] + sprintf "#%d %s:%s%s\n", pos + 1, file, line, + (id ? ":in `#{id.id2name}'" : "") + end + + def script_lines(file, line) + unless (lines = SCRIPT_LINES__[file]) and lines != true + Tracer::Single.get_line(file, line) if File.exist?(file) + lines = SCRIPT_LINES__[file] + lines = nil if lines == true + end + lines + end + + def display_list(b, e, file, line) + if lines = script_lines(file, line) + stdout.printf "[%d, %d] in %s\n", b, e, file + b.upto(e) do |n| + if n > 0 && lines[n-1] + if n == line + stdout.printf "=> %d %s\n", n, lines[n-1].chomp + else + stdout.printf " %d %s\n", n, lines[n-1].chomp + end + end + end + else + stdout.printf "No sourcefile available for %s\n", file + end + end + + def line_at(file, line) + lines = script_lines(file, line) + if lines and line = lines[line-1] + return line + end + return "\n" + end + + def debug_funcname(id) + if id.nil? + "toplevel" + else + id.id2name + end + end + + def check_break_points(file, klass, pos, binding, id) + return false if break_points.empty? + n = 1 + for b in break_points + if b[0] # valid + if b[1] == 0 # breakpoint + if (b[2] == file and b[3] == pos) or + (klass and b[2] == klass and b[3] == pos) + stdout.printf "Breakpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos + return true + end + elsif b[1] == 1 # watchpoint + if debug_silent_eval(b[2], binding) + stdout.printf "Watchpoint %d, %s at %s:%s\n", n, debug_funcname(id), file, pos + return true + end + end + end + n += 1 + end + return false + end + + def excn_handle(file, line, id, binding) + if $!.class <= SystemExit + set_trace_func nil + exit + end + + if @catch and ($!.class.ancestors.find { |e| e.to_s == @catch }) + stdout.printf "%s:%d: `%s' (%s)\n", file, line, $!, $!.class + fs = @frames.size + tb = caller(0)[-fs..-1] + if tb + for i in tb + stdout.printf "\tfrom %s\n", i + end + end + suspend_all + debug_command(file, line, id, binding) + end + end + + def trace_func(event, file, line, id, binding, klass) + Tracer.trace_func(event, file, line, id, binding, klass) if trace? + context(Thread.current).check_suspend + @file = file + @line = line + case event + when 'line' + frame_set_pos(file, line) + if !@no_step or @frames.size == @no_step + @stop_next -= 1 + @stop_next = -1 if @stop_next < 0 + elsif @frames.size < @no_step + @stop_next = 0 # break here before leaving... + else + # nothing to do. skipped. + end + if @stop_next == 0 or check_break_points(file, nil, line, binding, id) + @no_step = nil + suspend_all + debug_command(file, line, id, binding) + end + + when 'call' + @frames.unshift [binding, file, line, id] + if check_break_points(file, klass, id.id2name, binding, id) + suspend_all + debug_command(file, line, id, binding) + end + + when 'c-call' + frame_set_pos(file, line) + + when 'class' + @frames.unshift [binding, file, line, id] + + when 'return', 'end' + if @frames.size == @finish_pos + @stop_next = 1 + @finish_pos = 0 + end + @frames.shift + + when 'raise' + excn_handle(file, line, id, binding) + + end + @last_file = file + end + end + + trap("INT") { DEBUGGER__.interrupt } + @last_thread = Thread::main + @max_thread = 1 + @thread_list = {Thread::main => 1} + @break_points = [] + @display = [] + @waiting = [] + @stdout = STDOUT + + class << DEBUGGER__ + # Returns the IO used as stdout. Defaults to STDOUT + def stdout + @stdout + end + + # Sets the IO used as stdout. Defaults to STDOUT + def stdout=(s) + @stdout = s + end + + # Returns the display expression list + # + # See DEBUGGER__ for more usage + def display + @display + end + + # Returns the list of break points where execution will be stopped. + # + # See DEBUGGER__ for more usage + def break_points + @break_points + end + + # Returns the list of waiting threads. + # + # When stepping through the traces of a function, thread gets suspended, to + # be resumed later. + def waiting + @waiting + end + + def set_trace( arg ) + MUTEX.synchronize do + make_thread_list + for th, in @thread_list + context(th).set_trace arg + end + end + arg + end + + def set_last_thread(th) + @last_thread = th + end + + def suspend + MUTEX.synchronize do + make_thread_list + for th, in @thread_list + next if th == Thread.current + context(th).set_suspend + end + end + # Schedule other threads to suspend as soon as possible. + Thread.pass + end + + def resume + MUTEX.synchronize do + make_thread_list + @thread_list.each do |th,| + next if th == Thread.current + context(th).clear_suspend + end + waiting.each do |th| + th.run + end + waiting.clear + end + # Schedule other threads to restart as soon as possible. + Thread.pass + end + + def context(thread=Thread.current) + c = thread[:__debugger_data__] + unless c + thread[:__debugger_data__] = c = Context.new + end + c + end + + def interrupt + context(@last_thread).stop_next + end + + def get_thread(num) + th = @thread_list.key(num) + unless th + @stdout.print "No thread ##{num}\n" + throw :debug_error + end + th + end + + def thread_list(num) + th = get_thread(num) + if th == Thread.current + @stdout.print "+" + else + @stdout.print " " + end + @stdout.printf "%d ", num + @stdout.print th.inspect, "\t" + file = context(th).instance_eval{@file} + if file + @stdout.print file,":",context(th).instance_eval{@line} + end + @stdout.print "\n" + end + + def thread_list_all + for th in @thread_list.values.sort + thread_list(th) + end + end + + def make_thread_list + hash = {} + for th in Thread::list + if @thread_list.key? th + hash[th] = @thread_list[th] + else + @max_thread += 1 + hash[th] = @max_thread + end + end + @thread_list = hash + end + + def debug_thread_info(input, binding) + case input + when /^l(?:ist)?/ + make_thread_list + thread_list_all + + when /^c(?:ur(?:rent)?)?$/ + make_thread_list + thread_list(@thread_list[Thread.current]) + + when /^(?:sw(?:itch)?\s+)?(\d+)/ + make_thread_list + th = get_thread($1.to_i) + if th == Thread.current + @stdout.print "It's the current thread.\n" + else + thread_list(@thread_list[th]) + context(th).stop_next + th.run + return :cont + end + + when /^stop\s+(\d+)/ + make_thread_list + th = get_thread($1.to_i) + if th == Thread.current + @stdout.print "It's the current thread.\n" + elsif th.stop? + @stdout.print "Already stopped.\n" + else + thread_list(@thread_list[th]) + context(th).suspend + end + + when /^resume\s+(\d+)/ + make_thread_list + th = get_thread($1.to_i) + if th == Thread.current + @stdout.print "It's the current thread.\n" + elsif !th.stop? + @stdout.print "Already running." + else + thread_list(@thread_list[th]) + th.run + end + end + end + end + + stdout.printf "Debug.rb\n" + stdout.printf "Emacs support available.\n\n" + RubyVM::InstructionSequence.compile_option = { + trace_instruction: true + } + set_trace_func proc { |event, file, line, id, binding, klass, *rest| + DEBUGGER__.context.trace_func event, file, line, id, binding, klass + } +end diff --git a/ruby/lib/ruby/2.1.0/delegate.rb b/ruby/lib/ruby/2.1.0/delegate.rb new file mode 100644 index 0000000..d790229 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/delegate.rb @@ -0,0 +1,445 @@ +# = delegate -- Support for the Delegation Pattern +# +# Documentation by James Edward Gray II and Gavin Sinclair + +## +# This library provides three different ways to delegate method calls to an +# object. The easiest to use is SimpleDelegator. Pass an object to the +# constructor and all methods supported by the object will be delegated. This +# object can be changed later. +# +# Going a step further, the top level DelegateClass method allows you to easily +# setup delegation through class inheritance. This is considerably more +# flexible and thus probably the most common use for this library. +# +# Finally, if you need full control over the delegation scheme, you can inherit +# from the abstract class Delegator and customize as needed. (If you find +# yourself needing this control, have a look at Forwardable which is also in +# the standard library. It may suit your needs better.) +# +# SimpleDelegator's implementation serves as a nice example if the use of +# Delegator: +# +# class SimpleDelegator < Delegator +# def initialize(obj) +# super # pass obj to Delegator constructor, required +# @delegate_sd_obj = obj # store obj for future use +# end +# +# def __getobj__ +# @delegate_sd_obj # return object we are delegating to, required +# end +# +# def __setobj__(obj) +# @delegate_sd_obj = obj # change delegation object, +# # a feature we're providing +# end +# end +# +# == Notes +# +# Be advised, RDoc will not detect delegated methods. +# +class Delegator < BasicObject + kernel = ::Kernel.dup + kernel.class_eval do + alias __raise__ raise + [:to_s,:inspect,:=~,:!~,:===,:<=>,:eql?,:hash].each do |m| + undef_method m + end + private_instance_methods.each do |m| + if /\Ablock_given\?\z|iterator\?\z|\A__.*__\z/ =~ m + next + end + undef_method m + end + end + include kernel + + # :stopdoc: + def self.const_missing(n) + ::Object.const_get(n) + end + # :startdoc: + + # + # Pass in the _obj_ to delegate method calls to. All methods supported by + # _obj_ will be delegated to. + # + def initialize(obj) + __setobj__(obj) + end + + # + # Handles the magic of delegation through \_\_getobj\_\_. + # + def method_missing(m, *args, &block) + r = true + target = self.__getobj__ {r = false} + begin + if r && target.respond_to?(m) + target.__send__(m, *args, &block) + elsif ::Kernel.respond_to?(m, true) + ::Kernel.instance_method(m).bind(self).(*args, &block) + else + super(m, *args, &block) + end + ensure + $@.delete_if {|t| %r"\A#{Regexp.quote(__FILE__)}:(?:#{[__LINE__-7, __LINE__-5, __LINE__-3].join('|')}):"o =~ t} if $@ + end + end + + # + # Checks for a method provided by this the delegate object by forwarding the + # call through \_\_getobj\_\_. + # + def respond_to_missing?(m, include_private) + r = true + target = self.__getobj__ {r = false} + r &&= target.respond_to?(m, include_private) + if r && include_private && !target.respond_to?(m, false) + warn "#{caller(3)[0]}: delegator does not forward private method \##{m}" + return false + end + r + end + + # + # Returns the methods available to this delegate object as the union + # of this object's and \_\_getobj\_\_ methods. + # + def methods(all=true) + __getobj__.methods(all) | super + end + + # + # Returns the methods available to this delegate object as the union + # of this object's and \_\_getobj\_\_ public methods. + # + def public_methods(all=true) + __getobj__.public_methods(all) | super + end + + # + # Returns the methods available to this delegate object as the union + # of this object's and \_\_getobj\_\_ protected methods. + # + def protected_methods(all=true) + __getobj__.protected_methods(all) | super + end + + # Note: no need to specialize private_methods, since they are not forwarded + + # + # Returns true if two objects are considered of equal value. + # + def ==(obj) + return true if obj.equal?(self) + self.__getobj__ == obj + end + + # + # Returns true if two objects are not considered of equal value. + # + def !=(obj) + return false if obj.equal?(self) + __getobj__ != obj + end + + # + # Delegates ! to the \_\_getobj\_\_ + # + def ! + !__getobj__ + end + + # + # This method must be overridden by subclasses and should return the object + # method calls are being delegated to. + # + def __getobj__ + __raise__ ::NotImplementedError, "need to define `__getobj__'" + end + + # + # This method must be overridden by subclasses and change the object delegate + # to _obj_. + # + def __setobj__(obj) + __raise__ ::NotImplementedError, "need to define `__setobj__'" + end + + # + # Serialization support for the object returned by \_\_getobj\_\_. + # + def marshal_dump + ivars = instance_variables.reject {|var| /\A@delegate_/ =~ var} + [ + :__v2__, + ivars, ivars.map{|var| instance_variable_get(var)}, + __getobj__ + ] + end + + # + # Reinitializes delegation from a serialized object. + # + def marshal_load(data) + version, vars, values, obj = data + if version == :__v2__ + vars.each_with_index{|var, i| instance_variable_set(var, values[i])} + __setobj__(obj) + else + __setobj__(data) + end + end + + def initialize_clone(obj) # :nodoc: + self.__setobj__(obj.__getobj__.clone) + end + def initialize_dup(obj) # :nodoc: + self.__setobj__(obj.__getobj__.dup) + end + private :initialize_clone, :initialize_dup + + ## + # :method: trust + # Trust both the object returned by \_\_getobj\_\_ and self. + # + + ## + # :method: untrust + # Untrust both the object returned by \_\_getobj\_\_ and self. + # + + ## + # :method: taint + # Taint both the object returned by \_\_getobj\_\_ and self. + # + + ## + # :method: untaint + # Untaint both the object returned by \_\_getobj\_\_ and self. + # + + ## + # :method: freeze + # Freeze both the object returned by \_\_getobj\_\_ and self. + # + + [:trust, :untrust, :taint, :untaint, :freeze].each do |method| + define_method method do + __getobj__.send(method) + super() + end + end + + @delegator_api = self.public_instance_methods + def self.public_api # :nodoc: + @delegator_api + end +end + +## +# A concrete implementation of Delegator, this class provides the means to +# delegate all supported method calls to the object passed into the constructor +# and even to change the object being delegated to at a later time with +# #__setobj__. +# +# class User +# def born_on +# Date.new(1989, 09, 10) +# end +# end +# +# class UserDecorator < SimpleDelegator +# def birth_year +# born_on.year +# end +# end +# +# decorated_user = UserDecorator.new(User.new) +# decorated_user.birth_year #=> 1989 +# decorated_user.__getobj__ #=> # +# +# A SimpleDelegator instance can take advantage of the fact that SimpleDelegator +# is a subclass of +Delegator+ to call super to have methods called on +# the object being delegated to. +# +# class SuperArray < SimpleDelegator +# def [](*args) +# super + 1 +# end +# end +# +# SuperArray.new([1])[0] #=> 2 +# +# Here's a simple example that takes advantage of the fact that +# SimpleDelegator's delegation object can be changed at any time. +# +# class Stats +# def initialize +# @source = SimpleDelegator.new([]) +# end +# +# def stats(records) +# @source.__setobj__(records) +# +# "Elements: #{@source.size}\n" + +# " Non-Nil: #{@source.compact.size}\n" + +# " Unique: #{@source.uniq.size}\n" +# end +# end +# +# s = Stats.new +# puts s.stats(%w{James Edward Gray II}) +# puts +# puts s.stats([1, 2, 3, nil, 4, 5, 1, 2]) +# +# Prints: +# +# Elements: 4 +# Non-Nil: 4 +# Unique: 4 +# +# Elements: 8 +# Non-Nil: 7 +# Unique: 6 +# +class SimpleDelegator Edward + # names.__setobj__(%w{Gavin Sinclair}) + # puts names[1] # => Sinclair + # + def __setobj__(obj) + __raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj) + @delegate_sd_obj = obj + end +end + +def Delegator.delegating_block(mid) # :nodoc: + lambda do |*args, &block| + target = self.__getobj__ + begin + target.__send__(mid, *args, &block) + ensure + $@.delete_if {|t| /\A#{Regexp.quote(__FILE__)}:#{__LINE__-2}:/o =~ t} if $@ + end + end +end + +# +# The primary interface to this library. Use to setup delegation when defining +# your class. +# +# class MyClass < DelegateClass(ClassToDelegateTo) # Step 1 +# def initialize +# super(obj_of_ClassToDelegateTo) # Step 2 +# end +# end +# +# Here's a sample of use from Tempfile which is really a File object with a +# few special rules about storage location and when the File should be +# deleted. That makes for an almost textbook perfect example of how to use +# delegation. +# +# class Tempfile < DelegateClass(File) +# # constant and class member data initialization... +# +# def initialize(basename, tmpdir=Dir::tmpdir) +# # build up file path/name in var tmpname... +# +# @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600) +# +# # ... +# +# super(@tmpfile) +# +# # below this point, all methods of File are supported... +# end +# +# # ... +# end +# +def DelegateClass(superclass) + klass = Class.new(Delegator) + methods = superclass.instance_methods + methods -= ::Delegator.public_api + methods -= [:to_s,:inspect,:=~,:!~,:===] + klass.module_eval do + def __getobj__ # :nodoc: + unless defined?(@delegate_dc_obj) + return yield if block_given? + __raise__ ::ArgumentError, "not delegated" + end + @delegate_dc_obj + end + def __setobj__(obj) # :nodoc: + __raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj) + @delegate_dc_obj = obj + end + methods.each do |method| + define_method(method, Delegator.delegating_block(method)) + end + end + klass.define_singleton_method :public_instance_methods do |all=true| + super(all) - superclass.protected_instance_methods + end + klass.define_singleton_method :protected_instance_methods do |all=true| + super(all) | superclass.protected_instance_methods + end + return klass +end + +# :enddoc: + +if __FILE__ == $0 + class ExtArray true + p foo2.iter{[55,true]} # => true + foo2.error # raise error! +end diff --git a/ruby/lib/ruby/2.1.0/digest.rb b/ruby/lib/ruby/2.1.0/digest.rb new file mode 100644 index 0000000..5f7ebc2 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/digest.rb @@ -0,0 +1,90 @@ +require 'digest.so' + +module Digest + def self.const_missing(name) # :nodoc: + case name + when :SHA256, :SHA384, :SHA512 + lib = 'digest/sha2.so' + else + lib = File.join('digest', name.to_s.downcase) + end + + begin + require lib + rescue LoadError + raise LoadError, "library not found for class Digest::#{name} -- #{lib}", caller(1) + end + unless Digest.const_defined?(name) + raise NameError, "uninitialized constant Digest::#{name}", caller(1) + end + Digest.const_get(name) + end + + class ::Digest::Class + # Creates a digest object and reads a given file, _name_. + # Optional arguments are passed to the constructor of the digest + # class. + # + # p Digest::SHA256.file("X11R6.8.2-src.tar.bz2").hexdigest + # # => "f02e3c85572dc9ad7cb77c2a638e3be24cc1b5bea9fdbb0b0299c9668475c534" + def self.file(name, *args) + new(*args).file(name) + end + + # Returns the base64 encoded hash value of a given _string_. The + # return value is properly padded with '=' and contains no line + # feeds. + def self.base64digest(str, *args) + [digest(str, *args)].pack('m0') + end + end + + module Instance + # Updates the digest with the contents of a given file _name_ and + # returns self. + def file(name) + File.open(name, "rb") {|f| + buf = "" + while f.read(16384, buf) + update buf + end + } + self + end + + # If none is given, returns the resulting hash value of the digest + # in a base64 encoded form, keeping the digest's state. + # + # If a +string+ is given, returns the hash value for the given + # +string+ in a base64 encoded form, resetting the digest to the + # initial state before and after the process. + # + # In either case, the return value is properly padded with '=' and + # contains no line feeds. + def base64digest(str = nil) + [str ? digest(str) : digest].pack('m0') + end + + # Returns the resulting hash value and resets the digest to the + # initial state. + def base64digest! + [digest!].pack('m0') + end + end +end + +# call-seq: +# Digest(name) -> digest_subclass +# +# Returns a Digest subclass by +name+. +# +# require 'digest' +# +# Digest("MD5") +# # => Digest::MD5 +# +# Digest("Foo") +# # => LoadError: library not found for class Digest::Foo -- digest/foo +def Digest(name) + Digest.const_get(name) +end diff --git a/ruby/lib/ruby/2.1.0/digest/hmac.rb b/ruby/lib/ruby/2.1.0/digest/hmac.rb new file mode 100644 index 0000000..f505555 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/digest/hmac.rb @@ -0,0 +1,302 @@ +# == License +# +# Copyright (c) 2006 Akinori MUSHA +# +# Documentation by Akinori MUSHA +# +# All rights reserved. You can redistribute and/or modify it under +# the same terms as Ruby. +# +# $Id: hmac.rb 43668 2013-11-13 10:09:28Z zzak $ +# + +warn "use of the experimetal library 'digest/hmac' is discouraged; require 'openssl' and use OpenSSL::HMAC instead." if $VERBOSE + +require 'digest' + +module Digest + # = digest/hmac.rb + # + # An experimental implementation of HMAC keyed-hashing algorithm + # + # == Overview + # + # CAUTION: Use of this library is discouraged, because this + # implementation was meant to be experimental but somehow got into the + # 1.9 series without being noticed. Please use OpenSSL::HMAC in the + # "openssl" library instead. + # + # == Examples + # + # require 'digest/hmac' + # + # # one-liner example + # puts Digest::HMAC.hexdigest("data", "hash key", Digest::SHA1) + # + # # rather longer one + # hmac = Digest::HMAC.new("foo", Digest::RMD160) + # + # buf = "" + # while stream.read(16384, buf) + # hmac.update(buf) + # end + # + # puts hmac.hexdigest + # + class HMAC < Digest::Class + + # Creates a Digest::HMAC instance. + + def initialize(key, digester) + @md = digester.new + + block_len = @md.block_length + + if key.bytesize > block_len + key = @md.digest(key) + end + + ipad = Array.new(block_len, 0x36) + opad = Array.new(block_len, 0x5c) + + key.bytes.each_with_index { |c, i| + ipad[i] ^= c + opad[i] ^= c + } + + @key = key.freeze + @ipad = ipad.pack('C*').freeze + @opad = opad.pack('C*').freeze + @md.update(@ipad) + end + + def initialize_copy(other) # :nodoc: + @md = other.instance_eval { @md.clone } + end + + # call-seq: + # hmac.update(string) -> hmac + # hmac << string -> hmac + # + # Updates the hmac using a given +string+ and returns self. + def update(text) + @md.update(text) + self + end + alias << update + + # call-seq: + # hmac.reset -> hmac + # + # Resets the hmac to the initial state and returns self. + def reset + @md.reset + @md.update(@ipad) + self + end + + def finish # :nodoc: + d = @md.digest! + @md.update(@opad) + @md.update(d) + @md.digest! + end + private :finish + + # call-seq: + # hmac.digest_length -> Integer + # + # Returns the length in bytes of the hash value of the digest. + def digest_length + @md.digest_length + end + + # call-seq: + # hmac.block_length -> Integer + # + # Returns the block length in bytes of the hmac. + def block_length + @md.block_length + end + + # call-seq: + # hmac.inspect -> string + # + # Creates a printable version of the hmac object. + def inspect + sprintf('#<%s: key=%s, digest=%s>', self.class.name, @key.inspect, @md.inspect.sub(/^\#<(.*)>$/) { $1 }); + end + end +end + +if $0 == __FILE__ + eval DATA.gets(nil), nil, $0, DATA.lineno +end + +__END__ + +require 'test/unit' + +module TM_HMAC + def test_s_hexdigest + cases.each { |h| + digesters.each { |d| + assert_equal(h[:hexdigest], Digest::HMAC.hexdigest(h[:data], h[:key], d)) + } + } + end + + def test_hexdigest + cases.each { |h| + digesters.each { |d| + hmac = Digest::HMAC.new(h[:key], d) + + hmac.update(h[:data]) + + assert_equal(h[:hexdigest], hmac.hexdigest) + } + } + end + + def test_reset + cases.each { |h| + digesters.each { |d| + hmac = Digest::HMAC.new(h[:key], d) + hmac.update("test") + hmac.reset + hmac.update(h[:data]) + + assert_equal(h[:hexdigest], hmac.hexdigest) + } + } + end +end + +class TC_HMAC_MD5 < Test::Unit::TestCase + include TM_HMAC + + def digesters + [Digest::MD5, Digest::MD5.new] + end + + # Taken from RFC 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1 + def cases + [ + { + :key => "\x0b" * 16, + :data => "Hi There", + :hexdigest => "9294727a3638bb1c13f48ef8158bfc9d", + }, { + :key => "Jefe", + :data => "what do ya want for nothing?", + :hexdigest => "750c783e6ab0b503eaa86e310a5db738", + }, { + :key => "\xaa" * 16, + :data => "\xdd" * 50, + :hexdigest => "56be34521d144c88dbb8c733f0e8b3f6", + }, { + :key => "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", + :data => "\xcd" * 50, + :hexdigest => "697eaf0aca3a3aea3a75164746ffaa79", + }, { + :key => "\x0c" * 16, + :data => "Test With Truncation", + :hexdigest => "56461ef2342edc00f9bab995690efd4c", + }, { + :key => "\xaa" * 80, + :data => "Test Using Larger Than Block-Size Key - Hash Key First", + :hexdigest => "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd", + }, { + :key => "\xaa" * 80, + :data => "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", + :hexdigest => "6f630fad67cda0ee1fb1f562db3aa53e", + } + ] + end +end + +class TC_HMAC_SHA1 < Test::Unit::TestCase + include TM_HMAC + + def digesters + [Digest::SHA1, Digest::SHA1.new] + end + + # Taken from RFC 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1 + def cases + [ + { + :key => "\x0b" * 20, + :data => "Hi There", + :hexdigest => "b617318655057264e28bc0b6fb378c8ef146be00", + }, { + :key => "Jefe", + :data => "what do ya want for nothing?", + :hexdigest => "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", + }, { + :key => "\xaa" * 20, + :data => "\xdd" * 50, + :hexdigest => "125d7342b9ac11cd91a39af48aa17b4f63f175d3", + }, { + :key => "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", + :data => "\xcd" * 50, + :hexdigest => "4c9007f4026250c6bc8414f9bf50c86c2d7235da", + }, { + :key => "\x0c" * 20, + :data => "Test With Truncation", + :hexdigest => "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04", + }, { + :key => "\xaa" * 80, + :data => "Test Using Larger Than Block-Size Key - Hash Key First", + :hexdigest => "aa4ae5e15272d00e95705637ce8a3b55ed402112", + }, { + :key => "\xaa" * 80, + :data => "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", + :hexdigest => "e8e99d0f45237d786d6bbaa7965c7808bbff1a91", + } + ] + end +end + +class TC_HMAC_RMD160 < Test::Unit::TestCase + include TM_HMAC + + def digesters + [Digest::RMD160, Digest::RMD160.new] + end + + # Taken from RFC 2286: Test Cases for HMAC-RIPEMD160 and HMAC-RIPEMD128 + def cases + [ + { + :key => "\x0b" * 20, + :data => "Hi There", + :hexdigest => "24cb4bd67d20fc1a5d2ed7732dcc39377f0a5668", + }, { + :key => "Jefe", + :data => "what do ya want for nothing?", + :hexdigest => "dda6c0213a485a9e24f4742064a7f033b43c4069", + }, { + :key => "\xaa" * 20, + :data => "\xdd" * 50, + :hexdigest => "b0b105360de759960ab4f35298e116e295d8e7c1", + }, { + :key => "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", + :data => "\xcd" * 50, + :hexdigest => "d5ca862f4d21d5e610e18b4cf1beb97a4365ecf4", + }, { + :key => "\x0c" * 20, + :data => "Test With Truncation", + :hexdigest => "7619693978f91d90539ae786500ff3d8e0518e39", + }, { + :key => "\xaa" * 80, + :data => "Test Using Larger Than Block-Size Key - Hash Key First", + :hexdigest => "6466ca07ac5eac29e1bd523e5ada7605b791fd8b", + }, { + :key => "\xaa" * 80, + :data => "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", + :hexdigest => "69ea60798d71616cce5fd0871e23754cd75d5a0a", + } + ] + end +end diff --git a/ruby/lib/ruby/2.1.0/digest/sha2.rb b/ruby/lib/ruby/2.1.0/digest/sha2.rb new file mode 100644 index 0000000..fffc1f4 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/digest/sha2.rb @@ -0,0 +1,107 @@ +#-- +# sha2.rb - defines Digest::SHA2 class which wraps up the SHA256, +# SHA384, and SHA512 classes. +#++ +# Copyright (c) 2006 Akinori MUSHA +# +# All rights reserved. You can redistribute and/or modify it under the same +# terms as Ruby. +# +# $Id: sha2.rb 35293 2012-04-10 22:41:04Z drbrain $ + +require 'digest' +require 'digest/sha2.so' + +module Digest + # + # A meta digest provider class for SHA256, SHA384 and SHA512. + # + class SHA2 < Digest::Class + # call-seq: + # Digest::SHA2.new(bitlen = 256) -> digest_obj + # + # Creates a new SHA2 hash object with a given bit length. + # + # Valid bit lengths are 256, 384 and 512. + def initialize(bitlen = 256) + case bitlen + when 256 + @sha2 = Digest::SHA256.new + when 384 + @sha2 = Digest::SHA384.new + when 512 + @sha2 = Digest::SHA512.new + else + raise ArgumentError, "unsupported bit length: %s" % bitlen.inspect + end + @bitlen = bitlen + end + + # call-seq: + # digest_obj.reset -> digest_obj + # + # Resets the digest to the initial state and returns self. + def reset + @sha2.reset + self + end + + # call-seq: + # digest_obj.update(string) -> digest_obj + # digest_obj << string -> digest_obj + # + # Updates the digest using a given _string_ and returns self. + def update(str) + @sha2.update(str) + self + end + alias << update + + def finish # :nodoc: + @sha2.digest! + end + private :finish + + + # call-seq: + # digest_obj.block_length -> Integer + # + # Returns the block length of the digest in bytes. + # + # Digest::SHA256.new.block_length * 8 + # # => 512 + # Digest::SHA384.new.block_length * 8 + # # => 1024 + # Digest::SHA512.new.block_length * 8 + # # => 1024 + def block_length + @sha2.block_length + end + + # call-seq: + # digest_obj.digest_length -> Integer + # + # Returns the length of the hash value of the digest in bytes. + # + # Digest::SHA256.new.digest_length * 8 + # # => 256 + # Digest::SHA384.new.digest_length * 8 + # # => 384 + # Digest::SHA512.new.digest_length * 8 + # # => 512 + # + # For example, digests produced by Digest::SHA256 will always be 32 bytes + # (256 bits) in size. + def digest_length + @sha2.digest_length + end + + def initialize_copy(other) # :nodoc: + @sha2 = other.instance_eval { @sha2.clone } + end + + def inspect # :nodoc: + "#<%s:%d %s>" % [self.class.name, @bitlen, hexdigest] + end + end +end diff --git a/ruby/lib/ruby/2.1.0/dl.rb b/ruby/lib/ruby/2.1.0/dl.rb new file mode 100644 index 0000000..8e615ae --- /dev/null +++ b/ruby/lib/ruby/2.1.0/dl.rb @@ -0,0 +1,15 @@ +require 'dl.so' + +begin + require 'fiddle' unless Object.const_defined?(:Fiddle) +rescue LoadError +end + +warn "DL is deprecated, please use Fiddle" + +module DL + # Returns true if DL is using Fiddle, the libffi wrapper. + def self.fiddle? + Object.const_defined?(:Fiddle) + end +end diff --git a/ruby/lib/ruby/2.1.0/dl/callback.rb b/ruby/lib/ruby/2.1.0/dl/callback.rb new file mode 100644 index 0000000..1722d3c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/dl/callback.rb @@ -0,0 +1,112 @@ +require 'dl' +require 'thread' + +module DL + # The mutual exclusion (Mutex) semaphore for the DL module + SEM = Mutex.new # :nodoc: + + if DL.fiddle? + # A Hash of callback Procs + # + # Uses Fiddle + CdeclCallbackProcs = {} # :nodoc: + + # A Hash of the addresses of callback Proc + # + # Uses Fiddle + CdeclCallbackAddrs = {} # :nodoc: + + # A Hash of Stdcall callback Procs + # + # Uses Fiddle on win32 + StdcallCallbackProcs = {} # :nodoc: + + # A Hash of the addresses of Stdcall callback Procs + # + # Uses Fiddle on win32 + StdcallCallbackAddrs = {} # :nodoc: + end + + def set_callback_internal(proc_entry, addr_entry, argc, ty, abi = nil, &cbp) + if( argc < 0 ) + raise(ArgumentError, "arity should not be less than 0.") + end + addr = nil + + if DL.fiddle? + abi ||= Fiddle::Function::DEFAULT + closure = Fiddle::Closure::BlockCaller.new(ty, [TYPE_VOIDP] * argc, abi, &cbp) + proc_entry[closure.to_i] = closure + addr = closure.to_i + else + SEM.synchronize{ + ary = proc_entry[ty] + (0...MAX_CALLBACK).each{|n| + idx = (n * DLSTACK_SIZE) + argc + if( ary[idx].nil? ) + ary[idx] = cbp + addr = addr_entry[ty][idx] + break + end + } + } + end + + addr + end + + def set_cdecl_callback(ty, argc, &cbp) + set_callback_internal(CdeclCallbackProcs, CdeclCallbackAddrs, argc, ty, &cbp) + end + + def set_stdcall_callback(ty, argc, &cbp) + if DL.fiddle? + set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, Fiddle::Function::STDCALL, &cbp) + else + set_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, argc, ty, &cbp) + end + end + + def remove_callback_internal(proc_entry, addr_entry, addr, ctype = nil) + if DL.fiddle? + addr = addr.to_i + return false unless proc_entry.key?(addr) + proc_entry.delete(addr) + true + else + index = nil + if( ctype ) + addr_entry[ctype].each_with_index{|xaddr, idx| + if( xaddr == addr ) + index = idx + end + } + else + addr_entry.each{|ty,entry| + entry.each_with_index{|xaddr, idx| + if( xaddr == addr ) + index = idx + end + } + } + end + if( index and proc_entry[ctype][index] ) + proc_entry[ctype][index] = nil + return true + else + return false + end + end + end + + def remove_cdecl_callback(addr, ctype = nil) + remove_callback_internal(CdeclCallbackProcs, CdeclCallbackAddrs, addr, ctype) + end + + def remove_stdcall_callback(addr, ctype = nil) + remove_callback_internal(StdcallCallbackProcs, StdcallCallbackAddrs, addr, ctype) + end + + alias set_callback set_cdecl_callback + alias remove_callback remove_cdecl_callback +end diff --git a/ruby/lib/ruby/2.1.0/dl/cparser.rb b/ruby/lib/ruby/2.1.0/dl/cparser.rb new file mode 100644 index 0000000..e70e0f1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/dl/cparser.rb @@ -0,0 +1,156 @@ +module DL + # Methods for parsing C struct and C prototype signatures. + module CParser + # Parses a C struct's members + # + # Example: + # + # parse_struct_signature(['int i', 'char c']) + # => [[DL::TYPE_INT, DL::TYPE_CHAR], ["i", "c"]] + # + def parse_struct_signature(signature, tymap=nil) + if( signature.is_a?(String) ) + signature = signature.split(/\s*,\s*/) + end + mems = [] + tys = [] + signature.each{|msig| + tks = msig.split(/\s+(\*)?/) + ty = tks[0..-2].join(" ") + member = tks[-1] + + case ty + when /\[(\d+)\]/ + n = $1.to_i + ty.gsub!(/\s*\[\d+\]/,"") + ty = [ty, n] + when /\[\]/ + ty.gsub!(/\s*\[\]/, "*") + end + + case member + when /\[(\d+)\]/ + ty = [ty, $1.to_i] + member.gsub!(/\s*\[\d+\]/,"") + when /\[\]/ + ty = ty + "*" + member.gsub!(/\s*\[\]/, "") + end + + mems.push(member) + tys.push(parse_ctype(ty,tymap)) + } + return tys, mems + end + + # Parses a C prototype signature + # + # Example: + # + # include DL::CParser + # => Object + # + # parse_signature('double sum(double, double)') + # => ["sum", DL::TYPE_DOUBLE, [DL::TYPE_DOUBLE, DL::TYPE_DOUBLE]] + # + def parse_signature(signature, tymap=nil) + tymap ||= {} + signature = signature.gsub(/\s+/, " ").strip + case signature + when /^([\w@\*\s]+)\(([\w\*\s\,\[\]]*)\)$/ + ret = $1 + (args = $2).strip! + ret = ret.split(/\s+/) + args = args.split(/\s*,\s*/) + func = ret.pop + if( func =~ /^\*/ ) + func.gsub!(/^\*+/,"") + ret.push("*") + end + ret = ret.join(" ") + return [func, parse_ctype(ret, tymap), args.collect{|arg| parse_ctype(arg, tymap)}] + else + raise(RuntimeError,"can't parse the function prototype: #{signature}") + end + end + + # Given a String of C type +ty+, return the corresponding DL constant. + # + # +ty+ can also accept an Array of C type Strings, and will returned in a + # corresponding Array. + # + # If Hash +tymap+ is provided, +ty+ is expected to be the key, and the + # value will be the C type to be looked up. + # + # Example: + # + # parse_ctype('int') + # => DL::TYPE_INT + # + # parse_ctype('double') + # => DL::TYPE_DOUBLE + # + # parse_ctype('unsigned char') + # => -DL::TYPE_CHAR + # + def parse_ctype(ty, tymap=nil) + tymap ||= {} + case ty + when Array + return [parse_ctype(ty[0], tymap), ty[1]] + when "void" + return TYPE_VOID + when "char" + return TYPE_CHAR + when "unsigned char" + return -TYPE_CHAR + when "short" + return TYPE_SHORT + when "unsigned short" + return -TYPE_SHORT + when "int" + return TYPE_INT + when "unsigned int", 'uint' + return -TYPE_INT + when "long" + return TYPE_LONG + when "unsigned long" + return -TYPE_LONG + when "long long" + if( defined?(TYPE_LONG_LONG) ) + return TYPE_LONG_LONG + else + raise(RuntimeError, "unsupported type: #{ty}") + end + when "unsigned long long" + if( defined?(TYPE_LONG_LONG) ) + return -TYPE_LONG_LONG + else + raise(RuntimeError, "unsupported type: #{ty}") + end + when "float" + return TYPE_FLOAT + when "double" + return TYPE_DOUBLE + when "size_t" + return TYPE_SIZE_T + when "ssize_t" + return TYPE_SSIZE_T + when "ptrdiff_t" + return TYPE_PTRDIFF_T + when "intptr_t" + return TYPE_INTPTR_T + when "uintptr_t" + return TYPE_UINTPTR_T + when /\*/, /\[\s*\]/ + return TYPE_VOIDP + else + if( tymap[ty] ) + return parse_ctype(tymap[ty], tymap) + else + raise(DLError, "unknown type: #{ty}") + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/dl/func.rb b/ruby/lib/ruby/2.1.0/dl/func.rb new file mode 100644 index 0000000..543711f --- /dev/null +++ b/ruby/lib/ruby/2.1.0/dl/func.rb @@ -0,0 +1,251 @@ +require 'dl' +require 'dl/callback' +require 'dl/stack' +require 'dl/value' +require 'thread' + +module DL + parent = DL.fiddle? ? Fiddle::Function : Object + + class Function < parent + include DL + include ValueUtil + + if DL.fiddle? + # :stopdoc: + CALL_TYPE_TO_ABI = Hash.new { |h, k| + raise RuntimeError, "unsupported call type: #{k}" + }.merge({ :stdcall => + (Fiddle::Function::STDCALL rescue Fiddle::Function::DEFAULT), + :cdecl => Fiddle::Function::DEFAULT, + nil => Fiddle::Function::DEFAULT + }).freeze + private_constant :CALL_TYPE_TO_ABI + # :startdoc: + + def self.call_type_to_abi(call_type) # :nodoc: + CALL_TYPE_TO_ABI[call_type] + end + private_class_method :call_type_to_abi + + class FiddleClosureCFunc < Fiddle::Closure # :nodoc: all + def initialize ctype, arg, abi, name + @name = name + super(ctype, arg, abi) + end + def name + @name + end + def ptr + to_i + end + end + private_constant :FiddleClosureCFunc + + def self.class_fiddle_closure_cfunc # :nodoc: + FiddleClosureCFunc + end + private_class_method :class_fiddle_closure_cfunc + end + + def initialize cfunc, argtypes, abi = nil, &block + if DL.fiddle? + abi ||= CALL_TYPE_TO_ABI[(cfunc.calltype rescue nil)] + if block_given? + @cfunc = Class.new(FiddleClosureCFunc) { + define_method(:call, block) + }.new(cfunc.ctype, argtypes, abi, cfunc.name) + else + @cfunc = cfunc + end + + @args = argtypes + super(@cfunc, @args.reject { |x| x == TYPE_VOID }, cfunc.ctype, abi) + else + @cfunc = cfunc + @stack = Stack.new(argtypes.collect{|ty| ty.abs}) + if( @cfunc.ctype < 0 ) + @cfunc.ctype = @cfunc.ctype.abs + @unsigned = true + else + @unsigned = false + end + if block_given? + bind(&block) + end + end + end + + def to_i() + @cfunc.to_i + end + + def name + @cfunc.name + end + + def call(*args, &block) + if DL.fiddle? + if block_given? + args.find { |a| DL::Function === a }.bind_at_call(&block) + end + super + else + funcs = [] + if $SAFE >= 1 && args.any? { |x| x.tainted? } + raise SecurityError, "tainted parameter not allowed" + end + _args = wrap_args(args, @stack.types, funcs, &block) + r = @cfunc.call(@stack.pack(_args)) + funcs.each{|f| f.unbind_at_call()} + return wrap_result(r) + end + end + + def wrap_result(r) + case @cfunc.ctype + when TYPE_VOIDP + r = CPtr.new(r) + else + if( @unsigned ) + r = unsigned_value(r, @cfunc.ctype) + end + end + r + end + + def bind(&block) + if DL.fiddle? + @cfunc = Class.new(FiddleClosureCFunc) { + def initialize ctype, args, abi, name, block + super(ctype, args, abi, name) + @block = block + end + + def call *args + @block.call(*args) + end + }.new(@cfunc.ctype, @args, abi, name, block) + @ptr = @cfunc + return nil + else + if( !block ) + raise(RuntimeError, "block must be given.") + end + unless block.lambda? + block = Class.new(self.class){define_method(:call, block); def initialize(obj); obj.instance_variables.each{|s| instance_variable_set(s, obj.instance_variable_get(s))}; end}.new(self).method(:call) + end + if( @cfunc.ptr == 0 ) + cb = Proc.new{|*args| + ary = @stack.unpack(args) + @stack.types.each_with_index{|ty, idx| + case ty + when TYPE_VOIDP + ary[idx] = CPtr.new(ary[idx]) + end + } + r = block.call(*ary) + wrap_arg(r, @cfunc.ctype, []) + } + case @cfunc.calltype + when :cdecl + @cfunc.ptr = set_cdecl_callback(@cfunc.ctype, @stack.size, &cb) + when :stdcall + @cfunc.ptr = set_stdcall_callback(@cfunc.ctype, @stack.size, &cb) + else + raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}") + end + if( @cfunc.ptr == 0 ) + raise(RuntimeException, "can't bind C function.") + end + end + end + end + + def unbind() + if DL.fiddle? then + if @cfunc.kind_of?(Fiddle::Closure) and @cfunc.ptr != 0 then + call_type = case abi + when CALL_TYPE_TO_ABI[nil] + nil + when CALL_TYPE_TO_ABI[:stdcall] + :stdcall + else + raise(RuntimeError, "unsupported abi: #{abi}") + end + @cfunc = CFunc.new(0, @cfunc.ctype, name, call_type) + return 0 + elsif @cfunc.ptr != 0 then + @cfunc.ptr = 0 + return 0 + else + return nil + end + end + if( @cfunc.ptr != 0 ) + case @cfunc.calltype + when :cdecl + remove_cdecl_callback(@cfunc.ptr, @cfunc.ctype) + when :stdcall + remove_stdcall_callback(@cfunc.ptr, @cfunc.ctype) + else + raise(RuntimeError, "unsupported calltype: #{@cfunc.calltype}") + end + @cfunc.ptr = 0 + end + end + + def bound?() + @cfunc.ptr != 0 + end + + def bind_at_call(&block) + bind(&block) + end + + def unbind_at_call() + end + end + + class TempFunction < Function + def bind_at_call(&block) + bind(&block) + end + + def unbind_at_call() + unbind() + end + end + + class CarriedFunction < Function + def initialize(cfunc, argtypes, n) + super(cfunc, argtypes) + @carrier = [] + @index = n + @mutex = Mutex.new + end + + def create_carrier(data) + ary = [] + userdata = [ary, data] + @mutex.lock() + @carrier.push(userdata) + return dlwrap(userdata) + end + + def bind_at_call(&block) + userdata = @carrier[-1] + userdata[0].push(block) + bind{|*args| + ptr = args[@index] + if( !ptr ) + raise(RuntimeError, "The index of userdata should be lower than #{args.size}.") + end + userdata = dlunwrap(Integer(ptr)) + args[@index] = userdata[1] + userdata[0][0].call(*args) + } + @mutex.unlock() + end + end +end diff --git a/ruby/lib/ruby/2.1.0/dl/import.rb b/ruby/lib/ruby/2.1.0/dl/import.rb new file mode 100644 index 0000000..6f157cc --- /dev/null +++ b/ruby/lib/ruby/2.1.0/dl/import.rb @@ -0,0 +1,268 @@ +require 'dl' +require 'dl/func.rb' +require 'dl/struct.rb' +require 'dl/cparser.rb' + +module DL + class CompositeHandler + def initialize(handlers) + @handlers = handlers + end + + def handlers() + @handlers + end + + def sym(symbol) + @handlers.each{|handle| + if( handle ) + begin + addr = handle.sym(symbol) + return addr + rescue DLError + end + end + } + return nil + end + + def [](symbol) + sym(symbol) + end + end + + # DL::Importer includes the means to dynamically load libraries and build + # modules around them including calling extern functions within the C + # library that has been loaded. + # + # == Example + # + # require 'dl' + # require 'dl/import' + # + # module LibSum + # extend DL::Importer + # dlload './libsum.so' + # extern 'double sum(double*, int)' + # extern 'double split(double)' + # end + # + module Importer + include DL + include CParser + extend Importer + + def dlload(*libs) + handles = libs.collect{|lib| + case lib + when nil + nil + when Handle + lib + when Importer + lib.handlers + else + begin + DL.dlopen(lib) + rescue DLError + raise(DLError, "can't load #{lib}") + end + end + }.flatten() + @handler = CompositeHandler.new(handles) + @func_map = {} + @type_alias = {} + end + + def typealias(alias_type, orig_type) + @type_alias[alias_type] = orig_type + end + + def sizeof(ty) + @type_alias ||= nil + case ty + when String + ty = parse_ctype(ty, @type_alias).abs() + case ty + when TYPE_CHAR + return SIZEOF_CHAR + when TYPE_SHORT + return SIZEOF_SHORT + when TYPE_INT + return SIZEOF_INT + when TYPE_LONG + return SIZEOF_LONG + when TYPE_LONG_LONG + return SIZEOF_LONG_LON + when TYPE_FLOAT + return SIZEOF_FLOAT + when TYPE_DOUBLE + return SIZEOF_DOUBLE + when TYPE_VOIDP + return SIZEOF_VOIDP + else + raise(DLError, "unknown type: #{ty}") + end + when Class + if( ty.instance_methods().include?(:to_ptr) ) + return ty.size() + end + end + return CPtr[ty].size() + end + + def parse_bind_options(opts) + h = {} + while( opt = opts.shift() ) + case opt + when :stdcall, :cdecl + h[:call_type] = opt + when :carried, :temp, :temporal, :bind + h[:callback_type] = opt + h[:carrier] = opts.shift() + else + h[opt] = true + end + end + h + end + private :parse_bind_options + + def extern(signature, *opts) + @type_alias ||= nil + symname, ctype, argtype = parse_signature(signature, @type_alias) + opt = parse_bind_options(opts) + f = import_function(symname, ctype, argtype, opt[:call_type]) + name = symname.gsub(/@.+/,'') + @func_map[name] = f + # define_method(name){|*args,&block| f.call(*args,&block)} + begin + /^(.+?):(\d+)/ =~ caller.first + file, line = $1, $2.to_i + rescue + file, line = __FILE__, __LINE__+3 + end + module_eval(<<-EOS, file, line) + def #{name}(*args, &block) + @func_map['#{name}'].call(*args,&block) + end + EOS + module_function(name) + f + end + + def bind(signature, *opts, &blk) + @type_alias ||= nil + name, ctype, argtype = parse_signature(signature, @type_alias) + h = parse_bind_options(opts) + case h[:callback_type] + when :bind, nil + f = bind_function(name, ctype, argtype, h[:call_type], &blk) + when :temp, :temporal + f = create_temp_function(name, ctype, argtype, h[:call_type]) + when :carried + f = create_carried_function(name, ctype, argtype, h[:call_type], h[:carrier]) + else + raise(RuntimeError, "unknown callback type: #{h[:callback_type]}") + end + @func_map[name] = f + #define_method(name){|*args,&block| f.call(*args,&block)} + begin + /^(.+?):(\d+)/ =~ caller.first + file, line = $1, $2.to_i + rescue + file, line = __FILE__, __LINE__+3 + end + module_eval(<<-EOS, file, line) + def #{name}(*args,&block) + @func_map['#{name}'].call(*args,&block) + end + EOS + module_function(name) + f + end + + # Creates a class to wrap the C struct described by +signature+. + # + # MyStruct = struct ['int i', 'char c'] + def struct(signature) + @type_alias ||= nil + tys, mems = parse_struct_signature(signature, @type_alias) + DL::CStructBuilder.create(CStruct, tys, mems) + end + + # Creates a class to wrap the C union described by +signature+. + # + # MyUnion = union ['int i', 'char c'] + def union(signature) + @type_alias ||= nil + tys, mems = parse_struct_signature(signature, @type_alias) + DL::CStructBuilder.create(CUnion, tys, mems) + end + + def [](name) + @func_map[name] + end + + def create_value(ty, val=nil) + s = struct([ty + " value"]) + ptr = s.malloc() + if( val ) + ptr.value = val + end + return ptr + end + alias value create_value + + def import_value(ty, addr) + s = struct([ty + " value"]) + ptr = s.new(addr) + return ptr + end + + def handler + defined?(@handler) or raise "call dlload before importing symbols and functions" + @handler + end + + def import_symbol(name) + addr = handler.sym(name) + if( !addr ) + raise(DLError, "cannot find the symbol: #{name}") + end + CPtr.new(addr) + end + + def import_function(name, ctype, argtype, call_type = nil) + addr = handler.sym(name) + if( !addr ) + raise(DLError, "cannot find the function: #{name}()") + end + Function.new(CFunc.new(addr, ctype, name, call_type || :cdecl), argtype) + end + + def bind_function(name, ctype, argtype, call_type = nil, &block) + if DL.fiddle? + klass = Function.instance_eval { class_fiddle_closure_cfunc } + abi = Function.instance_eval { call_type_to_abi(call_type) } + closure = Class.new(klass) { + define_method(:call, block) + }.new(ctype, argtype, abi, name) + + Function.new(closure, argtype, abi) + else + f = Function.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype) + f.bind(&block) + f + end + end + + def create_temp_function(name, ctype, argtype, call_type = nil) + TempFunction.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype) + end + + def create_carried_function(name, ctype, argtype, call_type = nil, n = 0) + CarriedFunction.new(CFunc.new(0, ctype, name, call_type || :cdecl), argtype, n) + end + end +end diff --git a/ruby/lib/ruby/2.1.0/dl/pack.rb b/ruby/lib/ruby/2.1.0/dl/pack.rb new file mode 100644 index 0000000..7fbc802 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/dl/pack.rb @@ -0,0 +1,128 @@ +require 'dl' + +module DL + module PackInfo + ALIGN_MAP = { + TYPE_VOIDP => ALIGN_VOIDP, + TYPE_CHAR => ALIGN_CHAR, + TYPE_SHORT => ALIGN_SHORT, + TYPE_INT => ALIGN_INT, + TYPE_LONG => ALIGN_LONG, + TYPE_FLOAT => ALIGN_FLOAT, + TYPE_DOUBLE => ALIGN_DOUBLE, + -TYPE_CHAR => ALIGN_CHAR, + -TYPE_SHORT => ALIGN_SHORT, + -TYPE_INT => ALIGN_INT, + -TYPE_LONG => ALIGN_LONG, + } + + PACK_MAP = { + TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG) ? "q" : "l!"), + TYPE_CHAR => "c", + TYPE_SHORT => "s!", + TYPE_INT => "i!", + TYPE_LONG => "l!", + TYPE_FLOAT => "f", + TYPE_DOUBLE => "d", + -TYPE_CHAR => "c", + -TYPE_SHORT => "s!", + -TYPE_INT => "i!", + -TYPE_LONG => "l!", + } + + SIZE_MAP = { + TYPE_VOIDP => SIZEOF_VOIDP, + TYPE_CHAR => SIZEOF_CHAR, + TYPE_SHORT => SIZEOF_SHORT, + TYPE_INT => SIZEOF_INT, + TYPE_LONG => SIZEOF_LONG, + TYPE_FLOAT => SIZEOF_FLOAT, + TYPE_DOUBLE => SIZEOF_DOUBLE, + -TYPE_CHAR => SIZEOF_CHAR, + -TYPE_SHORT => SIZEOF_SHORT, + -TYPE_INT => SIZEOF_INT, + -TYPE_LONG => SIZEOF_LONG, + } + if defined?(TYPE_LONG_LONG) + ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG + PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q" + SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG + end + + def align(addr, align) + d = addr % align + if( d == 0 ) + addr + else + addr + (align - d) + end + end + module_function :align + end + + class Packer + include PackInfo + + def self.[](*types) + new(types) + end + + def initialize(types) + parse_types(types) + end + + def size() + @size + end + + def pack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.pack(@template) + when SIZEOF_LONG_LONG + ary.pack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + + def unpack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.join().unpack(@template) + when SIZEOF_LONG_LONG + ary.join().unpack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + + private + + def parse_types(types) + @template = "" + addr = 0 + types.each{|t| + orig_addr = addr + if( t.is_a?(Array) ) + addr = align(orig_addr, ALIGN_MAP[TYPE_VOIDP]) + else + addr = align(orig_addr, ALIGN_MAP[t]) + end + d = addr - orig_addr + if( d > 0 ) + @template << "x#{d}" + end + if( t.is_a?(Array) ) + @template << (PACK_MAP[t[0]] * t[1]) + addr += (SIZE_MAP[t[0]] * t[1]) + else + @template << PACK_MAP[t] + addr += SIZE_MAP[t] + end + } + addr = align(addr, ALIGN_MAP[TYPE_VOIDP]) + @size = addr + end + end +end diff --git a/ruby/lib/ruby/2.1.0/dl/stack.rb b/ruby/lib/ruby/2.1.0/dl/stack.rb new file mode 100644 index 0000000..dc22378 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/dl/stack.rb @@ -0,0 +1,116 @@ +require 'dl' + +module DL + class Stack + def self.[](*types) + new(types) + end + + def initialize(types) + parse_types(types) + end + + def size() + @size + end + + def types() + @types + end + + def pack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.pack(@template).unpack('l!*') + when SIZEOF_LONG_LONG + ary.pack(@template).unpack('q*') + else + raise(RuntimeError, "sizeof(void*)?") + end + end + + def unpack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.pack('l!*').unpack(@template) + when SIZEOF_LONG_LONG + ary.pack('q*').unpack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + + private + + def align(addr, align) + d = addr % align + if( d == 0 ) + addr + else + addr + (align - d) + end + end + + ALIGN_MAP = { + TYPE_VOIDP => ALIGN_VOIDP, + TYPE_CHAR => ALIGN_VOIDP, + TYPE_SHORT => ALIGN_VOIDP, + TYPE_INT => ALIGN_VOIDP, + TYPE_LONG => ALIGN_VOIDP, + TYPE_FLOAT => ALIGN_FLOAT, + TYPE_DOUBLE => ALIGN_DOUBLE, + } + + PACK_MAP = { + TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG)? "q" : "l!"), + TYPE_CHAR => "c", + TYPE_SHORT => "s!", + TYPE_INT => "i!", + TYPE_LONG => "l!", + TYPE_FLOAT => "f", + TYPE_DOUBLE => "d", + } + + SIZE_MAP = { + TYPE_VOIDP => SIZEOF_VOIDP, + TYPE_CHAR => SIZEOF_CHAR, + TYPE_SHORT => SIZEOF_SHORT, + TYPE_INT => SIZEOF_INT, + TYPE_LONG => SIZEOF_LONG, + TYPE_FLOAT => SIZEOF_FLOAT, + TYPE_DOUBLE => SIZEOF_DOUBLE, + } + if defined?(TYPE_LONG_LONG) + ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_LONG_LONG + PACK_MAP[TYPE_LONG_LONG] = "q" + SIZE_MAP[TYPE_LONG_LONG] = SIZEOF_LONG_LONG + end + + def parse_types(types) + @types = types + @template = "" + addr = 0 + types.each{|t| + addr = add_padding(addr, ALIGN_MAP[t]) + @template << PACK_MAP[t] + addr += SIZE_MAP[t] + } + addr = add_padding(addr, ALIGN_MAP[SIZEOF_VOIDP]) + if( addr % SIZEOF_VOIDP == 0 ) + @size = addr / SIZEOF_VOIDP + else + @size = (addr / SIZEOF_VOIDP) + 1 + end + end + + def add_padding(addr, align) + orig_addr = addr + addr = align(orig_addr, align) + d = addr - orig_addr + if( d > 0 ) + @template << "x#{d}" + end + addr + end + end +end diff --git a/ruby/lib/ruby/2.1.0/dl/struct.rb b/ruby/lib/ruby/2.1.0/dl/struct.rb new file mode 100644 index 0000000..e2d91a6 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/dl/struct.rb @@ -0,0 +1,236 @@ +require 'dl' +require 'dl/value' +require 'dl/pack.rb' + +module DL + # C struct shell + class CStruct + # accessor to DL::CStructEntity + def CStruct.entity_class() + CStructEntity + end + end + + # C union shell + class CUnion + # accessor to DL::CUnionEntity + def CUnion.entity_class() + CUnionEntity + end + end + + # Used to construct C classes (CUnion, CStruct, etc) + # + # DL::Importer#struct and DL::Importer#union wrap this functionality in an + # easy-to-use manner. + module CStructBuilder + # Construct a new class given a C: + # * class +klass+ (CUnion, CStruct, or other that provide an + # #entity_class) + # * +types+ (DL:TYPE_INT, DL::TYPE_SIZE_T, etc., see the C types + # constants) + # * corresponding +members+ + # + # DL::Importer#struct and DL::Importer#union wrap this functionality in an + # easy-to-use manner. + # + # Example: + # + # require 'dl/struct' + # require 'dl/cparser' + # + # include DL::CParser + # + # types, members = parse_struct_signature(['int i','char c']) + # + # MyStruct = DL::CStructBuilder.create(CUnion, types, members) + # + # obj = MyStruct.allocate + # + def create(klass, types, members) + new_class = Class.new(klass){ + define_method(:initialize){|addr| + @entity = klass.entity_class.new(addr, types) + @entity.assign_names(members) + } + define_method(:to_ptr){ @entity } + define_method(:to_i){ @entity.to_i } + members.each{|name| + define_method(name){ @entity[name] } + define_method(name + "="){|val| @entity[name] = val } + } + } + size = klass.entity_class.size(types) + new_class.module_eval(<<-EOS, __FILE__, __LINE__+1) + def new_class.size() + #{size} + end + def new_class.malloc() + addr = DL.malloc(#{size}) + new(addr) + end + EOS + return new_class + end + module_function :create + end + + # A C struct wrapper + class CStructEntity < (DL.fiddle? ? Fiddle::Pointer : CPtr) + include PackInfo + include ValueUtil + + # Allocates a C struct the +types+ provided. The C function +func+ is + # called when the instance is garbage collected. + def CStructEntity.malloc(types, func = nil) + addr = DL.malloc(CStructEntity.size(types)) + CStructEntity.new(addr, types, func) + end + + # Given +types+, returns the offset for the packed sizes of those types + # + # DL::CStructEntity.size([DL::TYPE_DOUBLE, DL::TYPE_INT, DL::TYPE_CHAR, + # DL::TYPE_VOIDP]) + # => 24 + def CStructEntity.size(types) + offset = 0 + + max_align = types.map { |type, count = 1| + last_offset = offset + + align = PackInfo::ALIGN_MAP[type] + offset = PackInfo.align(last_offset, align) + + (PackInfo::SIZE_MAP[type] * count) + + align + }.max + + PackInfo.align(offset, max_align) + end + + # Wraps the C pointer +addr+ as a C struct with the given +types+. The C + # function +func+ is called when the instance is garbage collected. + # + # See also DL::CPtr.new + def initialize(addr, types, func = nil) + set_ctypes(types) + super(addr, @size, func) + end + + # Set the names of the +members+ in this C struct + def assign_names(members) + @members = members + end + + # Given +types+, calculate the offsets and sizes for the types in the + # struct. + def set_ctypes(types) + @ctypes = types + @offset = [] + offset = 0 + + max_align = types.map { |type, count = 1| + orig_offset = offset + align = ALIGN_MAP[type] + offset = PackInfo.align(orig_offset, align) + + @offset << offset + + offset += (SIZE_MAP[type] * count) + + align + }.max + + @size = PackInfo.align(offset, max_align) + end + + # Fetch struct member +name+ + def [](name) + idx = @members.index(name) + if( idx.nil? ) + raise(ArgumentError, "no such member: #{name}") + end + ty = @ctypes[idx] + if( ty.is_a?(Array) ) + r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1]) + else + r = super(@offset[idx], SIZE_MAP[ty.abs]) + end + packer = Packer.new([ty]) + val = packer.unpack([r]) + case ty + when Array + case ty[0] + when TYPE_VOIDP + val = val.collect{|v| CPtr.new(v)} + end + when TYPE_VOIDP + val = CPtr.new(val[0]) + else + val = val[0] + end + if( ty.is_a?(Integer) && (ty < 0) ) + return unsigned_value(val, ty) + elsif( ty.is_a?(Array) && (ty[0] < 0) ) + return val.collect{|v| unsigned_value(v,ty[0])} + else + return val + end + end + + # Set struct member +name+, to value +val+ + def []=(name, val) + idx = @members.index(name) + if( idx.nil? ) + raise(ArgumentError, "no such member: #{name}") + end + ty = @ctypes[idx] + packer = Packer.new([ty]) + val = wrap_arg(val, ty, []) + buff = packer.pack([val].flatten()) + super(@offset[idx], buff.size, buff) + if( ty.is_a?(Integer) && (ty < 0) ) + return unsigned_value(val, ty) + elsif( ty.is_a?(Array) && (ty[0] < 0) ) + return val.collect{|v| unsigned_value(v,ty[0])} + else + return val + end + end + + def to_s() # :nodoc: + super(@size) + end + end + + # A C union wrapper + class CUnionEntity < CStructEntity + include PackInfo + + # Allocates a C union the +types+ provided. The C function +func+ is + # called when the instance is garbage collected. + def CUnionEntity.malloc(types, func=nil) + addr = DL.malloc(CUnionEntity.size(types)) + CUnionEntity.new(addr, types, func) + end + + # Given +types+, returns the size needed for the union. + # + # DL::CUnionEntity.size([DL::TYPE_DOUBLE, DL::TYPE_INT, DL::TYPE_CHAR, + # DL::TYPE_VOIDP]) + # => 8 + def CUnionEntity.size(types) + types.map { |type, count = 1| + PackInfo::SIZE_MAP[type] * count + }.max + end + + # Given +types+, calculate the necessary offset and for each union member + def set_ctypes(types) + @ctypes = types + @offset = Array.new(types.length, 0) + @size = self.class.size types + end + end +end + diff --git a/ruby/lib/ruby/2.1.0/dl/types.rb b/ruby/lib/ruby/2.1.0/dl/types.rb new file mode 100644 index 0000000..d5724e4 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/dl/types.rb @@ -0,0 +1,71 @@ +module DL + # Adds Windows type aliases to the including class for use with + # DL::Importer. + # + # The aliases added are: + # * ATOM + # * BOOL + # * BYTE + # * DWORD + # * DWORD32 + # * DWORD64 + # * HANDLE + # * HDC + # * HINSTANCE + # * HWND + # * LPCSTR + # * LPSTR + # * PBYTE + # * PDWORD + # * PHANDLE + # * PVOID + # * PWORD + # * UCHAR + # * UINT + # * ULONG + # * WORD + module Win32Types + def included(m) # :nodoc: + m.module_eval{ + typealias "DWORD", "unsigned long" + typealias "PDWORD", "unsigned long *" + typealias "DWORD32", "unsigned long" + typealias "DWORD64", "unsigned long long" + typealias "WORD", "unsigned short" + typealias "PWORD", "unsigned short *" + typealias "BOOL", "int" + typealias "ATOM", "int" + typealias "BYTE", "unsigned char" + typealias "PBYTE", "unsigned char *" + typealias "UINT", "unsigned int" + typealias "ULONG", "unsigned long" + typealias "UCHAR", "unsigned char" + typealias "HANDLE", "uintptr_t" + typealias "PHANDLE", "void*" + typealias "PVOID", "void*" + typealias "LPCSTR", "char*" + typealias "LPSTR", "char*" + typealias "HINSTANCE", "unsigned int" + typealias "HDC", "unsigned int" + typealias "HWND", "unsigned int" + } + end + module_function :included + end + + # Adds basic type aliases to the including class for use with DL::Importer. + # + # The aliases added are +uint+ and +u_int+ (unsigned int) and + # +ulong+ and +u_long+ (unsigned long) + module BasicTypes + def included(m) # :nodoc: + m.module_eval{ + typealias "uint", "unsigned int" + typealias "u_int", "unsigned int" + typealias "ulong", "unsigned long" + typealias "u_long", "unsigned long" + } + end + module_function :included + end +end diff --git a/ruby/lib/ruby/2.1.0/dl/value.rb b/ruby/lib/ruby/2.1.0/dl/value.rb new file mode 100644 index 0000000..147d9d1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/dl/value.rb @@ -0,0 +1,114 @@ +require 'dl' + +module DL + module ValueUtil + def unsigned_value(val, ty) + case ty.abs + when TYPE_CHAR + [val].pack("c").unpack("C")[0] + when TYPE_SHORT + [val].pack("s!").unpack("S!")[0] + when TYPE_INT + [val].pack("i!").unpack("I!")[0] + when TYPE_LONG + [val].pack("l!").unpack("L!")[0] + when TYPE_LONG_LONG + [val].pack("q").unpack("Q")[0] + else + val + end + end + + def signed_value(val, ty) + case ty.abs + when TYPE_CHAR + [val].pack("C").unpack("c")[0] + when TYPE_SHORT + [val].pack("S!").unpack("s!")[0] + when TYPE_INT + [val].pack("I!").unpack("i!")[0] + when TYPE_LONG + [val].pack("L!").unpack("l!")[0] + when TYPE_LONG_LONG + [val].pack("Q").unpack("q")[0] + else + val + end + end + + def wrap_args(args, tys, funcs, &block) + result = [] + tys ||= [] + args.each_with_index{|arg, idx| + result.push(wrap_arg(arg, tys[idx], funcs, &block)) + } + result + end + + def wrap_arg(arg, ty, funcs = [], &block) + require 'dl/func' + + funcs ||= [] + case arg + when nil + return 0 + when CPtr + return arg.to_i + when IO + case ty + when TYPE_VOIDP + return CPtr[arg].to_i + else + return arg.to_i + end + when Function + if( block ) + arg.bind_at_call(&block) + funcs.push(arg) + elsif !arg.bound? + raise(RuntimeError, "block must be given.") + end + return arg.to_i + when String + if( ty.is_a?(Array) ) + return arg.unpack('C*') + else + case SIZEOF_VOIDP + when SIZEOF_LONG + return [arg].pack("p").unpack("l!")[0] + when SIZEOF_LONG_LONG + return [arg].pack("p").unpack("q")[0] + else + raise(RuntimeError, "sizeof(void*)?") + end + end + when Float, Integer + return arg + when Array + if( ty.is_a?(Array) ) # used only by struct + case ty[0] + when TYPE_VOIDP + return arg.collect{|v| Integer(v)} + when TYPE_CHAR + if( arg.is_a?(String) ) + return val.unpack('C*') + end + end + return arg + else + return arg + end + else + if( arg.respond_to?(:to_ptr) ) + return arg.to_ptr.to_i + else + begin + return Integer(arg) + rescue + raise(ArgumentError, "unknown argument type: #{arg.class}") + end + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/drb.rb b/ruby/lib/ruby/2.1.0/drb.rb new file mode 100644 index 0000000..93cc811 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb.rb @@ -0,0 +1,2 @@ +require 'drb/drb' + diff --git a/ruby/lib/ruby/2.1.0/drb/acl.rb b/ruby/lib/ruby/2.1.0/drb/acl.rb new file mode 100644 index 0000000..29a3781 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/acl.rb @@ -0,0 +1,250 @@ +# Copyright (c) 2000,2002,2003 Masatoshi SEKI +# +# acl.rb is copyrighted free software by Masatoshi SEKI. +# You can redistribute it and/or modify it under the same terms as Ruby. + +require 'ipaddr' + +## +# Simple Access Control Lists. +# +# Access control lists are composed of "allow" and "deny" halves to control +# access. Use "all" or "*" to match any address. To match a specific address +# use any address or address mask that IPAddr can understand. +# +# Example: +# +# list = %w[ +# deny all +# allow 192.168.1.1 +# allow ::ffff:192.168.1.2 +# allow 192.168.1.3 +# ] +# +# # From Socket#peeraddr, see also ACL#allow_socket? +# addr = ["AF_INET", 10, "lc630", "192.168.1.3"] +# +# acl = ACL.new +# p acl.allow_addr?(addr) # => true +# +# acl = ACL.new(list, ACL::DENY_ALLOW) +# p acl.allow_addr?(addr) # => true + +class ACL + + ## + # The current version of ACL + + VERSION=["2.0.0"] + + ## + # An entry in an ACL + + class ACLEntry + + ## + # Creates a new entry using +str+. + # + # +str+ may be "*" or "all" to match any address, an IP address string + # to match a specific address, an IP address mask per IPAddr, or one + # containing "*" to match part of an IPv4 address. + + def initialize(str) + if str == '*' or str == 'all' + @pat = [:all] + elsif str.include?('*') + @pat = [:name, dot_pat(str)] + else + begin + @pat = [:ip, IPAddr.new(str)] + rescue ArgumentError + @pat = [:name, dot_pat(str)] + end + end + end + + private + + ## + # Creates a regular expression to match IPv4 addresses + + def dot_pat_str(str) + list = str.split('.').collect { |s| + (s == '*') ? '.+' : s + } + list.join("\\.") + end + + private + + ## + # Creates a Regexp to match an address. + + def dot_pat(str) + exp = "^" + dot_pat_str(str) + "$" + Regexp.new(exp) + end + + public + + ## + # Matches +addr+ against this entry. + + def match(addr) + case @pat[0] + when :all + true + when :ip + begin + ipaddr = IPAddr.new(addr[3]) + ipaddr = ipaddr.ipv4_mapped if @pat[1].ipv6? && ipaddr.ipv4? + rescue ArgumentError + return false + end + (@pat[1].include?(ipaddr)) ? true : false + when :name + (@pat[1] =~ addr[2]) ? true : false + else + false + end + end + end + + ## + # A list of ACLEntry objects. Used to implement the allow and deny halves + # of an ACL + + class ACLList + + ## + # Creates an empty ACLList + + def initialize + @list = [] + end + + public + + ## + # Matches +addr+ against each ACLEntry in this list. + + def match(addr) + @list.each do |e| + return true if e.match(addr) + end + false + end + + public + + ## + # Adds +str+ as an ACLEntry in this list + + def add(str) + @list.push(ACLEntry.new(str)) + end + + end + + ## + # Default to deny + + DENY_ALLOW = 0 + + ## + # Default to allow + + ALLOW_DENY = 1 + + ## + # Creates a new ACL from +list+ with an evaluation +order+ of DENY_ALLOW or + # ALLOW_DENY. + # + # An ACL +list+ is an Array of "allow" or "deny" and an address or address + # mask or "all" or "*" to match any address: + # + # %w[ + # deny all + # allow 192.0.2.2 + # allow 192.0.2.128/26 + # ] + + def initialize(list=nil, order = DENY_ALLOW) + @order = order + @deny = ACLList.new + @allow = ACLList.new + install_list(list) if list + end + + public + + ## + # Allow connections from Socket +soc+? + + def allow_socket?(soc) + allow_addr?(soc.peeraddr) + end + + public + + ## + # Allow connections from addrinfo +addr+? It must be formatted like + # Socket#peeraddr: + # + # ["AF_INET", 10, "lc630", "192.0.2.1"] + + def allow_addr?(addr) + case @order + when DENY_ALLOW + return true if @allow.match(addr) + return false if @deny.match(addr) + return true + when ALLOW_DENY + return false if @deny.match(addr) + return true if @allow.match(addr) + return false + else + false + end + end + + public + + ## + # Adds +list+ of ACL entries to this ACL. + + def install_list(list) + i = 0 + while i < list.size + permission, domain = list.slice(i,2) + case permission.downcase + when 'allow' + @allow.add(domain) + when 'deny' + @deny.add(domain) + else + raise "Invalid ACL entry #{list.to_s}" + end + i += 2 + end + end + +end + +if __FILE__ == $0 + # example + list = %w(deny all + allow 192.168.1.1 + allow ::ffff:192.168.1.2 + allow 192.168.1.3 + ) + + addr = ["AF_INET", 10, "lc630", "192.168.1.3"] + + acl = ACL.new + p acl.allow_addr?(addr) + + acl = ACL.new(list, ACL::DENY_ALLOW) + p acl.allow_addr?(addr) +end + diff --git a/ruby/lib/ruby/2.1.0/drb/drb.rb b/ruby/lib/ruby/2.1.0/drb/drb.rb new file mode 100644 index 0000000..9796522 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/drb.rb @@ -0,0 +1,1833 @@ +# +# = drb/drb.rb +# +# Distributed Ruby: _dRuby_ version 2.0.4 +# +# Copyright (c) 1999-2003 Masatoshi SEKI. You can redistribute it and/or +# modify it under the same terms as Ruby. +# +# Author:: Masatoshi SEKI +# +# Documentation:: William Webber (william@williamwebber.com) +# +# == Overview +# +# dRuby is a distributed object system for Ruby. It allows an object in one +# Ruby process to invoke methods on an object in another Ruby process on the +# same or a different machine. +# +# The Ruby standard library contains the core classes of the dRuby package. +# However, the full package also includes access control lists and the +# Rinda tuple-space distributed task management system, as well as a +# large number of samples. The full dRuby package can be downloaded from +# the dRuby home page (see *References*). +# +# For an introduction and examples of usage see the documentation to the +# DRb module. +# +# == References +# +# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.html] +# The dRuby home page, in Japanese. Contains the full dRuby package +# and links to other Japanese-language sources. +# +# [http://www2a.biglobe.ne.jp/~seki/ruby/druby.en.html] +# The English version of the dRuby home page. +# +# [http://pragprog.com/book/sidruby/the-druby-book] +# The dRuby Book: Distributed and Parallel Computing with Ruby +# by Masatoshi Seki and Makoto Inoue +# +# [http://www.ruby-doc.org/docs/ProgrammingRuby/html/ospace.html] +# The chapter from *Programming* *Ruby* by Dave Thomas and Andy Hunt +# which discusses dRuby. +# +# [http://www.clio.ne.jp/home/web-i31s/Flotuard/Ruby/PRC2K_seki/dRuby.en.html] +# Translation of presentation on Ruby by Masatoshi Seki. + +require 'socket' +require 'thread' +require 'fcntl' +require 'drb/eq' + +# +# == Overview +# +# dRuby is a distributed object system for Ruby. It is written in +# pure Ruby and uses its own protocol. No add-in services are needed +# beyond those provided by the Ruby runtime, such as TCP sockets. It +# does not rely on or interoperate with other distributed object +# systems such as CORBA, RMI, or .NET. +# +# dRuby allows methods to be called in one Ruby process upon a Ruby +# object located in another Ruby process, even on another machine. +# References to objects can be passed between processes. Method +# arguments and return values are dumped and loaded in marshalled +# format. All of this is done transparently to both the caller of the +# remote method and the object that it is called upon. +# +# An object in a remote process is locally represented by a +# DRb::DRbObject instance. This acts as a sort of proxy for the +# remote object. Methods called upon this DRbObject instance are +# forwarded to its remote object. This is arranged dynamically at run +# time. There are no statically declared interfaces for remote +# objects, such as CORBA's IDL. +# +# dRuby calls made into a process are handled by a DRb::DRbServer +# instance within that process. This reconstitutes the method call, +# invokes it upon the specified local object, and returns the value to +# the remote caller. Any object can receive calls over dRuby. There +# is no need to implement a special interface, or mixin special +# functionality. Nor, in the general case, does an object need to +# explicitly register itself with a DRbServer in order to receive +# dRuby calls. +# +# One process wishing to make dRuby calls upon another process must +# somehow obtain an initial reference to an object in the remote +# process by some means other than as the return value of a remote +# method call, as there is initially no remote object reference it can +# invoke a method upon. This is done by attaching to the server by +# URI. Each DRbServer binds itself to a URI such as +# 'druby://example.com:8787'. A DRbServer can have an object attached +# to it that acts as the server's *front* *object*. A DRbObject can +# be explicitly created from the server's URI. This DRbObject's +# remote object will be the server's front object. This front object +# can then return references to other Ruby objects in the DRbServer's +# process. +# +# Method calls made over dRuby behave largely the same as normal Ruby +# method calls made within a process. Method calls with blocks are +# supported, as are raising exceptions. In addition to a method's +# standard errors, a dRuby call may also raise one of the +# dRuby-specific errors, all of which are subclasses of DRb::DRbError. +# +# Any type of object can be passed as an argument to a dRuby call or +# returned as its return value. By default, such objects are dumped +# or marshalled at the local end, then loaded or unmarshalled at the +# remote end. The remote end therefore receives a copy of the local +# object, not a distributed reference to it; methods invoked upon this +# copy are executed entirely in the remote process, not passed on to +# the local original. This has semantics similar to pass-by-value. +# +# However, if an object cannot be marshalled, a dRuby reference to it +# is passed or returned instead. This will turn up at the remote end +# as a DRbObject instance. All methods invoked upon this remote proxy +# are forwarded to the local object, as described in the discussion of +# DRbObjects. This has semantics similar to the normal Ruby +# pass-by-reference. +# +# The easiest way to signal that we want an otherwise marshallable +# object to be passed or returned as a DRbObject reference, rather +# than marshalled and sent as a copy, is to include the +# DRb::DRbUndumped mixin module. +# +# dRuby supports calling remote methods with blocks. As blocks (or +# rather the Proc objects that represent them) are not marshallable, +# the block executes in the local, not the remote, context. Each +# value yielded to the block is passed from the remote object to the +# local block, then the value returned by each block invocation is +# passed back to the remote execution context to be collected, before +# the collected values are finally returned to the local context as +# the return value of the method invocation. +# +# == Examples of usage +# +# For more dRuby samples, see the +samples+ directory in the full +# dRuby distribution. +# +# === dRuby in client/server mode +# +# This illustrates setting up a simple client-server drb +# system. Run the server and client code in different terminals, +# starting the server code first. +# +# ==== Server code +# +# require 'drb/drb' +# +# # The URI for the server to connect to +# URI="druby://localhost:8787" +# +# class TimeServer +# +# def get_current_time +# return Time.now +# end +# +# end +# +# # The object that handles requests on the server +# FRONT_OBJECT=TimeServer.new +# +# $SAFE = 1 # disable eval() and friends +# +# DRb.start_service(URI, FRONT_OBJECT) +# # Wait for the drb server thread to finish before exiting. +# DRb.thread.join +# +# ==== Client code +# +# require 'drb/drb' +# +# # The URI to connect to +# SERVER_URI="druby://localhost:8787" +# +# # Start a local DRbServer to handle callbacks. +# # +# # Not necessary for this small example, but will be required +# # as soon as we pass a non-marshallable object as an argument +# # to a dRuby call. +# DRb.start_service +# +# timeserver = DRbObject.new_with_uri(SERVER_URI) +# puts timeserver.get_current_time +# +# === Remote objects under dRuby +# +# This example illustrates returning a reference to an object +# from a dRuby call. The Logger instances live in the server +# process. References to them are returned to the client process, +# where methods can be invoked upon them. These methods are +# executed in the server process. +# +# ==== Server code +# +# require 'drb/drb' +# +# URI="druby://localhost:8787" +# +# class Logger +# +# # Make dRuby send Logger instances as dRuby references, +# # not copies. +# include DRb::DRbUndumped +# +# def initialize(n, fname) +# @name = n +# @filename = fname +# end +# +# def log(message) +# File.open(@filename, "a") do |f| +# f.puts("#{Time.now}: #{@name}: #{message}") +# end +# end +# +# end +# +# # We have a central object for creating and retrieving loggers. +# # This retains a local reference to all loggers created. This +# # is so an existing logger can be looked up by name, but also +# # to prevent loggers from being garbage collected. A dRuby +# # reference to an object is not sufficient to prevent it being +# # garbage collected! +# class LoggerFactory +# +# def initialize(bdir) +# @basedir = bdir +# @loggers = {} +# end +# +# def get_logger(name) +# if !@loggers.has_key? name +# # make the filename safe, then declare it to be so +# fname = name.gsub(/[.\/\\\:]/, "_").untaint +# @loggers[name] = Logger.new(name, @basedir + "/" + fname) +# end +# return @loggers[name] +# end +# +# end +# +# FRONT_OBJECT=LoggerFactory.new("/tmp/dlog") +# +# $SAFE = 1 # disable eval() and friends +# +# DRb.start_service(URI, FRONT_OBJECT) +# DRb.thread.join +# +# ==== Client code +# +# require 'drb/drb' +# +# SERVER_URI="druby://localhost:8787" +# +# DRb.start_service +# +# log_service=DRbObject.new_with_uri(SERVER_URI) +# +# ["loga", "logb", "logc"].each do |logname| +# +# logger=log_service.get_logger(logname) +# +# logger.log("Hello, world!") +# logger.log("Goodbye, world!") +# logger.log("=== EOT ===") +# +# end +# +# == Security +# +# As with all network services, security needs to be considered when +# using dRuby. By allowing external access to a Ruby object, you are +# not only allowing outside clients to call the methods you have +# defined for that object, but by default to execute arbitrary Ruby +# code on your server. Consider the following: +# +# # !!! UNSAFE CODE !!! +# ro = DRbObject::new_with_uri("druby://your.server.com:8989") +# class << ro +# undef :instance_eval # force call to be passed to remote object +# end +# ro.instance_eval("`rm -rf *`") +# +# The dangers posed by instance_eval and friends are such that a +# DRbServer should generally be run with $SAFE set to at least +# level 1. This will disable eval() and related calls on strings +# passed across the wire. The sample usage code given above follows +# this practice. +# +# A DRbServer can be configured with an access control list to +# selectively allow or deny access from specified IP addresses. The +# main druby distribution provides the ACL class for this purpose. In +# general, this mechanism should only be used alongside, rather than +# as a replacement for, a good firewall. +# +# == dRuby internals +# +# dRuby is implemented using three main components: a remote method +# call marshaller/unmarshaller; a transport protocol; and an +# ID-to-object mapper. The latter two can be directly, and the first +# indirectly, replaced, in order to provide different behaviour and +# capabilities. +# +# Marshalling and unmarshalling of remote method calls is performed by +# a DRb::DRbMessage instance. This uses the Marshal module to dump +# the method call before sending it over the transport layer, then +# reconstitute it at the other end. There is normally no need to +# replace this component, and no direct way is provided to do so. +# However, it is possible to implement an alternative marshalling +# scheme as part of an implementation of the transport layer. +# +# The transport layer is responsible for opening client and server +# network connections and forwarding dRuby request across them. +# Normally, it uses DRb::DRbMessage internally to manage marshalling +# and unmarshalling. The transport layer is managed by +# DRb::DRbProtocol. Multiple protocols can be installed in +# DRbProtocol at the one time; selection between them is determined by +# the scheme of a dRuby URI. The default transport protocol is +# selected by the scheme 'druby:', and implemented by +# DRb::DRbTCPSocket. This uses plain TCP/IP sockets for +# communication. An alternative protocol, using UNIX domain sockets, +# is implemented by DRb::DRbUNIXSocket in the file drb/unix.rb, and +# selected by the scheme 'drbunix:'. A sample implementation over +# HTTP can be found in the samples accompanying the main dRuby +# distribution. +# +# The ID-to-object mapping component maps dRuby object ids to the +# objects they refer to, and vice versa. The implementation to use +# can be specified as part of a DRb::DRbServer's configuration. The +# default implementation is provided by DRb::DRbIdConv. It uses an +# object's ObjectSpace id as its dRuby id. This means that the dRuby +# reference to that object only remains meaningful for the lifetime of +# the object's process and the lifetime of the object within that +# process. A modified implementation is provided by DRb::TimerIdConv +# in the file drb/timeridconv.rb. This implementation retains a local +# reference to all objects exported over dRuby for a configurable +# period of time (defaulting to ten minutes), to prevent them being +# garbage-collected within this time. Another sample implementation +# is provided in sample/name.rb in the main dRuby distribution. This +# allows objects to specify their own id or "name". A dRuby reference +# can be made persistent across processes by having each process +# register an object using the same dRuby name. +# +module DRb + + # Superclass of all errors raised in the DRb module. + class DRbError < RuntimeError; end + + # Error raised when an error occurs on the underlying communication + # protocol. + class DRbConnError < DRbError; end + + # Class responsible for converting between an object and its id. + # + # This, the default implementation, uses an object's local ObjectSpace + # __id__ as its id. This means that an object's identification over + # drb remains valid only while that object instance remains alive + # within the server runtime. + # + # For alternative mechanisms, see DRb::TimerIdConv in rdb/timeridconv.rb + # and DRbNameIdConv in sample/name.rb in the full drb distribution. + class DRbIdConv + + # Convert an object reference id to an object. + # + # This implementation looks up the reference id in the local object + # space and returns the object it refers to. + def to_obj(ref) + ObjectSpace._id2ref(ref) + end + + # Convert an object into a reference id. + # + # This implementation returns the object's __id__ in the local + # object space. + def to_id(obj) + obj.nil? ? nil : obj.__id__ + end + end + + # Mixin module making an object undumpable or unmarshallable. + # + # If an object which includes this module is returned by method + # called over drb, then the object remains in the server space + # and a reference to the object is returned, rather than the + # object being marshalled and moved into the client space. + module DRbUndumped + def _dump(dummy) # :nodoc: + raise TypeError, 'can\'t dump' + end + end + + # Error raised by the DRb module when an attempt is made to refer to + # the context's current drb server but the context does not have one. + # See #current_server. + class DRbServerNotFound < DRbError; end + + # Error raised by the DRbProtocol module when it cannot find any + # protocol implementation support the scheme specified in a URI. + class DRbBadURI < DRbError; end + + # Error raised by a dRuby protocol when it doesn't support the + # scheme specified in a URI. See DRb::DRbProtocol. + class DRbBadScheme < DRbError; end + + # An exception wrapping a DRb::DRbUnknown object + class DRbUnknownError < DRbError + + # Create a new DRbUnknownError for the DRb::DRbUnknown object +unknown+ + def initialize(unknown) + @unknown = unknown + super(unknown.name) + end + + # Get the wrapped DRb::DRbUnknown object. + attr_reader :unknown + + def self._load(s) # :nodoc: + Marshal::load(s) + end + + def _dump(lv) # :nodoc: + Marshal::dump(@unknown) + end + end + + # An exception wrapping an error object + class DRbRemoteError < DRbError + + # Creates a new remote error that wraps the Exception +error+ + def initialize(error) + @reason = error.class.to_s + super("#{error.message} (#{error.class})") + set_backtrace(error.backtrace) + end + + # the class of the error, as a string. + attr_reader :reason + end + + # Class wrapping a marshalled object whose type is unknown locally. + # + # If an object is returned by a method invoked over drb, but the + # class of the object is unknown in the client namespace, or + # the object is a constant unknown in the client namespace, then + # the still-marshalled object is returned wrapped in a DRbUnknown instance. + # + # If this object is passed as an argument to a method invoked over + # drb, then the wrapped object is passed instead. + # + # The class or constant name of the object can be read from the + # +name+ attribute. The marshalled object is held in the +buf+ + # attribute. + class DRbUnknown + + # Create a new DRbUnknown object. + # + # +buf+ is a string containing a marshalled object that could not + # be unmarshalled. +err+ is the error message that was raised + # when the unmarshalling failed. It is used to determine the + # name of the unmarshalled object. + def initialize(err, buf) + case err.to_s + when /uninitialized constant (\S+)/ + @name = $1 + when /undefined class\/module (\S+)/ + @name = $1 + else + @name = nil + end + @buf = buf + end + + # The name of the unknown thing. + # + # Class name for unknown objects; variable name for unknown + # constants. + attr_reader :name + + # Buffer contained the marshalled, unknown object. + attr_reader :buf + + def self._load(s) # :nodoc: + begin + Marshal::load(s) + rescue NameError, ArgumentError + DRbUnknown.new($!, s) + end + end + + def _dump(lv) # :nodoc: + @buf + end + + # Attempt to load the wrapped marshalled object again. + # + # If the class of the object is now known locally, the object + # will be unmarshalled and returned. Otherwise, a new + # but identical DRbUnknown object will be returned. + def reload + self.class._load(@buf) + end + + # Create a DRbUnknownError exception containing this object. + def exception + DRbUnknownError.new(self) + end + end + + # An Array wrapper that can be sent to another server via DRb. + # + # All entries in the array will be dumped or be references that point to + # the local server. + + class DRbArray + + # Creates a new DRbArray that either dumps or wraps all the items in the + # Array +ary+ so they can be loaded by a remote DRb server. + + def initialize(ary) + @ary = ary.collect { |obj| + if obj.kind_of? DRbUndumped + DRbObject.new(obj) + else + begin + Marshal.dump(obj) + obj + rescue + DRbObject.new(obj) + end + end + } + end + + def self._load(s) # :nodoc: + Marshal::load(s) + end + + def _dump(lv) # :nodoc: + Marshal.dump(@ary) + end + end + + # Handler for sending and receiving drb messages. + # + # This takes care of the low-level marshalling and unmarshalling + # of drb requests and responses sent over the wire between server + # and client. This relieves the implementor of a new drb + # protocol layer with having to deal with these details. + # + # The user does not have to directly deal with this object in + # normal use. + class DRbMessage + def initialize(config) # :nodoc: + @load_limit = config[:load_limit] + @argc_limit = config[:argc_limit] + end + + def dump(obj, error=false) # :nodoc: + obj = make_proxy(obj, error) if obj.kind_of? DRbUndumped + begin + str = Marshal::dump(obj) + rescue + str = Marshal::dump(make_proxy(obj, error)) + end + [str.size].pack('N') + str + end + + def load(soc) # :nodoc: + begin + sz = soc.read(4) # sizeof (N) + rescue + raise(DRbConnError, $!.message, $!.backtrace) + end + raise(DRbConnError, 'connection closed') if sz.nil? + raise(DRbConnError, 'premature header') if sz.size < 4 + sz = sz.unpack('N')[0] + raise(DRbConnError, "too large packet #{sz}") if @load_limit < sz + begin + str = soc.read(sz) + rescue + raise(DRbConnError, $!.message, $!.backtrace) + end + raise(DRbConnError, 'connection closed') if str.nil? + raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz + DRb.mutex.synchronize do + begin + save = Thread.current[:drb_untaint] + Thread.current[:drb_untaint] = [] + Marshal::load(str) + rescue NameError, ArgumentError + DRbUnknown.new($!, str) + ensure + Thread.current[:drb_untaint].each do |x| + x.untaint + end + Thread.current[:drb_untaint] = save + end + end + end + + def send_request(stream, ref, msg_id, arg, b) # :nodoc: + ary = [] + ary.push(dump(ref.__drbref)) + ary.push(dump(msg_id.id2name)) + ary.push(dump(arg.length)) + arg.each do |e| + ary.push(dump(e)) + end + ary.push(dump(b)) + stream.write(ary.join('')) + rescue + raise(DRbConnError, $!.message, $!.backtrace) + end + + def recv_request(stream) # :nodoc: + ref = load(stream) + ro = DRb.to_obj(ref) + msg = load(stream) + argc = load(stream) + raise(DRbConnError, "too many arguments") if @argc_limit < argc + argv = Array.new(argc, nil) + argc.times do |n| + argv[n] = load(stream) + end + block = load(stream) + return ro, msg, argv, block + end + + def send_reply(stream, succ, result) # :nodoc: + stream.write(dump(succ) + dump(result, !succ)) + rescue + raise(DRbConnError, $!.message, $!.backtrace) + end + + def recv_reply(stream) # :nodoc: + succ = load(stream) + result = load(stream) + [succ, result] + end + + private + def make_proxy(obj, error=false) # :nodoc: + if error + DRbRemoteError.new(obj) + else + DRbObject.new(obj) + end + end + end + + # Module managing the underlying network protocol(s) used by drb. + # + # By default, drb uses the DRbTCPSocket protocol. Other protocols + # can be defined. A protocol must define the following class methods: + # + # [open(uri, config)] Open a client connection to the server at +uri+, + # using configuration +config+. Return a protocol + # instance for this connection. + # [open_server(uri, config)] Open a server listening at +uri+, + # using configuration +config+. Return a + # protocol instance for this listener. + # [uri_option(uri, config)] Take a URI, possibly containing an option + # component (e.g. a trailing '?param=val'), + # and return a [uri, option] tuple. + # + # All of these methods should raise a DRbBadScheme error if the URI + # does not identify the protocol they support (e.g. "druby:" for + # the standard Ruby protocol). This is how the DRbProtocol module, + # given a URI, determines which protocol implementation serves that + # protocol. + # + # The protocol instance returned by #open_server must have the + # following methods: + # + # [accept] Accept a new connection to the server. Returns a protocol + # instance capable of communicating with the client. + # [close] Close the server connection. + # [uri] Get the URI for this server. + # + # The protocol instance returned by #open must have the following methods: + # + # [send_request (ref, msg_id, arg, b)] + # Send a request to +ref+ with the given message id and arguments. + # This is most easily implemented by calling DRbMessage.send_request, + # providing a stream that sits on top of the current protocol. + # [recv_reply] + # Receive a reply from the server and return it as a [success-boolean, + # reply-value] pair. This is most easily implemented by calling + # DRb.recv_reply, providing a stream that sits on top of the + # current protocol. + # [alive?] + # Is this connection still alive? + # [close] + # Close this connection. + # + # The protocol instance returned by #open_server().accept() must have + # the following methods: + # + # [recv_request] + # Receive a request from the client and return a [object, message, + # args, block] tuple. This is most easily implemented by calling + # DRbMessage.recv_request, providing a stream that sits on top of + # the current protocol. + # [send_reply(succ, result)] + # Send a reply to the client. This is most easily implemented + # by calling DRbMessage.send_reply, providing a stream that sits + # on top of the current protocol. + # [close] + # Close this connection. + # + # A new protocol is registered with the DRbProtocol module using + # the add_protocol method. + # + # For examples of other protocols, see DRbUNIXSocket in drb/unix.rb, + # and HTTP0 in sample/http0.rb and sample/http0serv.rb in the full + # drb distribution. + module DRbProtocol + + # Add a new protocol to the DRbProtocol module. + def add_protocol(prot) + @protocol.push(prot) + end + module_function :add_protocol + + # Open a client connection to +uri+ with the configuration +config+. + # + # The DRbProtocol module asks each registered protocol in turn to + # try to open the URI. Each protocol signals that it does not handle that + # URI by raising a DRbBadScheme error. If no protocol recognises the + # URI, then a DRbBadURI error is raised. If a protocol accepts the + # URI, but an error occurs in opening it, a DRbConnError is raised. + def open(uri, config, first=true) + @protocol.each do |prot| + begin + return prot.open(uri, config) + rescue DRbBadScheme + rescue DRbConnError + raise($!) + rescue + raise(DRbConnError, "#{uri} - #{$!.inspect}") + end + end + if first && (config[:auto_load] != false) + auto_load(uri, config) + return open(uri, config, false) + end + raise DRbBadURI, 'can\'t parse uri:' + uri + end + module_function :open + + # Open a server listening for connections at +uri+ with + # configuration +config+. + # + # The DRbProtocol module asks each registered protocol in turn to + # try to open a server at the URI. Each protocol signals that it does + # not handle that URI by raising a DRbBadScheme error. If no protocol + # recognises the URI, then a DRbBadURI error is raised. If a protocol + # accepts the URI, but an error occurs in opening it, the underlying + # error is passed on to the caller. + def open_server(uri, config, first=true) + @protocol.each do |prot| + begin + return prot.open_server(uri, config) + rescue DRbBadScheme + end + end + if first && (config[:auto_load] != false) + auto_load(uri, config) + return open_server(uri, config, false) + end + raise DRbBadURI, 'can\'t parse uri:' + uri + end + module_function :open_server + + # Parse +uri+ into a [uri, option] pair. + # + # The DRbProtocol module asks each registered protocol in turn to + # try to parse the URI. Each protocol signals that it does not handle that + # URI by raising a DRbBadScheme error. If no protocol recognises the + # URI, then a DRbBadURI error is raised. + def uri_option(uri, config, first=true) + @protocol.each do |prot| + begin + uri, opt = prot.uri_option(uri, config) + # opt = nil if opt == '' + return uri, opt + rescue DRbBadScheme + end + end + if first && (config[:auto_load] != false) + auto_load(uri, config) + return uri_option(uri, config, false) + end + raise DRbBadURI, 'can\'t parse uri:' + uri + end + module_function :uri_option + + def auto_load(uri, config) # :nodoc: + if uri =~ /^drb([a-z0-9]+):/ + require("drb/#{$1}") rescue nil + end + end + module_function :auto_load + end + + # The default drb protocol which communicates over a TCP socket. + # + # The DRb TCP protocol URI looks like: + # druby://:?. The option is optional. + + class DRbTCPSocket + # :stopdoc: + private + def self.parse_uri(uri) + if uri =~ /^druby:\/\/(.*?):(\d+)(\?(.*))?$/ + host = $1 + port = $2.to_i + option = $4 + [host, port, option] + else + raise(DRbBadScheme, uri) unless uri =~ /^druby:/ + raise(DRbBadURI, 'can\'t parse uri:' + uri) + end + end + + public + + # Open a client connection to +uri+ (DRb URI string) using configuration + # +config+. + # + # This can raise DRb::DRbBadScheme or DRb::DRbBadURI if +uri+ is not for a + # recognized protocol. See DRb::DRbServer.new for information on built-in + # URI protocols. + def self.open(uri, config) + host, port, = parse_uri(uri) + host.untaint + port.untaint + soc = TCPSocket.open(host, port) + self.new(uri, soc, config) + end + + # Returns the hostname of this server + def self.getservername + host = Socket::gethostname + begin + Socket::gethostbyname(host)[0] + rescue + 'localhost' + end + end + + # For the families available for +host+, returns a TCPServer on +port+. + # If +port+ is 0 the first available port is used. IPv4 servers are + # preferred over IPv6 servers. + def self.open_server_inaddr_any(host, port) + infos = Socket::getaddrinfo(host, nil, + Socket::AF_UNSPEC, + Socket::SOCK_STREAM, + 0, + Socket::AI_PASSIVE) + families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten] + return TCPServer.open('0.0.0.0', port) if families.has_key?('AF_INET') + return TCPServer.open('::', port) if families.has_key?('AF_INET6') + return TCPServer.open(port) + # :stopdoc: + end + + # Open a server listening for connections at +uri+ using + # configuration +config+. + def self.open_server(uri, config) + uri = 'druby://:0' unless uri + host, port, _ = parse_uri(uri) + config = {:tcp_original_host => host}.update(config) + if host.size == 0 + host = getservername + soc = open_server_inaddr_any(host, port) + else + soc = TCPServer.open(host, port) + end + port = soc.addr[1] if port == 0 + config[:tcp_port] = port + uri = "druby://#{host}:#{port}" + self.new(uri, soc, config) + end + + # Parse +uri+ into a [uri, option] pair. + def self.uri_option(uri, config) + host, port, option = parse_uri(uri) + return "druby://#{host}:#{port}", option + end + + # Create a new DRbTCPSocket instance. + # + # +uri+ is the URI we are connected to. + # +soc+ is the tcp socket we are bound to. +config+ is our + # configuration. + def initialize(uri, soc, config={}) + @uri = uri + @socket = soc + @config = config + @acl = config[:tcp_acl] + @msg = DRbMessage.new(config) + set_sockopt(@socket) + end + + # Get the URI that we are connected to. + attr_reader :uri + + # Get the address of our TCP peer (the other end of the socket + # we are bound to. + def peeraddr + @socket.peeraddr + end + + # Get the socket. + def stream; @socket; end + + # On the client side, send a request to the server. + def send_request(ref, msg_id, arg, b) + @msg.send_request(stream, ref, msg_id, arg, b) + end + + # On the server side, receive a request from the client. + def recv_request + @msg.recv_request(stream) + end + + # On the server side, send a reply to the client. + def send_reply(succ, result) + @msg.send_reply(stream, succ, result) + end + + # On the client side, receive a reply from the server. + def recv_reply + @msg.recv_reply(stream) + end + + public + + # Close the connection. + # + # If this is an instance returned by #open_server, then this stops + # listening for new connections altogether. If this is an instance + # returned by #open or by #accept, then it closes this particular + # client-server session. + def close + if @socket + @socket.close + @socket = nil + end + end + + # On the server side, for an instance returned by #open_server, + # accept a client connection and return a new instance to handle + # the server's side of this client-server session. + def accept + while true + s = @socket.accept + break if (@acl ? @acl.allow_socket?(s) : true) + s.close + end + if @config[:tcp_original_host].to_s.size == 0 + uri = "druby://#{s.addr[3]}:#{@config[:tcp_port]}" + else + uri = @uri + end + self.class.new(uri, s, @config) + end + + # Check to see if this connection is alive. + def alive? + return false unless @socket + if IO.select([@socket], nil, nil, 0) + close + return false + end + true + end + + def set_sockopt(soc) # :nodoc: + soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) + soc.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC + end + end + + module DRbProtocol + @protocol = [DRbTCPSocket] # default + end + + class DRbURIOption # :nodoc: I don't understand the purpose of this class... + def initialize(option) + @option = option.to_s + end + attr :option + def to_s; @option; end + + def ==(other) + return false unless DRbURIOption === other + @option == other.option + end + + def hash + @option.hash + end + + alias eql? == + end + + # Object wrapping a reference to a remote drb object. + # + # Method calls on this object are relayed to the remote + # object that this object is a stub for. + class DRbObject + + # Unmarshall a marshalled DRbObject. + # + # If the referenced object is located within the local server, then + # the object itself is returned. Otherwise, a new DRbObject is + # created to act as a stub for the remote referenced object. + def self._load(s) + uri, ref = Marshal.load(s) + + if DRb.here?(uri) + obj = DRb.to_obj(ref) + if ((! obj.tainted?) && Thread.current[:drb_untaint]) + Thread.current[:drb_untaint].push(obj) + end + return obj + end + + self.new_with(uri, ref) + end + + # Creates a DRb::DRbObject given the reference information to the remote + # host +uri+ and object +ref+. + + def self.new_with(uri, ref) + it = self.allocate + it.instance_variable_set(:@uri, uri) + it.instance_variable_set(:@ref, ref) + it + end + + # Create a new DRbObject from a URI alone. + def self.new_with_uri(uri) + self.new(nil, uri) + end + + # Marshall this object. + # + # The URI and ref of the object are marshalled. + def _dump(lv) + Marshal.dump([@uri, @ref]) + end + + # Create a new remote object stub. + # + # +obj+ is the (local) object we want to create a stub for. Normally + # this is +nil+. +uri+ is the URI of the remote object that this + # will be a stub for. + def initialize(obj, uri=nil) + @uri = nil + @ref = nil + if obj.nil? + return if uri.nil? + @uri, option = DRbProtocol.uri_option(uri, DRb.config) + @ref = DRbURIOption.new(option) unless option.nil? + else + @uri = uri ? uri : (DRb.uri rescue nil) + @ref = obj ? DRb.to_id(obj) : nil + end + end + + # Get the URI of the remote object. + def __drburi + @uri + end + + # Get the reference of the object, if local. + def __drbref + @ref + end + + undef :to_s + undef :to_a if respond_to?(:to_a) + + # Routes respond_to? to the referenced remote object. + def respond_to?(msg_id, priv=false) + case msg_id + when :_dump + true + when :marshal_dump + false + else + method_missing(:respond_to?, msg_id, priv) + end + end + + # Routes method calls to the referenced remote object. + def method_missing(msg_id, *a, &b) + if DRb.here?(@uri) + obj = DRb.to_obj(@ref) + DRb.current_server.check_insecure_method(obj, msg_id) + return obj.__send__(msg_id, *a, &b) + end + + succ, result = self.class.with_friend(@uri) do + DRbConn.open(@uri) do |conn| + conn.send_message(self, msg_id, a, b) + end + end + + if succ + return result + elsif DRbUnknown === result + raise result + else + bt = self.class.prepare_backtrace(@uri, result) + result.set_backtrace(bt + caller) + raise result + end + end + + # Given the +uri+ of another host executes the block provided. + def self.with_friend(uri) # :nodoc: + friend = DRb.fetch_server(uri) + return yield() unless friend + + save = Thread.current['DRb'] + Thread.current['DRb'] = { 'server' => friend } + return yield + ensure + Thread.current['DRb'] = save if friend + end + + # Returns a modified backtrace from +result+ with the +uri+ where each call + # in the backtrace came from. + def self.prepare_backtrace(uri, result) # :nodoc: + prefix = "(#{uri}) " + bt = [] + result.backtrace.each do |x| + break if /`__send__'$/ =~ x + if /^\(druby:\/\// =~ x + bt.push(x) + else + bt.push(prefix + x) + end + end + bt + end + + def pretty_print(q) # :nodoc: + q.pp_object(self) + end + + def pretty_print_cycle(q) # :nodoc: + q.object_address_group(self) { + q.breakable + q.text '...' + } + end + end + + # Class handling the connection between a DRbObject and the + # server the real object lives on. + # + # This class maintains a pool of connections, to reduce the + # overhead of starting and closing down connections for each + # method call. + # + # This class is used internally by DRbObject. The user does + # not normally need to deal with it directly. + class DRbConn + POOL_SIZE = 16 # :nodoc: + @mutex = Mutex.new + @pool = [] + + def self.open(remote_uri) # :nodoc: + begin + conn = nil + + @mutex.synchronize do + #FIXME + new_pool = [] + @pool.each do |c| + if conn.nil? and c.uri == remote_uri + conn = c if c.alive? + else + new_pool.push c + end + end + @pool = new_pool + end + + conn = self.new(remote_uri) unless conn + succ, result = yield(conn) + return succ, result + + ensure + if conn + if succ + @mutex.synchronize do + @pool.unshift(conn) + @pool.pop.close while @pool.size > POOL_SIZE + end + else + conn.close + end + end + end + end + + def initialize(remote_uri) # :nodoc: + @uri = remote_uri + @protocol = DRbProtocol.open(remote_uri, DRb.config) + end + attr_reader :uri # :nodoc: + + def send_message(ref, msg_id, arg, block) # :nodoc: + @protocol.send_request(ref, msg_id, arg, block) + @protocol.recv_reply + end + + def close # :nodoc: + @protocol.close + @protocol = nil + end + + def alive? # :nodoc: + return false unless @protocol + @protocol.alive? + end + end + + # Class representing a drb server instance. + # + # A DRbServer must be running in the local process before any incoming + # dRuby calls can be accepted, or any local objects can be passed as + # dRuby references to remote processes, even if those local objects are + # never actually called remotely. You do not need to start a DRbServer + # in the local process if you are only making outgoing dRuby calls + # passing marshalled parameters. + # + # Unless multiple servers are being used, the local DRbServer is normally + # started by calling DRb.start_service. + class DRbServer + @@acl = nil + @@idconv = DRbIdConv.new + @@secondary_server = nil + @@argc_limit = 256 + @@load_limit = 256 * 102400 + @@verbose = false + @@safe_level = 0 + + # Set the default value for the :argc_limit option. + # + # See #new(). The initial default value is 256. + def self.default_argc_limit(argc) + @@argc_limit = argc + end + + # Set the default value for the :load_limit option. + # + # See #new(). The initial default value is 25 MB. + def self.default_load_limit(sz) + @@load_limit = sz + end + + # Set the default access control list to +acl+. The default ACL is +nil+. + # + # See also DRb::ACL and #new() + def self.default_acl(acl) + @@acl = acl + end + + # Set the default value for the :id_conv option. + # + # See #new(). The initial default value is a DRbIdConv instance. + def self.default_id_conv(idconv) + @@idconv = idconv + end + + # Set the default safe level to +level+. The default safe level is 0 + # + # See #new for more information. + def self.default_safe_level(level) + @@safe_level = level + end + + # Set the default value of the :verbose option. + # + # See #new(). The initial default value is false. + def self.verbose=(on) + @@verbose = on + end + + # Get the default value of the :verbose option. + def self.verbose + @@verbose + end + + def self.make_config(hash={}) # :nodoc: + default_config = { + :idconv => @@idconv, + :verbose => @@verbose, + :tcp_acl => @@acl, + :load_limit => @@load_limit, + :argc_limit => @@argc_limit, + :safe_level => @@safe_level + } + default_config.update(hash) + end + + # Create a new DRbServer instance. + # + # +uri+ is the URI to bind to. This is normally of the form + # 'druby://:' where is a hostname of + # the local machine. If nil, then the system's default hostname + # will be bound to, on a port selected by the system; these value + # can be retrieved from the +uri+ attribute. 'druby:' specifies + # the default dRuby transport protocol: another protocol, such + # as 'drbunix:', can be specified instead. + # + # +front+ is the front object for the server, that is, the object + # to which remote method calls on the server will be passed. If + # nil, then the server will not accept remote method calls. + # + # If +config_or_acl+ is a hash, it is the configuration to + # use for this server. The following options are recognised: + # + # :idconv :: an id-to-object conversion object. This defaults + # to an instance of the class DRb::DRbIdConv. + # :verbose :: if true, all unsuccessful remote calls on objects + # in the server will be logged to $stdout. false + # by default. + # :tcp_acl :: the access control list for this server. See + # the ACL class from the main dRuby distribution. + # :load_limit :: the maximum message size in bytes accepted by + # the server. Defaults to 25 MB (26214400). + # :argc_limit :: the maximum number of arguments to a remote + # method accepted by the server. Defaults to + # 256. + # :safe_level :: The safe level of the DRbServer. The attribute + # sets $SAFE for methods performed in the main_loop. + # Defaults to 0. + # + # The default values of these options can be modified on + # a class-wide basis by the class methods #default_argc_limit, + # #default_load_limit, #default_acl, #default_id_conv, + # and #verbose= + # + # If +config_or_acl+ is not a hash, but is not nil, it is + # assumed to be the access control list for this server. + # See the :tcp_acl option for more details. + # + # If no other server is currently set as the primary server, + # this will become the primary server. + # + # The server will immediately start running in its own thread. + def initialize(uri=nil, front=nil, config_or_acl=nil) + if Hash === config_or_acl + config = config_or_acl.dup + else + acl = config_or_acl || @@acl + config = { + :tcp_acl => acl + } + end + + @config = self.class.make_config(config) + + @protocol = DRbProtocol.open_server(uri, @config) + @uri = @protocol.uri + @exported_uri = [@uri] + + @front = front + @idconv = @config[:idconv] + @safe_level = @config[:safe_level] + + @grp = ThreadGroup.new + @thread = run + + DRb.regist_server(self) + end + + # The URI of this DRbServer. + attr_reader :uri + + # The main thread of this DRbServer. + # + # This is the thread that listens for and accepts connections + # from clients, not that handles each client's request-response + # session. + attr_reader :thread + + # The front object of the DRbServer. + # + # This object receives remote method calls made on the server's + # URI alone, with an object id. + attr_reader :front + + # The configuration of this DRbServer + attr_reader :config + + # The safe level for this server. This is a number corresponding to + # $SAFE. + # + # The default safe_level is 0 + attr_reader :safe_level + + # Set whether to operate in verbose mode. + # + # In verbose mode, failed calls are logged to stdout. + def verbose=(v); @config[:verbose]=v; end + + # Get whether the server is in verbose mode. + # + # In verbose mode, failed calls are logged to stdout. + def verbose; @config[:verbose]; end + + # Is this server alive? + def alive? + @thread.alive? + end + + # Is +uri+ the URI for this server? + def here?(uri) + @exported_uri.include?(uri) + end + + # Stop this server. + def stop_service + DRb.remove_server(self) + if Thread.current['DRb'] && Thread.current['DRb']['server'] == self + Thread.current['DRb']['stop_service'] = true + else + @thread.kill.join + end + end + + # Convert a dRuby reference to the local object it refers to. + def to_obj(ref) + return front if ref.nil? + return front[ref.to_s] if DRbURIOption === ref + @idconv.to_obj(ref) + end + + # Convert a local object to a dRuby reference. + def to_id(obj) + return nil if obj.__id__ == front.__id__ + @idconv.to_id(obj) + end + + private + + ## + # Starts the DRb main loop in a new thread. + + def run + Thread.start do + begin + while true + main_loop + end + ensure + @protocol.close if @protocol + end + end + end + + # List of insecure methods. + # + # These methods are not callable via dRuby. + INSECURE_METHOD = [ + :__send__ + ] + + # Has a method been included in the list of insecure methods? + def insecure_method?(msg_id) + INSECURE_METHOD.include?(msg_id) + end + + # Coerce an object to a string, providing our own representation if + # to_s is not defined for the object. + def any_to_s(obj) + obj.to_s + ":#{obj.class}" + rescue + sprintf("#<%s:0x%lx>", obj.class, obj.__id__) + end + + # Check that a method is callable via dRuby. + # + # +obj+ is the object we want to invoke the method on. +msg_id+ is the + # method name, as a Symbol. + # + # If the method is an insecure method (see #insecure_method?) a + # SecurityError is thrown. If the method is private or undefined, + # a NameError is thrown. + def check_insecure_method(obj, msg_id) + return true if Proc === obj && msg_id == :__drb_yield + raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class + raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id) + + if obj.private_methods.include?(msg_id) + desc = any_to_s(obj) + raise NoMethodError, "private method `#{msg_id}' called for #{desc}" + elsif obj.protected_methods.include?(msg_id) + desc = any_to_s(obj) + raise NoMethodError, "protected method `#{msg_id}' called for #{desc}" + else + true + end + end + public :check_insecure_method + + class InvokeMethod # :nodoc: + def initialize(drb_server, client) + @drb_server = drb_server + @safe_level = drb_server.safe_level + @client = client + end + + def perform + @result = nil + @succ = false + setup_message + + if $SAFE < @safe_level + info = Thread.current['DRb'] + if @block + @result = Thread.new { + Thread.current['DRb'] = info + $SAFE = @safe_level + perform_with_block + }.value + else + @result = Thread.new { + Thread.current['DRb'] = info + $SAFE = @safe_level + perform_without_block + }.value + end + else + if @block + @result = perform_with_block + else + @result = perform_without_block + end + end + @succ = true + if @msg_id == :to_ary && @result.class == Array + @result = DRbArray.new(@result) + end + return @succ, @result + rescue StandardError, ScriptError, Interrupt + @result = $! + return @succ, @result + end + + private + def init_with_client + obj, msg, argv, block = @client.recv_request + @obj = obj + @msg_id = msg.intern + @argv = argv + @block = block + end + + def check_insecure_method + @drb_server.check_insecure_method(@obj, @msg_id) + end + + def setup_message + init_with_client + check_insecure_method + end + + def perform_without_block + if Proc === @obj && @msg_id == :__drb_yield + if @argv.size == 1 + ary = @argv + else + ary = [@argv] + end + ary.collect(&@obj)[0] + else + @obj.__send__(@msg_id, *@argv) + end + end + + end + + if RUBY_VERSION >= '1.8' + require 'drb/invokemethod' + class InvokeMethod + include InvokeMethod18Mixin + end + else + require 'drb/invokemethod16' + class InvokeMethod + include InvokeMethod16Mixin + end + end + + # The main loop performed by a DRbServer's internal thread. + # + # Accepts a connection from a client, and starts up its own + # thread to handle it. This thread loops, receiving requests + # from the client, invoking them on a local object, and + # returning responses, until the client closes the connection + # or a local method call fails. + def main_loop + Thread.start(@protocol.accept) do |client| + @grp.add Thread.current + Thread.current['DRb'] = { 'client' => client , + 'server' => self } + DRb.mutex.synchronize do + client_uri = client.uri + @exported_uri << client_uri unless @exported_uri.include?(client_uri) + end + loop do + begin + succ = false + invoke_method = InvokeMethod.new(self, client) + succ, result = invoke_method.perform + if !succ && verbose + p result + result.backtrace.each do |x| + puts x + end + end + client.send_reply(succ, result) rescue nil + ensure + client.close unless succ + if Thread.current['DRb']['stop_service'] + Thread.new { stop_service } + end + break unless succ + end + end + end + end + end + + @primary_server = nil + + # Start a dRuby server locally. + # + # The new dRuby server will become the primary server, even + # if another server is currently the primary server. + # + # +uri+ is the URI for the server to bind to. If nil, + # the server will bind to random port on the default local host + # name and use the default dRuby protocol. + # + # +front+ is the server's front object. This may be nil. + # + # +config+ is the configuration for the new server. This may + # be nil. + # + # See DRbServer::new. + def start_service(uri=nil, front=nil, config=nil) + @primary_server = DRbServer.new(uri, front, config) + end + module_function :start_service + + # The primary local dRuby server. + # + # This is the server created by the #start_service call. + attr_accessor :primary_server + module_function :primary_server=, :primary_server + + # Get the 'current' server. + # + # In the context of execution taking place within the main + # thread of a dRuby server (typically, as a result of a remote + # call on the server or one of its objects), the current + # server is that server. Otherwise, the current server is + # the primary server. + # + # If the above rule fails to find a server, a DRbServerNotFound + # error is raised. + def current_server + drb = Thread.current['DRb'] + server = (drb && drb['server']) ? drb['server'] : @primary_server + raise DRbServerNotFound unless server + return server + end + module_function :current_server + + # Stop the local dRuby server. + # + # This operates on the primary server. If there is no primary + # server currently running, it is a noop. + def stop_service + @primary_server.stop_service if @primary_server + @primary_server = nil + end + module_function :stop_service + + # Get the URI defining the local dRuby space. + # + # This is the URI of the current server. See #current_server. + def uri + drb = Thread.current['DRb'] + client = (drb && drb['client']) + if client + uri = client.uri + return uri if uri + end + current_server.uri + end + module_function :uri + + # Is +uri+ the URI for the current local server? + def here?(uri) + current_server.here?(uri) rescue false + # (current_server.uri rescue nil) == uri + end + module_function :here? + + # Get the configuration of the current server. + # + # If there is no current server, this returns the default configuration. + # See #current_server and DRbServer::make_config. + def config + current_server.config + rescue + DRbServer.make_config + end + module_function :config + + # Get the front object of the current server. + # + # This raises a DRbServerNotFound error if there is no current server. + # See #current_server. + def front + current_server.front + end + module_function :front + + # Convert a reference into an object using the current server. + # + # This raises a DRbServerNotFound error if there is no current server. + # See #current_server. + def to_obj(ref) + current_server.to_obj(ref) + end + + # Get a reference id for an object using the current server. + # + # This raises a DRbServerNotFound error if there is no current server. + # See #current_server. + def to_id(obj) + current_server.to_id(obj) + end + module_function :to_id + module_function :to_obj + + # Get the thread of the primary server. + # + # This returns nil if there is no primary server. See #primary_server. + def thread + @primary_server ? @primary_server.thread : nil + end + module_function :thread + + # Set the default id conversion object. + # + # This is expected to be an instance such as DRb::DRbIdConv that responds to + # #to_id and #to_obj that can convert objects to and from DRb references. + # + # See DRbServer#default_id_conv. + def install_id_conv(idconv) + DRbServer.default_id_conv(idconv) + end + module_function :install_id_conv + + # Set the default ACL to +acl+. + # + # See DRb::DRbServer.default_acl. + def install_acl(acl) + DRbServer.default_acl(acl) + end + module_function :install_acl + + @mutex = Mutex.new + def mutex # :nodoc: + @mutex + end + module_function :mutex + + @server = {} + # Registers +server+ with DRb. + # + # This is called when a new DRb::DRbServer is created. + # + # If there is no primary server then +server+ becomes the primary server. + # + # Example: + # + # require 'drb' + # + # s = DRb::DRbServer.new # automatically calls regist_server + # DRb.fetch_server s.uri #=> # + def regist_server(server) + @server[server.uri] = server + mutex.synchronize do + @primary_server = server unless @primary_server + end + end + module_function :regist_server + + # Removes +server+ from the list of registered servers. + def remove_server(server) + @server.delete(server.uri) + end + module_function :remove_server + + # Retrieves the server with the given +uri+. + # + # See also regist_server and remove_server. + def fetch_server(uri) + @server[uri] + end + module_function :fetch_server +end + +# :stopdoc: +DRbObject = DRb::DRbObject +DRbUndumped = DRb::DRbUndumped +DRbIdConv = DRb::DRbIdConv diff --git a/ruby/lib/ruby/2.1.0/drb/eq.rb b/ruby/lib/ruby/2.1.0/drb/eq.rb new file mode 100644 index 0000000..553f30c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/eq.rb @@ -0,0 +1,14 @@ +module DRb + class DRbObject # :nodoc: + def ==(other) + return false unless DRbObject === other + (@ref == other.__drbref) && (@uri == other.__drburi) + end + + def hash + [@uri, @ref].hash + end + + alias eql? == + end +end diff --git a/ruby/lib/ruby/2.1.0/drb/extserv.rb b/ruby/lib/ruby/2.1.0/drb/extserv.rb new file mode 100644 index 0000000..c70ced8 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/extserv.rb @@ -0,0 +1,73 @@ +=begin + external service + Copyright (c) 2000,2002 Masatoshi SEKI +=end + +require 'drb/drb' +require 'monitor' + +module DRb + class ExtServ + include MonitorMixin + include DRbUndumped + + def initialize(there, name, server=nil) + super() + @server = server || DRb::primary_server + @name = name + ro = DRbObject.new(nil, there) + synchronize do + @invoker = ro.regist(name, DRbObject.new(self, @server.uri)) + end + end + attr_reader :server + + def front + DRbObject.new(nil, @server.uri) + end + + def stop_service + synchronize do + @invoker.unregist(@name) + server = @server + @server = nil + server.stop_service + true + end + end + + def alive? + @server ? @server.alive? : false + end + end +end + +# :stopdoc: + +if __FILE__ == $0 + class Foo + include DRbUndumped + + def initialize(str) + @str = str + end + + def hello(it) + "#{it}: #{self}" + end + + def to_s + @str + end + end + + cmd = ARGV.shift + case cmd + when 'itest1', 'itest2' + front = Foo.new(cmd) + manager = DRb::DRbServer.new(nil, front) + es = DRb::ExtServ.new(ARGV.shift, ARGV.shift, manager) + es.server.thread.join + end +end + diff --git a/ruby/lib/ruby/2.1.0/drb/extservm.rb b/ruby/lib/ruby/2.1.0/drb/extservm.rb new file mode 100644 index 0000000..8a7fc31 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/extservm.rb @@ -0,0 +1,93 @@ +=begin + external service manager + Copyright (c) 2000 Masatoshi SEKI +=end + +require 'drb/drb' +require 'thread' +require 'monitor' + +module DRb + class ExtServManager + include DRbUndumped + include MonitorMixin + + @@command = {} + + def self.command + @@command + end + + def self.command=(cmd) + @@command = cmd + end + + def initialize + super() + @cond = new_cond + @servers = {} + @waiting = [] + @queue = Queue.new + @thread = invoke_thread + @uri = nil + end + attr_accessor :uri + + def service(name) + synchronize do + while true + server = @servers[name] + return server if server && server.alive? + invoke_service(name) + @cond.wait + end + end + end + + def regist(name, ro) + synchronize do + @servers[name] = ro + @cond.signal + end + self + end + + def unregist(name) + synchronize do + @servers.delete(name) + end + end + + private + def invoke_thread + Thread.new do + while true + name = @queue.pop + invoke_service_command(name, @@command[name]) + end + end + end + + def invoke_service(name) + @queue.push(name) + end + + def invoke_service_command(name, command) + raise "invalid command. name: #{name}" unless command + synchronize do + return if @servers.include?(name) + @servers[name] = false + end + uri = @uri || DRb.uri + if command.respond_to? :to_ary + command = command.to_ary + [uri, name] + pid = spawn(*command) + else + pid = spawn("#{command} #{uri} #{name}") + end + th = Process.detach(pid) + th[:drb_service] = name + th + end + end +end diff --git a/ruby/lib/ruby/2.1.0/drb/gw.rb b/ruby/lib/ruby/2.1.0/drb/gw.rb new file mode 100644 index 0000000..b3568ab --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/gw.rb @@ -0,0 +1,160 @@ +require 'drb/drb' +require 'monitor' + +module DRb + + # Gateway id conversion forms a gateway between different DRb protocols or + # networks. + # + # The gateway needs to install this id conversion and create servers for + # each of the protocols or networks it will be a gateway between. It then + # needs to create a server that attaches to each of these networks. For + # example: + # + # require 'drb/drb' + # require 'drb/unix' + # require 'drb/gw' + # + # DRb.install_id_conv DRb::GWIdConv.new + # gw = DRb::GW.new + # s1 = DRb::DRbServer.new 'drbunix:/path/to/gateway', gw + # s2 = DRb::DRbServer.new 'druby://example:10000', gw + # + # s1.thread.join + # s2.thread.join + # + # Each client must register services with the gateway, for example: + # + # DRb.start_service 'drbunix:', nil # an anonymous server + # gw = DRbObject.new nil, 'drbunix:/path/to/gateway' + # gw[:unix] = some_service + # DRb.thread.join + + class GWIdConv < DRbIdConv + def to_obj(ref) # :nodoc: + if Array === ref && ref[0] == :DRbObject + return DRbObject.new_with(ref[1], ref[2]) + end + super(ref) + end + end + + # The GW provides a synchronized store for participants in the gateway to + # communicate. + + class GW + include MonitorMixin + + # Creates a new GW + + def initialize + super() + @hash = {} + end + + # Retrieves +key+ from the GW + + def [](key) + synchronize do + @hash[key] + end + end + + # Stores value +v+ at +key+ in the GW + + def []=(key, v) + synchronize do + @hash[key] = v + end + end + end + + class DRbObject # :nodoc: + def self._load(s) + uri, ref = Marshal.load(s) + if DRb.uri == uri + return ref ? DRb.to_obj(ref) : DRb.front + end + + self.new_with(DRb.uri, [:DRbObject, uri, ref]) + end + + def _dump(lv) + if DRb.uri == @uri + if Array === @ref && @ref[0] == :DRbObject + Marshal.dump([@ref[1], @ref[2]]) + else + Marshal.dump([@uri, @ref]) # ?? + end + else + Marshal.dump([DRb.uri, [:DRbObject, @uri, @ref]]) + end + end + end +end + +=begin +DRb.install_id_conv(DRb::GWIdConv.new) + +front = DRb::GW.new + +s1 = DRb::DRbServer.new('drbunix:/tmp/gw_b_a', front) +s2 = DRb::DRbServer.new('drbunix:/tmp/gw_b_c', front) + +s1.thread.join +s2.thread.join +=end + +=begin +# foo.rb + +require 'drb/drb' + +class Foo + include DRbUndumped + def initialize(name, peer=nil) + @name = name + @peer = peer + end + + def ping(obj) + puts "#{@name}: ping: #{obj.inspect}" + @peer.ping(self) if @peer + end +end +=end + +=begin +# gw_a.rb +require 'drb/unix' +require 'foo' + +obj = Foo.new('a') +DRb.start_service("drbunix:/tmp/gw_a", obj) + +robj = DRbObject.new_with_uri('drbunix:/tmp/gw_b_a') +robj[:a] = obj + +DRb.thread.join +=end + +=begin +# gw_c.rb +require 'drb/unix' +require 'foo' + +foo = Foo.new('c', nil) + +DRb.start_service("drbunix:/tmp/gw_c", nil) + +robj = DRbObject.new_with_uri("drbunix:/tmp/gw_b_c") + +puts "c->b" +a = robj[:a] +sleep 2 + +a.ping(foo) + +DRb.thread.join +=end + diff --git a/ruby/lib/ruby/2.1.0/drb/invokemethod.rb b/ruby/lib/ruby/2.1.0/drb/invokemethod.rb new file mode 100644 index 0000000..71ebec1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/invokemethod.rb @@ -0,0 +1,34 @@ +# for ruby-1.8.0 + +module DRb # :nodoc: all + class DRbServer + module InvokeMethod18Mixin + def block_yield(x) + if x.size == 1 && x[0].class == Array + x[0] = DRbArray.new(x[0]) + end + @block.call(*x) + end + + def perform_with_block + @obj.__send__(@msg_id, *@argv) do |*x| + jump_error = nil + begin + block_value = block_yield(x) + rescue LocalJumpError + jump_error = $! + end + if jump_error + case jump_error.reason + when :break + break(jump_error.exit_value) + else + raise jump_error + end + end + block_value + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/drb/observer.rb b/ruby/lib/ruby/2.1.0/drb/observer.rb new file mode 100644 index 0000000..cab9ebc --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/observer.rb @@ -0,0 +1,25 @@ +require 'observer' + +module DRb + # The Observable module extended to DRb. See Observable for details. + module DRbObservable + include Observable + + # Notifies observers of a change in state. See also + # Observable#notify_observers + def notify_observers(*arg) + if defined? @observer_state and @observer_state + if defined? @observer_peers + @observer_peers.each do |observer, method| + begin + observer.send(method, *arg) + rescue + delete_observer(observer) + end + end + end + @observer_state = false + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/drb/ssl.rb b/ruby/lib/ruby/2.1.0/drb/ssl.rb new file mode 100644 index 0000000..78c0038 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/ssl.rb @@ -0,0 +1,344 @@ +require 'socket' +require 'openssl' +require 'drb/drb' +require 'singleton' + +module DRb + + # The protocol for DRb over an SSL socket + # + # The URI for a DRb socket over SSL is: + # drbssl://:?. The option is optional + class DRbSSLSocket < DRbTCPSocket + + # SSLConfig handles the needed SSL information for establishing a + # DRbSSLSocket connection, including generating the X509 / RSA pair. + # + # An instance of this config can be passed to DRbSSLSocket.new, + # DRbSSLSocket.open and DRbSSLSocket.open_server + # + # See DRb::DRbSSLSocket::SSLConfig.new for more details + class SSLConfig + + # Default values for a SSLConfig instance. + # + # See DRb::DRbSSLSocket::SSLConfig.new for more details + DEFAULT = { + :SSLCertificate => nil, + :SSLPrivateKey => nil, + :SSLClientCA => nil, + :SSLCACertificatePath => nil, + :SSLCACertificateFile => nil, + :SSLTmpDhCallback => nil, + :SSLVerifyMode => ::OpenSSL::SSL::VERIFY_NONE, + :SSLVerifyDepth => nil, + :SSLVerifyCallback => nil, # custom verification + :SSLCertificateStore => nil, + # Must specify if you use auto generated certificate. + :SSLCertName => nil, # e.g. [["CN","fqdn.example.com"]] + :SSLCertComment => "Generated by Ruby/OpenSSL" + } + + # Create a new DRb::DRbSSLSocket::SSLConfig instance + # + # The DRb::DRbSSLSocket will take either a +config+ Hash or an instance + # of SSLConfig, and will setup the certificate for its session for the + # configuration. If want it to generate a generic certificate, the bare + # minimum is to provide the :SSLCertName + # + # === Config options + # + # From +config+ Hash: + # + # :SSLCertificate :: + # An instance of OpenSSL::X509::Certificate. If this is not provided, + # then a generic X509 is generated, with a correspond :SSLPrivateKey + # + # :SSLPrivateKey :: + # A private key instance, like OpenSSL::PKey::RSA. This key must be + # the key that signed the :SSLCertificate + # + # :SSLClientCA :: + # An OpenSSL::X509::Certificate, or Array of certificates that will + # used as ClientCAs in the SSL Context + # + # :SSLCACertificatePath :: + # A path to the directory of CA certificates. The certificates must + # be in PEM format. + # + # :SSLCACertificateFile :: + # A path to a CA certificate file, in PEM format. + # + # :SSLTmpDhCallback :: + # A DH callback. See OpenSSL::SSL::SSLContext.tmp_dh_callback + # + # :SSLVerifyMode :: + # This is the SSL verification mode. See OpenSSL::SSL::VERIFY_* for + # available modes. The default is OpenSSL::SSL::VERIFY_NONE + # + # :SSLVerifyDepth :: + # Number of CA certificates to walk, when verifying a certificate + # chain. + # + # :SSLVerifyCallback :: + # A callback to be used for additional verification. See + # OpenSSL::SSL::SSLContext.verify_callback + # + # :SSLCertificateStore :: + # A OpenSSL::X509::Store used for verification of certificates + # + # :SSLCertName :: + # Issuer name for the certificate. This is required when generating + # the certificate (if :SSLCertificate and :SSLPrivateKey were not + # given). The value of this is to be an Array of pairs: + # + # [["C", "Raleigh"], ["ST","North Carolina"], + # ["CN","fqdn.example.com"]] + # + # See also OpenSSL::X509::Name + # + # :SSLCertComment :: + # A comment to be used for generating the certificate. The default is + # "Generated by Ruby/OpenSSL" + # + # + # === Example + # + # These values can be added after the fact, like a Hash. + # + # require 'drb/ssl' + # c = DRb::DRbSSLSocket::SSLConfig.new {} + # c[:SSLCertificate] = + # OpenSSL::X509::Certificate.new(File.read('mycert.crt')) + # c[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(File.read('mycert.key')) + # c[:SSLVerifyMode] = OpenSSL::SSL::VERIFY_PEER + # c[:SSLCACertificatePath] = "/etc/ssl/certs/" + # c.setup_certificate + # + # or + # + # require 'drb/ssl' + # c = DRb::DRbSSLSocket::SSLConfig.new({ + # :SSLCertName => [["CN" => DRb::DRbSSLSocket.getservername]] + # }) + # c.setup_certificate + # + def initialize(config) + @config = config + @cert = config[:SSLCertificate] + @pkey = config[:SSLPrivateKey] + @ssl_ctx = nil + end + + # A convenience method to access the values like a Hash + def [](key); + @config[key] || DEFAULT[key] + end + + # Connect to IO +tcp+, with context of the current certificate + # configuration + def connect(tcp) + ssl = ::OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx) + ssl.sync = true + ssl.connect + ssl + end + + # Accept connection to IO +tcp+, with context of the current certificate + # configuration + def accept(tcp) + ssl = OpenSSL::SSL::SSLSocket.new(tcp, @ssl_ctx) + ssl.sync = true + ssl.accept + ssl + end + + # Ensures that :SSLCertificate and :SSLPrivateKey have been provided + # or that a new certificate is generated with the other parameters + # provided. + def setup_certificate + if @cert && @pkey + return + end + + rsa = OpenSSL::PKey::RSA.new(1024){|p, n| + next unless self[:verbose] + case p + when 0; $stderr.putc "." # BN_generate_prime + when 1; $stderr.putc "+" # BN_generate_prime + when 2; $stderr.putc "*" # searching good prime, + # n = #of try, + # but also data from BN_generate_prime + when 3; $stderr.putc "\n" # found good prime, n==0 - p, n==1 - q, + # but also data from BN_generate_prime + else; $stderr.putc "*" # BN_generate_prime + end + } + + cert = OpenSSL::X509::Certificate.new + cert.version = 3 + cert.serial = 0 + name = OpenSSL::X509::Name.new(self[:SSLCertName]) + cert.subject = name + cert.issuer = name + cert.not_before = Time.now + cert.not_after = Time.now + (365*24*60*60) + cert.public_key = rsa.public_key + + ef = OpenSSL::X509::ExtensionFactory.new(nil,cert) + cert.extensions = [ + ef.create_extension("basicConstraints","CA:FALSE"), + ef.create_extension("subjectKeyIdentifier", "hash") ] + ef.issuer_certificate = cert + cert.add_extension(ef.create_extension("authorityKeyIdentifier", + "keyid:always,issuer:always")) + if comment = self[:SSLCertComment] + cert.add_extension(ef.create_extension("nsComment", comment)) + end + cert.sign(rsa, OpenSSL::Digest::SHA1.new) + + @cert = cert + @pkey = rsa + end + + # Establish the OpenSSL::SSL::SSLContext with the configuration + # parameters provided. + def setup_ssl_context + ctx = ::OpenSSL::SSL::SSLContext.new + ctx.cert = @cert + ctx.key = @pkey + ctx.client_ca = self[:SSLClientCA] + ctx.ca_path = self[:SSLCACertificatePath] + ctx.ca_file = self[:SSLCACertificateFile] + ctx.tmp_dh_callback = self[:SSLTmpDhCallback] + ctx.verify_mode = self[:SSLVerifyMode] + ctx.verify_depth = self[:SSLVerifyDepth] + ctx.verify_callback = self[:SSLVerifyCallback] + ctx.cert_store = self[:SSLCertificateStore] + @ssl_ctx = ctx + end + end + + # Parse the dRuby +uri+ for an SSL connection. + # + # Expects drbssl://... + # + # Raises DRbBadScheme or DRbBadURI if +uri+ is not matching or malformed + def self.parse_uri(uri) # :nodoc: + if uri =~ /^drbssl:\/\/(.*?):(\d+)(\?(.*))?$/ + host = $1 + port = $2.to_i + option = $4 + [host, port, option] + else + raise(DRbBadScheme, uri) unless uri =~ /^drbssl:/ + raise(DRbBadURI, 'can\'t parse uri:' + uri) + end + end + + # Return an DRb::DRbSSLSocket instance as a client-side connection, + # with the SSL connected. This is called from DRb::start_service or while + # connecting to a remote object: + # + # DRb.start_service 'drbssl://localhost:0', front, config + # + # +uri+ is the URI we are connected to, + # 'drbssl://localhost:0' above, +config+ is our + # configuration. Either a Hash or DRb::DRbSSLSocket::SSLConfig + def self.open(uri, config) + host, port, = parse_uri(uri) + host.untaint + port.untaint + soc = TCPSocket.open(host, port) + ssl_conf = SSLConfig::new(config) + ssl_conf.setup_ssl_context + ssl = ssl_conf.connect(soc) + self.new(uri, ssl, ssl_conf, true) + end + + # Returns a DRb::DRbSSLSocket instance as a server-side connection, with + # the SSL connected. This is called from DRb::start_service or while + # connecting to a remote object: + # + # DRb.start_service 'drbssl://localhost:0', front, config + # + # +uri+ is the URI we are connected to, + # 'drbssl://localhost:0' above, +config+ is our + # configuration. Either a Hash or DRb::DRbSSLSocket::SSLConfig + def self.open_server(uri, config) + uri = 'drbssl://:0' unless uri + host, port, = parse_uri(uri) + if host.size == 0 + host = getservername + soc = open_server_inaddr_any(host, port) + else + soc = TCPServer.open(host, port) + end + port = soc.addr[1] if port == 0 + @uri = "drbssl://#{host}:#{port}" + + ssl_conf = SSLConfig.new(config) + ssl_conf.setup_certificate + ssl_conf.setup_ssl_context + self.new(@uri, soc, ssl_conf, false) + end + + # This is a convenience method to parse +uri+ and separate out any + # additional options appended in the +uri+. + # + # Returns an option-less uri and the option => [uri,option] + # + # The +config+ is completely unused, so passing nil is sufficient. + def self.uri_option(uri, config) # :nodoc: + host, port, option = parse_uri(uri) + return "drbssl://#{host}:#{port}", option + end + + # Create a DRb::DRbSSLSocket instance. + # + # +uri+ is the URI we are connected to. + # +soc+ is the tcp socket we are bound to. + # +config+ is our configuration. Either a Hash or SSLConfig + # +is_established+ is a boolean of whether +soc+ is currently established + # + # This is called automatically based on the DRb protocol. + def initialize(uri, soc, config, is_established) + @ssl = is_established ? soc : nil + super(uri, soc.to_io, config) + end + + # Returns the SSL stream + def stream; @ssl; end # :nodoc: + + # Closes the SSL stream before closing the dRuby connection. + def close # :nodoc: + if @ssl + @ssl.close + @ssl = nil + end + super + end + + def accept # :nodoc: + begin + while true + soc = @socket.accept + break if (@acl ? @acl.allow_socket?(soc) : true) + soc.close + end + begin + ssl = @config.accept(soc) + rescue Exception + soc.close + raise + end + self.class.new(uri, ssl, @config, true) + rescue OpenSSL::SSL::SSLError + warn("#{__FILE__}:#{__LINE__}: warning: #{$!.message} (#{$!.class})") if @config[:verbose] + retry + end + end + end + + DRbProtocol.add_protocol(DRbSSLSocket) +end diff --git a/ruby/lib/ruby/2.1.0/drb/timeridconv.rb b/ruby/lib/ruby/2.1.0/drb/timeridconv.rb new file mode 100644 index 0000000..4ea3035 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/timeridconv.rb @@ -0,0 +1,101 @@ +require 'drb/drb' +require 'monitor' + +module DRb + + # Timer id conversion keeps objects alive for a certain amount of time after + # their last access. The default time period is 600 seconds and can be + # changed upon initialization. + # + # To use TimerIdConv: + # + # DRb.install_id_conv TimerIdConv.new 60 # one minute + + class TimerIdConv < DRbIdConv + class TimerHolder2 # :nodoc: + include MonitorMixin + + class InvalidIndexError < RuntimeError; end + + def initialize(timeout=600) + super() + @sentinel = Object.new + @gc = {} + @curr = {} + @renew = {} + @timeout = timeout + @keeper = keeper + end + + def add(obj) + synchronize do + key = obj.__id__ + @curr[key] = obj + return key + end + end + + def fetch(key, dv=@sentinel) + synchronize do + obj = peek(key) + if obj == @sentinel + return dv unless dv == @sentinel + raise InvalidIndexError + end + @renew[key] = obj # KeepIt + return obj + end + end + + def include?(key) + synchronize do + obj = peek(key) + return false if obj == @sentinel + true + end + end + + def peek(key) + synchronize do + return @curr.fetch(key, @renew.fetch(key, @gc.fetch(key, @sentinel))) + end + end + + private + def alternate + synchronize do + @gc = @curr # GCed + @curr = @renew + @renew = {} + end + end + + def keeper + Thread.new do + loop do + alternate + sleep(@timeout) + end + end + end + end + + # Creates a new TimerIdConv which will hold objects for +timeout+ seconds. + def initialize(timeout=600) + @holder = TimerHolder2.new(timeout) + end + + def to_obj(ref) # :nodoc: + return super if ref.nil? + @holder.fetch(ref) + rescue TimerHolder2::InvalidIndexError + raise "invalid reference" + end + + def to_id(obj) # :nodoc: + return @holder.add(obj) + end + end +end + +# DRb.install_id_conv(TimerIdConv.new) diff --git a/ruby/lib/ruby/2.1.0/drb/unix.rb b/ruby/lib/ruby/2.1.0/drb/unix.rb new file mode 100644 index 0000000..4d24578 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/drb/unix.rb @@ -0,0 +1,115 @@ +require 'socket' +require 'drb/drb' +require 'tmpdir' + +raise(LoadError, "UNIXServer is required") unless defined?(UNIXServer) + +module DRb + + # Implements DRb over a UNIX socket + # + # DRb UNIX socket URIs look like drbunix:?. The + # option is optional. + + class DRbUNIXSocket < DRbTCPSocket + # :stopdoc: + def self.parse_uri(uri) + if /^drbunix:(.*?)(\?(.*))?$/ =~ uri + filename = $1 + option = $3 + [filename, option] + else + raise(DRbBadScheme, uri) unless uri =~ /^drbunix:/ + raise(DRbBadURI, 'can\'t parse uri:' + uri) + end + end + + def self.open(uri, config) + filename, = parse_uri(uri) + filename.untaint + soc = UNIXSocket.open(filename) + self.new(uri, soc, config) + end + + def self.open_server(uri, config) + filename, = parse_uri(uri) + if filename.size == 0 + soc = temp_server + filename = soc.path + uri = 'drbunix:' + soc.path + else + soc = UNIXServer.open(filename) + end + owner = config[:UNIXFileOwner] + group = config[:UNIXFileGroup] + if owner || group + require 'etc' + owner = Etc.getpwnam( owner ).uid if owner + group = Etc.getgrnam( group ).gid if group + File.chown owner, group, filename + end + mode = config[:UNIXFileMode] + File.chmod(mode, filename) if mode + + self.new(uri, soc, config, true) + end + + def self.uri_option(uri, config) + filename, option = parse_uri(uri) + return "drbunix:#{filename}", option + end + + def initialize(uri, soc, config={}, server_mode = false) + super(uri, soc, config) + set_sockopt(@socket) + @server_mode = server_mode + @acl = nil + end + + # import from tempfile.rb + Max_try = 10 + private + def self.temp_server + tmpdir = Dir::tmpdir + n = 0 + while true + begin + tmpname = sprintf('%s/druby%d.%d', tmpdir, $$, n) + lock = tmpname + '.lock' + unless File.exist?(tmpname) or File.exist?(lock) + Dir.mkdir(lock) + break + end + rescue + raise "cannot generate tempfile `%s'" % tmpname if n >= Max_try + #sleep(1) + end + n += 1 + end + soc = UNIXServer.new(tmpname) + Dir.rmdir(lock) + soc + end + + public + def close + return unless @socket + path = @socket.path if @server_mode + @socket.close + File.unlink(path) if @server_mode + @socket = nil + end + + def accept + s = @socket.accept + self.class.new(nil, s, @config) + end + + def set_sockopt(soc) + soc.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined? Fcntl::FD_CLOEXEC + end + end + + DRbProtocol.add_protocol(DRbUNIXSocket) + # :startdoc: +end diff --git a/ruby/lib/ruby/2.1.0/e2mmap.rb b/ruby/lib/ruby/2.1.0/e2mmap.rb new file mode 100644 index 0000000..b75c431 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/e2mmap.rb @@ -0,0 +1,176 @@ +# +#-- +# e2mmap.rb - for Ruby 1.1 +# $Release Version: 2.0$ +# $Revision: 1.10 $ +# by Keiju ISHITSUKA +# +#++ +# +# Helper module for easily defining exceptions with predefined messages. +# +# == Usage +# +# 1. +# class Foo +# extend Exception2MessageMapper +# def_e2message ExistingExceptionClass, "message..." +# def_exception :NewExceptionClass, "message..."[, superclass] +# ... +# end +# +# 2. +# module Error +# extend Exception2MessageMapper +# def_e2message ExistingExceptionClass, "message..." +# def_exception :NewExceptionClass, "message..."[, superclass] +# ... +# end +# class Foo +# include Error +# ... +# end +# +# foo = Foo.new +# foo.Fail .... +# +# 3. +# module Error +# extend Exception2MessageMapper +# def_e2message ExistingExceptionClass, "message..." +# def_exception :NewExceptionClass, "message..."[, superclass] +# ... +# end +# class Foo +# extend Exception2MessageMapper +# include Error +# ... +# end +# +# Foo.Fail NewExceptionClass, arg... +# Foo.Fail ExistingExceptionClass, arg... +# +# +module Exception2MessageMapper + @RCS_ID='-$Id: e2mmap.rb,v 1.10 1999/02/17 12:33:17 keiju Exp keiju $-' + + E2MM = Exception2MessageMapper # :nodoc: + + def E2MM.extend_object(cl) + super + cl.bind(self) unless cl < E2MM + end + + def bind(cl) + self.module_eval %[ + def Raise(err = nil, *rest) + Exception2MessageMapper.Raise(self.class, err, *rest) + end + alias Fail Raise + + def self.included(mod) + mod.extend Exception2MessageMapper + end + ] + end + + # Fail(err, *rest) + # err: exception + # rest: message arguments + # + def Raise(err = nil, *rest) + E2MM.Raise(self, err, *rest) + end + alias Fail Raise + alias fail Raise + + # def_e2message(c, m) + # c: exception + # m: message_form + # define exception c with message m. + # + def def_e2message(c, m) + E2MM.def_e2message(self, c, m) + end + + # def_exception(n, m, s) + # n: exception_name + # m: message_form + # s: superclass(default: StandardError) + # define exception named ``c'' with message m. + # + def def_exception(n, m, s = StandardError) + E2MM.def_exception(self, n, m, s) + end + + # + # Private definitions. + # + # {[class, exp] => message, ...} + @MessageMap = {} + + # E2MM.def_e2message(k, e, m) + # k: class to define exception under. + # e: exception + # m: message_form + # define exception c with message m. + # + def E2MM.def_e2message(k, c, m) + E2MM.instance_eval{@MessageMap[[k, c]] = m} + c + end + + # E2MM.def_exception(k, n, m, s) + # k: class to define exception under. + # n: exception_name + # m: message_form + # s: superclass(default: StandardError) + # define exception named ``c'' with message m. + # + def E2MM.def_exception(k, n, m, s = StandardError) + n = n.id2name if n.kind_of?(Fixnum) + e = Class.new(s) + E2MM.instance_eval{@MessageMap[[k, e]] = m} + k.const_set(n, e) + end + + # Fail(klass, err, *rest) + # klass: class to define exception under. + # err: exception + # rest: message arguments + # + def E2MM.Raise(klass = E2MM, err = nil, *rest) + if form = e2mm_message(klass, err) + b = $@.nil? ? caller(1) : $@ + #p $@ + #p __FILE__ + b.shift if b[0] =~ /^#{Regexp.quote(__FILE__)}:/ + raise err, sprintf(form, *rest), b + else + E2MM.Fail E2MM, ErrNotRegisteredException, err.inspect + end + end + class << E2MM + alias Fail Raise + end + + def E2MM.e2mm_message(klass, exp) + for c in klass.ancestors + if mes = @MessageMap[[c,exp]] + #p mes + m = klass.instance_eval('"' + mes + '"') + return m + end + end + nil + end + class << self + alias message e2mm_message + end + + E2MM.def_exception(E2MM, + :ErrNotRegisteredException, + "not registered exception(%s)") +end + + diff --git a/ruby/lib/ruby/2.1.0/erb.rb b/ruby/lib/ruby/2.1.0/erb.rb new file mode 100644 index 0000000..28c6fc6 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/erb.rb @@ -0,0 +1,1009 @@ +# -*- coding: us-ascii -*- +# = ERB -- Ruby Templating +# +# Author:: Masatoshi SEKI +# Documentation:: James Edward Gray II, Gavin Sinclair, and Simon Chiang +# +# See ERB for primary documentation and ERB::Util for a couple of utility +# routines. +# +# Copyright (c) 1999-2000,2002,2003 Masatoshi SEKI +# +# You can redistribute it and/or modify it under the same terms as Ruby. + +require "cgi/util" + +# +# = ERB -- Ruby Templating +# +# == Introduction +# +# ERB provides an easy to use but powerful templating system for Ruby. Using +# ERB, actual Ruby code can be added to any plain text document for the +# purposes of generating document information details and/or flow control. +# +# A very simple example is this: +# +# require 'erb' +# +# x = 42 +# template = ERB.new <<-EOF +# The value of x is: <%= x %> +# EOF +# puts template.result(binding) +# +# Prints: The value of x is: 42 +# +# More complex examples are given below. +# +# +# == Recognized Tags +# +# ERB recognizes certain tags in the provided template and converts them based +# on the rules below: +# +# <% Ruby code -- inline with output %> +# <%= Ruby expression -- replace with result %> +# <%# comment -- ignored -- useful in testing %> +# % a line of Ruby code -- treated as <% line %> (optional -- see ERB.new) +# %% replaced with % if first thing on a line and % processing is used +# <%% or %%> -- replace with <% or %> respectively +# +# All other text is passed through ERB filtering unchanged. +# +# +# == Options +# +# There are several settings you can change when you use ERB: +# * the nature of the tags that are recognized; +# * the value of $SAFE under which the template is run; +# * the binding used to resolve local variables in the template. +# +# See the ERB.new and ERB#result methods for more detail. +# +# == Character encodings +# +# ERB (or Ruby code generated by ERB) returns a string in the same +# character encoding as the input string. When the input string has +# a magic comment, however, it returns a string in the encoding specified +# by the magic comment. +# +# # -*- coding: UTF-8 -*- +# require 'erb' +# +# template = ERB.new < +# \_\_ENCODING\_\_ is <%= \_\_ENCODING\_\_ %>. +# EOF +# puts template.result +# +# Prints: \_\_ENCODING\_\_ is Big5. +# +# +# == Examples +# +# === Plain Text +# +# ERB is useful for any generic templating situation. Note that in this example, we use the +# convenient "% at start of line" tag, and we quote the template literally with +# %q{...} to avoid trouble with the backslash. +# +# require "erb" +# +# # Create template. +# template = %q{ +# From: James Edward Gray II +# To: <%= to %> +# Subject: Addressing Needs +# +# <%= to[/\w+/] %>: +# +# Just wanted to send a quick note assuring that your needs are being +# addressed. +# +# I want you to know that my team will keep working on the issues, +# especially: +# +# <%# ignore numerous minor requests -- focus on priorities %> +# % priorities.each do |priority| +# * <%= priority %> +# % end +# +# Thanks for your patience. +# +# James Edward Gray II +# }.gsub(/^ /, '') +# +# message = ERB.new(template, 0, "%<>") +# +# # Set up template data. +# to = "Community Spokesman " +# priorities = [ "Run Ruby Quiz", +# "Document Modules", +# "Answer Questions on Ruby Talk" ] +# +# # Produce result. +# email = message.result +# puts email +# +# Generates: +# +# From: James Edward Gray II +# To: Community Spokesman +# Subject: Addressing Needs +# +# Community: +# +# Just wanted to send a quick note assuring that your needs are being addressed. +# +# I want you to know that my team will keep working on the issues, especially: +# +# * Run Ruby Quiz +# * Document Modules +# * Answer Questions on Ruby Talk +# +# Thanks for your patience. +# +# James Edward Gray II +# +# === Ruby in HTML +# +# ERB is often used in .rhtml files (HTML with embedded Ruby). Notice the need in +# this example to provide a special binding when the template is run, so that the instance +# variables in the Product object can be resolved. +# +# require "erb" +# +# # Build template data class. +# class Product +# def initialize( code, name, desc, cost ) +# @code = code +# @name = name +# @desc = desc +# @cost = cost +# +# @features = [ ] +# end +# +# def add_feature( feature ) +# @features << feature +# end +# +# # Support templating of member data. +# def get_binding +# binding +# end +# +# # ... +# end +# +# # Create template. +# template = %{ +# +# Ruby Toys -- <%= @name %> +# +# +#

<%= @name %> (<%= @code %>)

+#

<%= @desc %>

+# +#
    +# <% @features.each do |f| %> +#
  • <%= f %>
  • +# <% end %> +#
+# +#

+# <% if @cost < 10 %> +# Only <%= @cost %>!!! +# <% else %> +# Call for a price, today! +# <% end %> +#

+# +# +# +# }.gsub(/^ /, '') +# +# rhtml = ERB.new(template) +# +# # Set up template data. +# toy = Product.new( "TZ-1002", +# "Rubysapien", +# "Geek's Best Friend! Responds to Ruby commands...", +# 999.95 ) +# toy.add_feature("Listens for verbal commands in the Ruby language!") +# toy.add_feature("Ignores Perl, Java, and all C variants.") +# toy.add_feature("Karate-Chop Action!!!") +# toy.add_feature("Matz signature on left leg.") +# toy.add_feature("Gem studded eyes... Rubies, of course!") +# +# # Produce result. +# rhtml.run(toy.get_binding) +# +# Generates (some blank lines removed): +# +# +# Ruby Toys -- Rubysapien +# +# +#

Rubysapien (TZ-1002)

+#

Geek's Best Friend! Responds to Ruby commands...

+# +#
    +#
  • Listens for verbal commands in the Ruby language!
  • +#
  • Ignores Perl, Java, and all C variants.
  • +#
  • Karate-Chop Action!!!
  • +#
  • Matz signature on left leg.
  • +#
  • Gem studded eyes... Rubies, of course!
  • +#
+# +#

+# Call for a price, today! +#

+# +# +# +# +# +# == Notes +# +# There are a variety of templating solutions available in various Ruby projects: +# * ERB's big brother, eRuby, works the same but is written in C for speed; +# * Amrita (smart at producing HTML/XML); +# * cs/Template (written in C for speed); +# * RDoc, distributed with Ruby, uses its own template engine, which can be reused elsewhere; +# * and others; search {RubyGems.org}[https://rubygems.org/] or +# {The Ruby Toolbox}[https://www.ruby-toolbox.com/]. +# +# Rails, the web application framework, uses ERB to create views. +# +class ERB + Revision = '$Date:: 2014-08-11 23:55:58 +0900#$' # :nodoc: #' + + # Returns revision information for the erb.rb module. + def self.version + "erb.rb [2.1.0 #{ERB::Revision.split[1]}]" + end +end + +#-- +# ERB::Compiler +class ERB + # = ERB::Compiler + # + # Compiles ERB templates into Ruby code; the compiled code produces the + # template result when evaluated. ERB::Compiler provides hooks to define how + # generated output is handled. + # + # Internally ERB does something like this to generate the code returned by + # ERB#src: + # + # compiler = ERB::Compiler.new('<>') + # compiler.pre_cmd = ["_erbout=''"] + # compiler.put_cmd = "_erbout.concat" + # compiler.insert_cmd = "_erbout.concat" + # compiler.post_cmd = ["_erbout"] + # + # code, enc = compiler.compile("Got <%= obj %>!\n") + # puts code + # + # Generates: + # + # #coding:UTF-8 + # _erbout=''; _erbout.concat "Got "; _erbout.concat(( obj ).to_s); _erbout.concat "!\n"; _erbout + # + # By default the output is sent to the print method. For example: + # + # compiler = ERB::Compiler.new('<>') + # code, enc = compiler.compile("Got <%= obj %>!\n") + # puts code + # + # Generates: + # + # #coding:UTF-8 + # print "Got "; print(( obj ).to_s); print "!\n" + # + # == Evaluation + # + # The compiled code can be used in any context where the names in the code + # correctly resolve. Using the last example, each of these print 'Got It!' + # + # Evaluate using a variable: + # + # obj = 'It' + # eval code + # + # Evaluate using an input: + # + # mod = Module.new + # mod.module_eval %{ + # def get(obj) + # #{code} + # end + # } + # extend mod + # get('It') + # + # Evaluate using an accessor: + # + # klass = Class.new Object + # klass.class_eval %{ + # attr_accessor :obj + # def initialize(obj) + # @obj = obj + # end + # def get_it + # #{code} + # end + # } + # klass.new('It').get_it + # + # Good! See also ERB#def_method, ERB#def_module, and ERB#def_class. + class Compiler # :nodoc: + class PercentLine # :nodoc: + def initialize(str) + @value = str + end + attr_reader :value + alias :to_s :value + + def empty? + @value.empty? + end + end + + class Scanner # :nodoc: + @scanner_map = {} + def self.regist_scanner(klass, trim_mode, percent) + @scanner_map[[trim_mode, percent]] = klass + end + + def self.default_scanner=(klass) + @default_scanner = klass + end + + def self.make_scanner(src, trim_mode, percent) + klass = @scanner_map.fetch([trim_mode, percent], @default_scanner) + klass.new(src, trim_mode, percent) + end + + def initialize(src, trim_mode, percent) + @src = src + @stag = nil + end + attr_accessor :stag + + def scan; end + end + + class TrimScanner < Scanner # :nodoc: + def initialize(src, trim_mode, percent) + super + @trim_mode = trim_mode + @percent = percent + if @trim_mode == '>' + @scan_line = self.method(:trim_line1) + elsif @trim_mode == '<>' + @scan_line = self.method(:trim_line2) + elsif @trim_mode == '-' + @scan_line = self.method(:explicit_trim_line) + else + @scan_line = self.method(:scan_line) + end + end + attr_accessor :stag + + def scan(&block) + @stag = nil + if @percent + @src.each_line do |line| + percent_line(line, &block) + end + else + @scan_line.call(@src, &block) + end + nil + end + + def percent_line(line, &block) + if @stag || line[0] != ?% + return @scan_line.call(line, &block) + end + + line[0] = '' + if line[0] == ?% + @scan_line.call(line, &block) + else + yield(PercentLine.new(line.chomp)) + end + end + + def scan_line(line) + line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens| + tokens.each do |token| + next if token.empty? + yield(token) + end + end + end + + def trim_line1(line) + line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens| + tokens.each do |token| + next if token.empty? + if token == "%>\n" + yield('%>') + yield(:cr) + else + yield(token) + end + end + end + end + + def trim_line2(line) + head = nil + line.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>\n|%>|\n|\z)/m) do |tokens| + tokens.each do |token| + next if token.empty? + head = token unless head + if token == "%>\n" + yield('%>') + if is_erb_stag?(head) + yield(:cr) + else + yield("\n") + end + head = nil + else + yield(token) + head = nil if token == "\n" + end + end + end + end + + def explicit_trim_line(line) + line.scan(/(.*?)(^[ \t]*<%\-|<%\-|<%%|%%>|<%=|<%#|<%|-%>\n|-%>|%>|\z)/m) do |tokens| + tokens.each do |token| + next if token.empty? + if @stag.nil? && /[ \t]*<%-/ =~ token + yield('<%') + elsif @stag && token == "-%>\n" + yield('%>') + yield(:cr) + elsif @stag && token == '-%>' + yield('%>') + else + yield(token) + end + end + end + end + + ERB_STAG = %w(<%= <%# <%) + def is_erb_stag?(s) + ERB_STAG.member?(s) + end + end + + Scanner.default_scanner = TrimScanner + + class SimpleScanner < Scanner # :nodoc: + def scan + @src.scan(/(.*?)(<%%|%%>|<%=|<%#|<%|%>|\n|\z)/m) do |tokens| + tokens.each do |token| + next if token.empty? + yield(token) + end + end + end + end + + Scanner.regist_scanner(SimpleScanner, nil, false) + + begin + require 'strscan' + class SimpleScanner2 < Scanner # :nodoc: + def scan + stag_reg = /(.*?)(<%%|<%=|<%#|<%|\z)/m + etag_reg = /(.*?)(%%>|%>|\z)/m + scanner = StringScanner.new(@src) + while ! scanner.eos? + scanner.scan(@stag ? etag_reg : stag_reg) + yield(scanner[1]) + yield(scanner[2]) + end + end + end + Scanner.regist_scanner(SimpleScanner2, nil, false) + + class ExplicitScanner < Scanner # :nodoc: + def scan + stag_reg = /(.*?)(^[ \t]*<%-|<%%|<%=|<%#|<%-|<%|\z)/m + etag_reg = /(.*?)(%%>|-%>|%>|\z)/m + scanner = StringScanner.new(@src) + while ! scanner.eos? + scanner.scan(@stag ? etag_reg : stag_reg) + yield(scanner[1]) + + elem = scanner[2] + if /[ \t]*<%-/ =~ elem + yield('<%') + elsif elem == '-%>' + yield('%>') + yield(:cr) if scanner.scan(/(\n|\z)/) + else + yield(elem) + end + end + end + end + Scanner.regist_scanner(ExplicitScanner, '-', false) + + rescue LoadError + end + + class Buffer # :nodoc: + def initialize(compiler, enc=nil) + @compiler = compiler + @line = [] + @script = enc ? "#coding:#{enc.to_s}\n" : "" + @compiler.pre_cmd.each do |x| + push(x) + end + end + attr_reader :script + + def push(cmd) + @line << cmd + end + + def cr + @script << (@line.join('; ')) + @line = [] + @script << "\n" + end + + def close + return unless @line + @compiler.post_cmd.each do |x| + push(x) + end + @script << (@line.join('; ')) + @line = nil + end + end + + def content_dump(s) # :nodoc: + n = s.count("\n") + if n > 0 + s.dump + "\n" * n + else + s.dump + end + end + + def add_put_cmd(out, content) + out.push("#{@put_cmd} #{content_dump(content)}") + end + + def add_insert_cmd(out, content) + out.push("#{@insert_cmd}((#{content}).to_s)") + end + + # Compiles an ERB template into Ruby code. Returns an array of the code + # and encoding like ["code", Encoding]. + def compile(s) + enc = s.encoding + raise ArgumentError, "#{enc} is not ASCII compatible" if enc.dummy? + s = s.b # see String#b + enc = detect_magic_comment(s) || enc + out = Buffer.new(self, enc) + + content = '' + scanner = make_scanner(s) + scanner.scan do |token| + next if token.nil? + next if token == '' + if scanner.stag.nil? + case token + when PercentLine + add_put_cmd(out, content) if content.size > 0 + content = '' + out.push(token.to_s) + out.cr + when :cr + out.cr + when '<%', '<%=', '<%#' + scanner.stag = token + add_put_cmd(out, content) if content.size > 0 + content = '' + when "\n" + content << "\n" + add_put_cmd(out, content) + content = '' + when '<%%' + content << '<%' + else + content << token + end + else + case token + when '%>' + case scanner.stag + when '<%' + if content[-1] == ?\n + content.chop! + out.push(content) + out.cr + else + out.push(content) + end + when '<%=' + add_insert_cmd(out, content) + when '<%#' + # out.push("# #{content_dump(content)}") + end + scanner.stag = nil + content = '' + when '%%>' + content << '%>' + else + content << token + end + end + end + add_put_cmd(out, content) if content.size > 0 + out.close + return out.script, enc + end + + def prepare_trim_mode(mode) # :nodoc: + case mode + when 1 + return [false, '>'] + when 2 + return [false, '<>'] + when 0 + return [false, nil] + when String + perc = mode.include?('%') + if mode.include?('-') + return [perc, '-'] + elsif mode.include?('<>') + return [perc, '<>'] + elsif mode.include?('>') + return [perc, '>'] + else + [perc, nil] + end + else + return [false, nil] + end + end + + def make_scanner(src) # :nodoc: + Scanner.make_scanner(src, @trim_mode, @percent) + end + + # Construct a new compiler using the trim_mode. See ERB::new for available + # trim modes. + def initialize(trim_mode) + @percent, @trim_mode = prepare_trim_mode(trim_mode) + @put_cmd = 'print' + @insert_cmd = @put_cmd + @pre_cmd = [] + @post_cmd = [] + end + attr_reader :percent, :trim_mode + + # The command to handle text that ends with a newline + attr_accessor :put_cmd + + # The command to handle text that is inserted prior to a newline + attr_accessor :insert_cmd + + # An array of commands prepended to compiled code + attr_accessor :pre_cmd + + # An array of commands appended to compiled code + attr_accessor :post_cmd + + private + def detect_magic_comment(s) + if /\A<%#(.*)%>/ =~ s or (@percent and /\A%#(.*)/ =~ s) + comment = $1 + comment = $1 if comment[/-\*-\s*(.*?)\s*-*-$/] + if %r"coding\s*[=:]\s*([[:alnum:]\-_]+)" =~ comment + enc = $1.sub(/-(?:mac|dos|unix)/i, '') + enc = Encoding.find(enc) + end + end + end + end +end + +#-- +# ERB +class ERB + # + # Constructs a new ERB object with the template specified in _str_. + # + # An ERB object works by building a chunk of Ruby code that will output + # the completed template when run. If _safe_level_ is set to a non-nil value, + # ERB code will be run in a separate thread with $SAFE set to the + # provided level. + # + # If _trim_mode_ is passed a String containing one or more of the following + # modifiers, ERB will adjust its code generation as listed: + # + # % enables Ruby code processing for lines beginning with % + # <> omit newline for lines starting with <% and ending in %> + # > omit newline for lines ending in %> + # - omit blank lines ending in -%> + # + # _eoutvar_ can be used to set the name of the variable ERB will build up + # its output in. This is useful when you need to run multiple ERB + # templates through the same binding and/or when you want to control where + # output ends up. Pass the name of the variable to be used inside a String. + # + # === Example + # + # require "erb" + # + # # build data class + # class Listings + # PRODUCT = { :name => "Chicken Fried Steak", + # :desc => "A well messages pattie, breaded and fried.", + # :cost => 9.95 } + # + # attr_reader :product, :price + # + # def initialize( product = "", price = "" ) + # @product = product + # @price = price + # end + # + # def build + # b = binding + # # create and run templates, filling member data variables + # ERB.new(<<-'END_PRODUCT'.gsub(/^\s+/, ""), 0, "", "@product").result b + # <%= PRODUCT[:name] %> + # <%= PRODUCT[:desc] %> + # END_PRODUCT + # ERB.new(<<-'END_PRICE'.gsub(/^\s+/, ""), 0, "", "@price").result b + # <%= PRODUCT[:name] %> -- <%= PRODUCT[:cost] %> + # <%= PRODUCT[:desc] %> + # END_PRICE + # end + # end + # + # # setup template data + # listings = Listings.new + # listings.build + # + # puts listings.product + "\n" + listings.price + # + # _Generates_ + # + # Chicken Fried Steak + # A well messages pattie, breaded and fried. + # + # Chicken Fried Steak -- 9.95 + # A well messages pattie, breaded and fried. + # + def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout') + @safe_level = safe_level + compiler = make_compiler(trim_mode) + set_eoutvar(compiler, eoutvar) + @src, @enc = *compiler.compile(str) + @filename = nil + end + + ## + # Creates a new compiler for ERB. See ERB::Compiler.new for details + + def make_compiler(trim_mode) + ERB::Compiler.new(trim_mode) + end + + # The Ruby code generated by ERB + attr_reader :src + + # The optional _filename_ argument passed to Kernel#eval when the ERB code + # is run + attr_accessor :filename + + # + # Can be used to set _eoutvar_ as described in ERB::new. It's probably + # easier to just use the constructor though, since calling this method + # requires the setup of an ERB _compiler_ object. + # + def set_eoutvar(compiler, eoutvar = '_erbout') + compiler.put_cmd = "#{eoutvar}.concat" + compiler.insert_cmd = "#{eoutvar}.concat" + compiler.pre_cmd = ["#{eoutvar} = ''"] + compiler.post_cmd = ["#{eoutvar}.force_encoding(__ENCODING__)"] + end + + # Generate results and print them. (see ERB#result) + def run(b=new_toplevel) + print self.result(b) + end + + # + # Executes the generated ERB code to produce a completed template, returning + # the results of that code. (See ERB::new for details on how this process + # can be affected by _safe_level_.) + # + # _b_ accepts a Binding object which is used to set the context of + # code evaluation. + # + def result(b=new_toplevel) + if @safe_level + proc { + $SAFE = @safe_level + eval(@src, b, (@filename || '(erb)'), 0) + }.call + else + eval(@src, b, (@filename || '(erb)'), 0) + end + end + + ## + # Returns a new binding each time *near* TOPLEVEL_BINDING for runs that do + # not specify a binding. + + def new_toplevel + TOPLEVEL_BINDING.dup + end + private :new_toplevel + + # Define _methodname_ as instance method of _mod_ from compiled Ruby source. + # + # example: + # filename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtml + # erb = ERB.new(File.read(filename)) + # erb.def_method(MyClass, 'render(arg1, arg2)', filename) + # print MyClass.new.render('foo', 123) + def def_method(mod, methodname, fname='(ERB)') + src = self.src + magic_comment = "#coding:#{@enc}\n" + mod.module_eval do + eval(magic_comment + "def #{methodname}\n" + src + "\nend\n", binding, fname, -2) + end + end + + # Create unnamed module, define _methodname_ as instance method of it, and return it. + # + # example: + # filename = 'example.rhtml' # 'arg1' and 'arg2' are used in example.rhtml + # erb = ERB.new(File.read(filename)) + # erb.filename = filename + # MyModule = erb.def_module('render(arg1, arg2)') + # class MyClass + # include MyModule + # end + def def_module(methodname='erb') + mod = Module.new + def_method(mod, methodname, @filename || '(ERB)') + mod + end + + # Define unnamed class which has _methodname_ as instance method, and return it. + # + # example: + # class MyClass_ + # def initialize(arg1, arg2) + # @arg1 = arg1; @arg2 = arg2 + # end + # end + # filename = 'example.rhtml' # @arg1 and @arg2 are used in example.rhtml + # erb = ERB.new(File.read(filename)) + # erb.filename = filename + # MyClass = erb.def_class(MyClass_, 'render()') + # print MyClass.new('foo', 123).render() + def def_class(superklass=Object, methodname='result') + cls = Class.new(superklass) + def_method(cls, methodname, @filename || '(ERB)') + cls + end +end + +#-- +# ERB::Util +class ERB + # A utility module for conversion routines, often handy in HTML generation. + module Util + public + # + # A utility method for escaping HTML tag characters in _s_. + # + # require "erb" + # include ERB::Util + # + # puts html_escape("is a > 0 & a < 10?") + # + # _Generates_ + # + # is a > 0 & a < 10? + # + def html_escape(s) + CGI.escapeHTML(s.to_s) + end + alias h html_escape + module_function :h + module_function :html_escape + + # + # A utility method for encoding the String _s_ as a URL. + # + # require "erb" + # include ERB::Util + # + # puts url_encode("Programming Ruby: The Pragmatic Programmer's Guide") + # + # _Generates_ + # + # Programming%20Ruby%3A%20%20The%20Pragmatic%20Programmer%27s%20Guide + # + def url_encode(s) + s.to_s.b.gsub(/[^a-zA-Z0-9_\-.]/n) { |m| + sprintf("%%%02X", m.unpack("C")[0]) + } + end + alias u url_encode + module_function :u + module_function :url_encode + end +end + +#-- +# ERB::DefMethod +class ERB + # Utility module to define eRuby script as instance method. + # + # === Example + # + # example.rhtml: + # <% for item in @items %> + # <%= item %> + # <% end %> + # + # example.rb: + # require 'erb' + # class MyClass + # extend ERB::DefMethod + # def_erb_method('render()', 'example.rhtml') + # def initialize(items) + # @items = items + # end + # end + # print MyClass.new([10,20,30]).render() + # + # result: + # + # 10 + # + # 20 + # + # 30 + # + module DefMethod + public + # define _methodname_ as instance method of current module, using ERB + # object or eRuby file + def def_erb_method(methodname, erb_or_fname) + if erb_or_fname.kind_of? String + fname = erb_or_fname + erb = ERB.new(File.read(fname)) + erb.def_method(self, methodname, fname) + else + erb = erb_or_fname + erb.def_method(self, methodname, erb.filename || '(ERB)') + end + end + module_function :def_erb_method + end +end diff --git a/ruby/lib/ruby/2.1.0/fiddle.rb b/ruby/lib/ruby/2.1.0/fiddle.rb new file mode 100644 index 0000000..ae6e299 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/fiddle.rb @@ -0,0 +1,55 @@ +require 'fiddle.so' +require 'fiddle/function' +require 'fiddle/closure' + +module Fiddle + if WINDOWS + # Returns the last win32 +Error+ of the current executing +Thread+ or nil + # if none + def self.win32_last_error + Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] + end + + # Sets the last win32 +Error+ of the current executing +Thread+ to +error+ + def self.win32_last_error= error + Thread.current[:__FIDDLE_WIN32_LAST_ERROR__] = error + end + end + + # Returns the last +Error+ of the current executing +Thread+ or nil if none + def self.last_error + Thread.current[:__FIDDLE_LAST_ERROR__] + end + + # Sets the last +Error+ of the current executing +Thread+ to +error+ + def self.last_error= error + Thread.current[:__DL2_LAST_ERROR__] = error + Thread.current[:__FIDDLE_LAST_ERROR__] = error + end + + # call-seq: dlopen(library) => Fiddle::Handle + # + # Creates a new handler that opens +library+, and returns an instance of + # Fiddle::Handle. + # + # If +nil+ is given for the +library+, Fiddle::Handle::DEFAULT is used, which + # is the equivalent to RTLD_DEFAULT. See man 3 dlopen for more. + # + # lib = Fiddle.dlopen(nil) + # + # The default is dependent on OS, and provide a handle for all libraries + # already loaded. For example, in most cases you can use this to access + # +libc+ functions, or ruby functions like +rb_str_new+. + # + # See Fiddle::Handle.new for more. + def dlopen library + Fiddle::Handle.new library + end + module_function :dlopen + + # Add constants for backwards compat + + RTLD_GLOBAL = Handle::RTLD_GLOBAL # :nodoc: + RTLD_LAZY = Handle::RTLD_LAZY # :nodoc: + RTLD_NOW = Handle::RTLD_NOW # :nodoc: +end diff --git a/ruby/lib/ruby/2.1.0/fiddle/closure.rb b/ruby/lib/ruby/2.1.0/fiddle/closure.rb new file mode 100644 index 0000000..beb90ec --- /dev/null +++ b/ruby/lib/ruby/2.1.0/fiddle/closure.rb @@ -0,0 +1,48 @@ +module Fiddle + class Closure + + # the C type of the return of the FFI closure + attr_reader :ctype + + # arguments of the FFI closure + attr_reader :args + + # Extends Fiddle::Closure to allow for building the closure in a block + class BlockCaller < Fiddle::Closure + + # == Description + # + # Construct a new BlockCaller object. + # + # * +ctype+ is the C type to be returned + # * +args+ are passed the callback + # * +abi+ is the abi of the closure + # + # If there is an error in preparing the +ffi_cif+ or +ffi_prep_closure+, + # then a RuntimeError will be raised. + # + # == Example + # + # include Fiddle + # + # cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one| + # one + # end + # + # func = Function.new(cb, [TYPE_INT], TYPE_INT) + # + def initialize ctype, args, abi = Fiddle::Function::DEFAULT, &block + super(ctype, args, abi) + @block = block + end + + # Calls the constructed BlockCaller, with +args+ + # + # For an example see Fiddle::Closure::BlockCaller.new + # + def call *args + @block.call(*args) + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/fiddle/cparser.rb b/ruby/lib/ruby/2.1.0/fiddle/cparser.rb new file mode 100644 index 0000000..43fb184 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/fiddle/cparser.rb @@ -0,0 +1,176 @@ +module Fiddle + # A mixin that provides methods for parsing C struct and prototype signatures. + # + # == Example + # require 'fiddle/import' + # + # include Fiddle::CParser + # #=> Object + # + # parse_ctype('int increment(int)') + # #=> ["increment", Fiddle::TYPE_INT, [Fiddle::TYPE_INT]] + # + module CParser + # Parses a C struct's members + # + # Example: + # + # include Fiddle::CParser + # #=> Object + # + # parse_struct_signature(['int i', 'char c']) + # #=> [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]] + # + def parse_struct_signature(signature, tymap=nil) + if( signature.is_a?(String) ) + signature = signature.split(/\s*,\s*/) + end + mems = [] + tys = [] + signature.each{|msig| + tks = msig.split(/\s+(\*)?/) + ty = tks[0..-2].join(" ") + member = tks[-1] + + case ty + when /\[(\d+)\]/ + n = $1.to_i + ty.gsub!(/\s*\[\d+\]/,"") + ty = [ty, n] + when /\[\]/ + ty.gsub!(/\s*\[\]/, "*") + end + + case member + when /\[(\d+)\]/ + ty = [ty, $1.to_i] + member.gsub!(/\s*\[\d+\]/,"") + when /\[\]/ + ty = ty + "*" + member.gsub!(/\s*\[\]/, "") + end + + mems.push(member) + tys.push(parse_ctype(ty,tymap)) + } + return tys, mems + end + + # Parses a C prototype signature + # + # If Hash +tymap+ is provided, the return value and the arguments from the + # +signature+ are expected to be keys, and the value will be the C type to + # be looked up. + # + # Example: + # + # include Fiddle::CParser + # #=> Object + # + # parse_signature('double sum(double, double)') + # #=> ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]] + # + def parse_signature(signature, tymap=nil) + tymap ||= {} + signature = signature.gsub(/\s+/, " ").strip + case signature + when /^([\w@\*\s]+)\(([\w\*\s\,\[\]]*)\)$/ + ret = $1 + (args = $2).strip! + ret = ret.split(/\s+/) + args = args.split(/\s*,\s*/) + func = ret.pop + if( func =~ /^\*/ ) + func.gsub!(/^\*+/,"") + ret.push("*") + end + ret = ret.join(" ") + return [func, parse_ctype(ret, tymap), args.collect{|arg| parse_ctype(arg, tymap)}] + else + raise(RuntimeError,"can't parse the function prototype: #{signature}") + end + end + + # Given a String of C type +ty+, returns the corresponding Fiddle constant. + # + # +ty+ can also accept an Array of C type Strings, and will be returned in + # a corresponding Array. + # + # If Hash +tymap+ is provided, +ty+ is expected to be the key, and the + # value will be the C type to be looked up. + # + # Example: + # + # include Fiddle::CParser + # #=> Object + # + # parse_ctype('int') + # #=> Fiddle::TYPE_INT + # + # parse_ctype('double') + # #=> Fiddle::TYPE_DOUBLE + # + # parse_ctype('unsigned char') + # #=> -Fiddle::TYPE_CHAR + # + def parse_ctype(ty, tymap=nil) + tymap ||= {} + case ty + when Array + return [parse_ctype(ty[0], tymap), ty[1]] + when "void" + return TYPE_VOID + when "char" + return TYPE_CHAR + when "unsigned char" + return -TYPE_CHAR + when "short" + return TYPE_SHORT + when "unsigned short" + return -TYPE_SHORT + when "int" + return TYPE_INT + when "unsigned int", 'uint' + return -TYPE_INT + when "long" + return TYPE_LONG + when "unsigned long" + return -TYPE_LONG + when "long long" + if( defined?(TYPE_LONG_LONG) ) + return TYPE_LONG_LONG + else + raise(RuntimeError, "unsupported type: #{ty}") + end + when "unsigned long long" + if( defined?(TYPE_LONG_LONG) ) + return -TYPE_LONG_LONG + else + raise(RuntimeError, "unsupported type: #{ty}") + end + when "float" + return TYPE_FLOAT + when "double" + return TYPE_DOUBLE + when "size_t" + return TYPE_SIZE_T + when "ssize_t" + return TYPE_SSIZE_T + when "ptrdiff_t" + return TYPE_PTRDIFF_T + when "intptr_t" + return TYPE_INTPTR_T + when "uintptr_t" + return TYPE_UINTPTR_T + when /\*/, /\[\s*\]/ + return TYPE_VOIDP + else + if( tymap[ty] ) + return parse_ctype(tymap[ty], tymap) + else + raise(DLError, "unknown type: #{ty}") + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/fiddle/function.rb b/ruby/lib/ruby/2.1.0/fiddle/function.rb new file mode 100644 index 0000000..ab7496e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/fiddle/function.rb @@ -0,0 +1,17 @@ +module Fiddle + class Function + # The ABI of the Function. + attr_reader :abi + + # The address of this function + attr_reader :ptr + + # The name of this function + attr_reader :name + + # The integer memory location of this function + def to_i + ptr.to_i + end + end +end diff --git a/ruby/lib/ruby/2.1.0/fiddle/import.rb b/ruby/lib/ruby/2.1.0/fiddle/import.rb new file mode 100644 index 0000000..ec5ee94 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/fiddle/import.rb @@ -0,0 +1,314 @@ +require 'fiddle' +require 'fiddle/struct' +require 'fiddle/cparser' + +module Fiddle + + # Used internally by Fiddle::Importer + class CompositeHandler + # Create a new handler with the open +handlers+ + # + # Used internally by Fiddle::Importer.dlload + def initialize(handlers) + @handlers = handlers + end + + # Array of the currently loaded libraries. + def handlers() + @handlers + end + + # Returns the address as an Integer from any handlers with the function + # named +symbol+. + # + # Raises a DLError if the handle is closed. + def sym(symbol) + @handlers.each{|handle| + if( handle ) + begin + addr = handle.sym(symbol) + return addr + rescue DLError + end + end + } + return nil + end + + # See Fiddle::CompositeHandler.sym + def [](symbol) + sym(symbol) + end + end + + # A DSL that provides the means to dynamically load libraries and build + # modules around them including calling extern functions within the C + # library that has been loaded. + # + # == Example + # + # require 'fiddle' + # require 'fiddle/import' + # + # module LibSum + # extend Fiddle::Importer + # dlload './libsum.so' + # extern 'double sum(double*, int)' + # extern 'double split(double)' + # end + # + module Importer + include Fiddle + include CParser + extend Importer + + # Creates an array of handlers for the given +libs+, can be an instance of + # Fiddle::Handle, Fiddle::Importer, or will create a new istance of + # Fiddle::Handle using Fiddle.dlopen + # + # Raises a DLError if the library cannot be loaded. + # + # See Fiddle.dlopen + def dlload(*libs) + handles = libs.collect{|lib| + case lib + when nil + nil + when Handle + lib + when Importer + lib.handlers + else + begin + Fiddle.dlopen(lib) + rescue DLError + raise(DLError, "can't load #{lib}") + end + end + }.flatten() + @handler = CompositeHandler.new(handles) + @func_map = {} + @type_alias = {} + end + + # Sets the type alias for +alias_type+ as +orig_type+ + def typealias(alias_type, orig_type) + @type_alias[alias_type] = orig_type + end + + # Returns the sizeof +ty+, using Fiddle::Importer.parse_ctype to determine + # the C type and the appropriate Fiddle constant. + def sizeof(ty) + case ty + when String + ty = parse_ctype(ty, @type_alias).abs() + case ty + when TYPE_CHAR + return SIZEOF_CHAR + when TYPE_SHORT + return SIZEOF_SHORT + when TYPE_INT + return SIZEOF_INT + when TYPE_LONG + return SIZEOF_LONG + when TYPE_LONG_LONG + return SIZEOF_LONG_LONG + when TYPE_FLOAT + return SIZEOF_FLOAT + when TYPE_DOUBLE + return SIZEOF_DOUBLE + when TYPE_VOIDP + return SIZEOF_VOIDP + else + raise(DLError, "unknown type: #{ty}") + end + when Class + if( ty.instance_methods().include?(:to_ptr) ) + return ty.size() + end + end + return Pointer[ty].size() + end + + def parse_bind_options(opts) + h = {} + while( opt = opts.shift() ) + case opt + when :stdcall, :cdecl + h[:call_type] = opt + when :carried, :temp, :temporal, :bind + h[:callback_type] = opt + h[:carrier] = opts.shift() + else + h[opt] = true + end + end + h + end + private :parse_bind_options + + # :stopdoc: + CALL_TYPE_TO_ABI = Hash.new { |h, k| + raise RuntimeError, "unsupported call type: #{k}" + }.merge({ :stdcall => (Function::STDCALL rescue Function::DEFAULT), + :cdecl => Function::DEFAULT, + nil => Function::DEFAULT + }).freeze + private_constant :CALL_TYPE_TO_ABI + # :startdoc: + + # Creates a global method from the given C +signature+. + def extern(signature, *opts) + symname, ctype, argtype = parse_signature(signature, @type_alias) + opt = parse_bind_options(opts) + f = import_function(symname, ctype, argtype, opt[:call_type]) + name = symname.gsub(/@.+/,'') + @func_map[name] = f + # define_method(name){|*args,&block| f.call(*args,&block)} + begin + /^(.+?):(\d+)/ =~ caller.first + file, line = $1, $2.to_i + rescue + file, line = __FILE__, __LINE__+3 + end + module_eval(<<-EOS, file, line) + def #{name}(*args, &block) + @func_map['#{name}'].call(*args,&block) + end + EOS + module_function(name) + f + end + + # Creates a global method from the given C +signature+ using the given + # +opts+ as bind parameters with the given block. + def bind(signature, *opts, &blk) + name, ctype, argtype = parse_signature(signature, @type_alias) + h = parse_bind_options(opts) + case h[:callback_type] + when :bind, nil + f = bind_function(name, ctype, argtype, h[:call_type], &blk) + else + raise(RuntimeError, "unknown callback type: #{h[:callback_type]}") + end + @func_map[name] = f + #define_method(name){|*args,&block| f.call(*args,&block)} + begin + /^(.+?):(\d+)/ =~ caller.first + file, line = $1, $2.to_i + rescue + file, line = __FILE__, __LINE__+3 + end + module_eval(<<-EOS, file, line) + def #{name}(*args,&block) + @func_map['#{name}'].call(*args,&block) + end + EOS + module_function(name) + f + end + + # Creates a class to wrap the C struct described by +signature+. + # + # MyStruct = struct ['int i', 'char c'] + def struct(signature) + tys, mems = parse_struct_signature(signature, @type_alias) + Fiddle::CStructBuilder.create(CStruct, tys, mems) + end + + # Creates a class to wrap the C union described by +signature+. + # + # MyUnion = union ['int i', 'char c'] + def union(signature) + tys, mems = parse_struct_signature(signature, @type_alias) + Fiddle::CStructBuilder.create(CUnion, tys, mems) + end + + # Returns the function mapped to +name+, that was created by either + # Fiddle::Importer.extern or Fiddle::Importer.bind + def [](name) + @func_map[name] + end + + # Creates a class to wrap the C struct with the value +ty+ + # + # See also Fiddle::Importer.struct + def create_value(ty, val=nil) + s = struct([ty + " value"]) + ptr = s.malloc() + if( val ) + ptr.value = val + end + return ptr + end + alias value create_value + + # Returns a new instance of the C struct with the value +ty+ at the +addr+ + # address. + def import_value(ty, addr) + s = struct([ty + " value"]) + ptr = s.new(addr) + return ptr + end + + + # The Fiddle::CompositeHandler instance + # + # Will raise an error if no handlers are open. + def handler + @handler or raise "call dlload before importing symbols and functions" + end + + # Returns a new Fiddle::Pointer instance at the memory address of the given + # +name+ symbol. + # + # Raises a DLError if the +name+ doesn't exist. + # + # See Fiddle::CompositeHandler.sym and Fiddle::Handle.sym + def import_symbol(name) + addr = handler.sym(name) + if( !addr ) + raise(DLError, "cannot find the symbol: #{name}") + end + Pointer.new(addr) + end + + # Returns a new Fiddle::Function instance at the memory address of the given + # +name+ function. + # + # Raises a DLError if the +name+ doesn't exist. + # + # * +argtype+ is an Array of arguments, passed to the +name+ function. + # * +ctype+ is the return type of the function + # * +call_type+ is the ABI of the function + # + # See also Fiddle:Function.new + # + # See Fiddle::CompositeHandler.sym and Fiddle::Handler.sym + def import_function(name, ctype, argtype, call_type = nil) + addr = handler.sym(name) + if( !addr ) + raise(DLError, "cannot find the function: #{name}()") + end + Function.new(addr, argtype, ctype, CALL_TYPE_TO_ABI[call_type], + name: name) + end + + # Returns a new closure wrapper for the +name+ function. + # + # * +ctype+ is the return type of the function + # * +argtype+ is an Array of arguments, passed to the callback function + # * +call_type+ is the abi of the closure + # * +block+ is passed to the callback + # + # See Fiddle::Closure + def bind_function(name, ctype, argtype, call_type = nil, &block) + abi = CALL_TYPE_TO_ABI[call_type] + closure = Class.new(Fiddle::Closure) { + define_method(:call, block) + }.new(ctype, argtype, abi) + + Function.new(closure, argtype, ctype, abi, name: name) + end + end +end diff --git a/ruby/lib/ruby/2.1.0/fiddle/pack.rb b/ruby/lib/ruby/2.1.0/fiddle/pack.rb new file mode 100644 index 0000000..e4e9542 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/fiddle/pack.rb @@ -0,0 +1,128 @@ +require 'fiddle' + +module Fiddle + module PackInfo # :nodoc: all + ALIGN_MAP = { + TYPE_VOIDP => ALIGN_VOIDP, + TYPE_CHAR => ALIGN_CHAR, + TYPE_SHORT => ALIGN_SHORT, + TYPE_INT => ALIGN_INT, + TYPE_LONG => ALIGN_LONG, + TYPE_FLOAT => ALIGN_FLOAT, + TYPE_DOUBLE => ALIGN_DOUBLE, + -TYPE_CHAR => ALIGN_CHAR, + -TYPE_SHORT => ALIGN_SHORT, + -TYPE_INT => ALIGN_INT, + -TYPE_LONG => ALIGN_LONG, + } + + PACK_MAP = { + TYPE_VOIDP => ((SIZEOF_VOIDP == SIZEOF_LONG_LONG) ? "q" : "l!"), + TYPE_CHAR => "c", + TYPE_SHORT => "s!", + TYPE_INT => "i!", + TYPE_LONG => "l!", + TYPE_FLOAT => "f", + TYPE_DOUBLE => "d", + -TYPE_CHAR => "c", + -TYPE_SHORT => "s!", + -TYPE_INT => "i!", + -TYPE_LONG => "l!", + } + + SIZE_MAP = { + TYPE_VOIDP => SIZEOF_VOIDP, + TYPE_CHAR => SIZEOF_CHAR, + TYPE_SHORT => SIZEOF_SHORT, + TYPE_INT => SIZEOF_INT, + TYPE_LONG => SIZEOF_LONG, + TYPE_FLOAT => SIZEOF_FLOAT, + TYPE_DOUBLE => SIZEOF_DOUBLE, + -TYPE_CHAR => SIZEOF_CHAR, + -TYPE_SHORT => SIZEOF_SHORT, + -TYPE_INT => SIZEOF_INT, + -TYPE_LONG => SIZEOF_LONG, + } + if defined?(TYPE_LONG_LONG) + ALIGN_MAP[TYPE_LONG_LONG] = ALIGN_MAP[-TYPE_LONG_LONG] = ALIGN_LONG_LONG + PACK_MAP[TYPE_LONG_LONG] = PACK_MAP[-TYPE_LONG_LONG] = "q" + SIZE_MAP[TYPE_LONG_LONG] = SIZE_MAP[-TYPE_LONG_LONG] = SIZEOF_LONG_LONG + end + + def align(addr, align) + d = addr % align + if( d == 0 ) + addr + else + addr + (align - d) + end + end + module_function :align + end + + class Packer # :nodoc: all + include PackInfo + + def self.[](*types) + new(types) + end + + def initialize(types) + parse_types(types) + end + + def size() + @size + end + + def pack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.pack(@template) + when SIZEOF_LONG_LONG + ary.pack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + + def unpack(ary) + case SIZEOF_VOIDP + when SIZEOF_LONG + ary.join().unpack(@template) + when SIZEOF_LONG_LONG + ary.join().unpack(@template) + else + raise(RuntimeError, "sizeof(void*)?") + end + end + + private + + def parse_types(types) + @template = "" + addr = 0 + types.each{|t| + orig_addr = addr + if( t.is_a?(Array) ) + addr = align(orig_addr, ALIGN_MAP[TYPE_VOIDP]) + else + addr = align(orig_addr, ALIGN_MAP[t]) + end + d = addr - orig_addr + if( d > 0 ) + @template << "x#{d}" + end + if( t.is_a?(Array) ) + @template << (PACK_MAP[t[0]] * t[1]) + addr += (SIZE_MAP[t[0]] * t[1]) + else + @template << PACK_MAP[t] + addr += SIZE_MAP[t] + end + } + addr = align(addr, ALIGN_MAP[TYPE_VOIDP]) + @size = addr + end + end +end diff --git a/ruby/lib/ruby/2.1.0/fiddle/struct.rb b/ruby/lib/ruby/2.1.0/fiddle/struct.rb new file mode 100644 index 0000000..695a4d2 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/fiddle/struct.rb @@ -0,0 +1,243 @@ +require 'fiddle' +require 'fiddle/value' +require 'fiddle/pack' + +module Fiddle + # C struct shell + class CStruct + # accessor to Fiddle::CStructEntity + def CStruct.entity_class + CStructEntity + end + end + + # C union shell + class CUnion + # accessor to Fiddle::CUnionEntity + def CUnion.entity_class + CUnionEntity + end + end + + # Used to construct C classes (CUnion, CStruct, etc) + # + # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an + # easy-to-use manner. + module CStructBuilder + # Construct a new class given a C: + # * class +klass+ (CUnion, CStruct, or other that provide an + # #entity_class) + # * +types+ (Fiddle::TYPE_INT, Fiddle::TYPE_SIZE_T, etc., see the C types + # constants) + # * corresponding +members+ + # + # Fiddle::Importer#struct and Fiddle::Importer#union wrap this functionality in an + # easy-to-use manner. + # + # Example: + # + # require 'fiddle/struct' + # require 'fiddle/cparser' + # + # include Fiddle::CParser + # + # types, members = parse_struct_signature(['int i','char c']) + # + # MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members) + # + # obj = MyStruct.allocate + # + def create(klass, types, members) + new_class = Class.new(klass){ + define_method(:initialize){|addr| + @entity = klass.entity_class.new(addr, types) + @entity.assign_names(members) + } + define_method(:to_ptr){ @entity } + define_method(:to_i){ @entity.to_i } + members.each{|name| + define_method(name){ @entity[name] } + define_method(name + "="){|val| @entity[name] = val } + } + } + size = klass.entity_class.size(types) + new_class.module_eval(<<-EOS, __FILE__, __LINE__+1) + def new_class.size() + #{size} + end + def new_class.malloc() + addr = Fiddle.malloc(#{size}) + new(addr) + end + EOS + return new_class + end + module_function :create + end + + # A C struct wrapper + class CStructEntity < Fiddle::Pointer + include PackInfo + include ValueUtil + + # Allocates a C struct with the +types+ provided. + # + # When the instance is garbage collected, the C function +func+ is called. + def CStructEntity.malloc(types, func = nil) + addr = Fiddle.malloc(CStructEntity.size(types)) + CStructEntity.new(addr, types, func) + end + + # Returns the offset for the packed sizes for the given +types+. + # + # Fiddle::CStructEntity.size( + # [ Fiddle::TYPE_DOUBLE, + # Fiddle::TYPE_INT, + # Fiddle::TYPE_CHAR, + # Fiddle::TYPE_VOIDP ]) #=> 24 + def CStructEntity.size(types) + offset = 0 + + max_align = types.map { |type, count = 1| + last_offset = offset + + align = PackInfo::ALIGN_MAP[type] + offset = PackInfo.align(last_offset, align) + + (PackInfo::SIZE_MAP[type] * count) + + align + }.max + + PackInfo.align(offset, max_align) + end + + # Wraps the C pointer +addr+ as a C struct with the given +types+. + # + # When the instance is garbage collected, the C function +func+ is called. + # + # See also Fiddle::Pointer.new + def initialize(addr, types, func = nil) + set_ctypes(types) + super(addr, @size, func) + end + + # Set the names of the +members+ in this C struct + def assign_names(members) + @members = members + end + + # Calculates the offsets and sizes for the given +types+ in the struct. + def set_ctypes(types) + @ctypes = types + @offset = [] + offset = 0 + + max_align = types.map { |type, count = 1| + orig_offset = offset + align = ALIGN_MAP[type] + offset = PackInfo.align(orig_offset, align) + + @offset << offset + + offset += (SIZE_MAP[type] * count) + + align + }.max + + @size = PackInfo.align(offset, max_align) + end + + # Fetch struct member +name+ + def [](name) + idx = @members.index(name) + if( idx.nil? ) + raise(ArgumentError, "no such member: #{name}") + end + ty = @ctypes[idx] + if( ty.is_a?(Array) ) + r = super(@offset[idx], SIZE_MAP[ty[0]] * ty[1]) + else + r = super(@offset[idx], SIZE_MAP[ty.abs]) + end + packer = Packer.new([ty]) + val = packer.unpack([r]) + case ty + when Array + case ty[0] + when TYPE_VOIDP + val = val.collect{|v| Pointer.new(v)} + end + when TYPE_VOIDP + val = Pointer.new(val[0]) + else + val = val[0] + end + if( ty.is_a?(Integer) && (ty < 0) ) + return unsigned_value(val, ty) + elsif( ty.is_a?(Array) && (ty[0] < 0) ) + return val.collect{|v| unsigned_value(v,ty[0])} + else + return val + end + end + + # Set struct member +name+, to value +val+ + def []=(name, val) + idx = @members.index(name) + if( idx.nil? ) + raise(ArgumentError, "no such member: #{name}") + end + ty = @ctypes[idx] + packer = Packer.new([ty]) + val = wrap_arg(val, ty, []) + buff = packer.pack([val].flatten()) + super(@offset[idx], buff.size, buff) + if( ty.is_a?(Integer) && (ty < 0) ) + return unsigned_value(val, ty) + elsif( ty.is_a?(Array) && (ty[0] < 0) ) + return val.collect{|v| unsigned_value(v,ty[0])} + else + return val + end + end + + def to_s() # :nodoc: + super(@size) + end + end + + # A C union wrapper + class CUnionEntity < CStructEntity + include PackInfo + + # Allocates a C union the +types+ provided. + # + # When the instance is garbage collected, the C function +func+ is called. + def CUnionEntity.malloc(types, func=nil) + addr = Fiddle.malloc(CUnionEntity.size(types)) + CUnionEntity.new(addr, types, func) + end + + # Returns the size needed for the union with the given +types+. + # + # Fiddle::CUnionEntity.size( + # [ Fiddle::TYPE_DOUBLE, + # Fiddle::TYPE_INT, + # Fiddle::TYPE_CHAR, + # Fiddle::TYPE_VOIDP ]) #=> 8 + def CUnionEntity.size(types) + types.map { |type, count = 1| + PackInfo::SIZE_MAP[type] * count + }.max + end + + # Calculate the necessary offset and for each union member with the given + # +types+ + def set_ctypes(types) + @ctypes = types + @offset = Array.new(types.length, 0) + @size = self.class.size types + end + end +end + diff --git a/ruby/lib/ruby/2.1.0/fiddle/types.rb b/ruby/lib/ruby/2.1.0/fiddle/types.rb new file mode 100644 index 0000000..02c1d25 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/fiddle/types.rb @@ -0,0 +1,71 @@ +module Fiddle + # Adds Windows type aliases to the including class for use with + # Fiddle::Importer. + # + # The aliases added are: + # * ATOM + # * BOOL + # * BYTE + # * DWORD + # * DWORD32 + # * DWORD64 + # * HANDLE + # * HDC + # * HINSTANCE + # * HWND + # * LPCSTR + # * LPSTR + # * PBYTE + # * PDWORD + # * PHANDLE + # * PVOID + # * PWORD + # * UCHAR + # * UINT + # * ULONG + # * WORD + module Win32Types + def included(m) # :nodoc: + m.module_eval{ + typealias "DWORD", "unsigned long" + typealias "PDWORD", "unsigned long *" + typealias "DWORD32", "unsigned long" + typealias "DWORD64", "unsigned long long" + typealias "WORD", "unsigned short" + typealias "PWORD", "unsigned short *" + typealias "BOOL", "int" + typealias "ATOM", "int" + typealias "BYTE", "unsigned char" + typealias "PBYTE", "unsigned char *" + typealias "UINT", "unsigned int" + typealias "ULONG", "unsigned long" + typealias "UCHAR", "unsigned char" + typealias "HANDLE", "uintptr_t" + typealias "PHANDLE", "void*" + typealias "PVOID", "void*" + typealias "LPCSTR", "char*" + typealias "LPSTR", "char*" + typealias "HINSTANCE", "unsigned int" + typealias "HDC", "unsigned int" + typealias "HWND", "unsigned int" + } + end + module_function :included + end + + # Adds basic type aliases to the including class for use with Fiddle::Importer. + # + # The aliases added are +uint+ and +u_int+ (unsigned int) and + # +ulong+ and +u_long+ (unsigned long) + module BasicTypes + def included(m) # :nodoc: + m.module_eval{ + typealias "uint", "unsigned int" + typealias "u_int", "unsigned int" + typealias "ulong", "unsigned long" + typealias "u_long", "unsigned long" + } + end + module_function :included + end +end diff --git a/ruby/lib/ruby/2.1.0/fiddle/value.rb b/ruby/lib/ruby/2.1.0/fiddle/value.rb new file mode 100644 index 0000000..8d71e47 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/fiddle/value.rb @@ -0,0 +1,112 @@ +require 'fiddle' + +module Fiddle + module ValueUtil #:nodoc: all + def unsigned_value(val, ty) + case ty.abs + when TYPE_CHAR + [val].pack("c").unpack("C")[0] + when TYPE_SHORT + [val].pack("s!").unpack("S!")[0] + when TYPE_INT + [val].pack("i!").unpack("I!")[0] + when TYPE_LONG + [val].pack("l!").unpack("L!")[0] + when TYPE_LONG_LONG + [val].pack("q").unpack("Q")[0] + else + val + end + end + + def signed_value(val, ty) + case ty.abs + when TYPE_CHAR + [val].pack("C").unpack("c")[0] + when TYPE_SHORT + [val].pack("S!").unpack("s!")[0] + when TYPE_INT + [val].pack("I!").unpack("i!")[0] + when TYPE_LONG + [val].pack("L!").unpack("l!")[0] + when TYPE_LONG_LONG + [val].pack("Q").unpack("q")[0] + else + val + end + end + + def wrap_args(args, tys, funcs, &block) + result = [] + tys ||= [] + args.each_with_index{|arg, idx| + result.push(wrap_arg(arg, tys[idx], funcs, &block)) + } + result + end + + def wrap_arg(arg, ty, funcs = [], &block) + funcs ||= [] + case arg + when nil + return 0 + when Pointer + return arg.to_i + when IO + case ty + when TYPE_VOIDP + return Pointer[arg].to_i + else + return arg.to_i + end + when Function + if( block ) + arg.bind_at_call(&block) + funcs.push(arg) + elsif !arg.bound? + raise(RuntimeError, "block must be given.") + end + return arg.to_i + when String + if( ty.is_a?(Array) ) + return arg.unpack('C*') + else + case SIZEOF_VOIDP + when SIZEOF_LONG + return [arg].pack("p").unpack("l!")[0] + when SIZEOF_LONG_LONG + return [arg].pack("p").unpack("q")[0] + else + raise(RuntimeError, "sizeof(void*)?") + end + end + when Float, Integer + return arg + when Array + if( ty.is_a?(Array) ) # used only by struct + case ty[0] + when TYPE_VOIDP + return arg.collect{|v| Integer(v)} + when TYPE_CHAR + if( arg.is_a?(String) ) + return val.unpack('C*') + end + end + return arg + else + return arg + end + else + if( arg.respond_to?(:to_ptr) ) + return arg.to_ptr.to_i + else + begin + return Integer(arg) + rescue + raise(ArgumentError, "unknown argument type: #{arg.class}") + end + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/fileutils.rb b/ruby/lib/ruby/2.1.0/fileutils.rb new file mode 100644 index 0000000..99044e2 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/fileutils.rb @@ -0,0 +1,1764 @@ +# +# = fileutils.rb +# +# Copyright (c) 2000-2007 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the same terms of ruby. +# +# == module FileUtils +# +# Namespace for several file utility methods for copying, moving, removing, etc. +# +# === Module Functions +# +# cd(dir, options) +# cd(dir, options) {|dir| .... } +# pwd() +# mkdir(dir, options) +# mkdir(list, options) +# mkdir_p(dir, options) +# mkdir_p(list, options) +# rmdir(dir, options) +# rmdir(list, options) +# ln(old, new, options) +# ln(list, destdir, options) +# ln_s(old, new, options) +# ln_s(list, destdir, options) +# ln_sf(src, dest, options) +# cp(src, dest, options) +# cp(list, dir, options) +# cp_r(src, dest, options) +# cp_r(list, dir, options) +# mv(src, dest, options) +# mv(list, dir, options) +# rm(list, options) +# rm_r(list, options) +# rm_rf(list, options) +# install(src, dest, mode = , options) +# chmod(mode, list, options) +# chmod_R(mode, list, options) +# chown(user, group, list, options) +# chown_R(user, group, list, options) +# touch(list, options) +# +# The options parameter is a hash of options, taken from the list +# :force, :noop, :preserve, and :verbose. +# :noop means that no changes are made. The other two are obvious. +# Each method documents the options that it honours. +# +# All methods that have the concept of a "source" file or directory can take +# either one file or a list of files in that argument. See the method +# documentation for examples. +# +# There are some `low level' methods, which do not accept any option: +# +# copy_entry(src, dest, preserve = false, dereference = false) +# copy_file(src, dest, preserve = false, dereference = true) +# copy_stream(srcstream, deststream) +# remove_entry(path, force = false) +# remove_entry_secure(path, force = false) +# remove_file(path, force = false) +# compare_file(path_a, path_b) +# compare_stream(stream_a, stream_b) +# uptodate?(file, cmp_list) +# +# == module FileUtils::Verbose +# +# This module has all methods of FileUtils module, but it outputs messages +# before acting. This equates to passing the :verbose flag to methods +# in FileUtils. +# +# == module FileUtils::NoWrite +# +# This module has all methods of FileUtils module, but never changes +# files/directories. This equates to passing the :noop flag to methods +# in FileUtils. +# +# == module FileUtils::DryRun +# +# This module has all methods of FileUtils module, but never changes +# files/directories. This equates to passing the :noop and +# :verbose flags to methods in FileUtils. +# + +module FileUtils + + def self.private_module_function(name) #:nodoc: + module_function name + private_class_method name + end + + # This hash table holds command options. + OPT_TABLE = {} #:nodoc: internal use only + + # + # Options: (none) + # + # Returns the name of the current directory. + # + def pwd + Dir.pwd + end + module_function :pwd + + alias getwd pwd + module_function :getwd + + # + # Options: verbose + # + # Changes the current directory to the directory +dir+. + # + # If this method is called with block, resumes to the old + # working directory after the block execution finished. + # + # FileUtils.cd('/', :verbose => true) # chdir and report it + # + # FileUtils.cd('/') do # chdir + # [...] # do something + # end # return to original directory + # + def cd(dir, options = {}, &block) # :yield: dir + fu_check_options options, OPT_TABLE['cd'] + fu_output_message "cd #{dir}" if options[:verbose] + Dir.chdir(dir, &block) + fu_output_message 'cd -' if options[:verbose] and block + end + module_function :cd + + alias chdir cd + module_function :chdir + + OPT_TABLE['cd'] = + OPT_TABLE['chdir'] = [:verbose] + + # + # Options: (none) + # + # Returns true if +newer+ is newer than all +old_list+. + # Non-existent files are older than any file. + # + # FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \ + # system 'make hello.o' + # + def uptodate?(new, old_list) + return false unless File.exist?(new) + new_time = File.mtime(new) + old_list.each do |old| + if File.exist?(old) + return false unless new_time > File.mtime(old) + end + end + true + end + module_function :uptodate? + + def remove_tailing_slash(dir) + dir == '/' ? dir : dir.chomp(?/) + end + private_module_function :remove_tailing_slash + + # + # Options: mode noop verbose + # + # Creates one or more directories. + # + # FileUtils.mkdir 'test' + # FileUtils.mkdir %w( tmp data ) + # FileUtils.mkdir 'notexist', :noop => true # Does not really create. + # FileUtils.mkdir 'tmp', :mode => 0700 + # + def mkdir(list, options = {}) + fu_check_options options, OPT_TABLE['mkdir'] + list = fu_list(list) + fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose] + return if options[:noop] + + list.each do |dir| + fu_mkdir dir, options[:mode] + end + end + module_function :mkdir + + OPT_TABLE['mkdir'] = [:mode, :noop, :verbose] + + # + # Options: mode noop verbose + # + # Creates a directory and all its parent directories. + # For example, + # + # FileUtils.mkdir_p '/usr/local/lib/ruby' + # + # causes to make following directories, if it does not exist. + # * /usr + # * /usr/local + # * /usr/local/lib + # * /usr/local/lib/ruby + # + # You can pass several directories at a time in a list. + # + def mkdir_p(list, options = {}) + fu_check_options options, OPT_TABLE['mkdir_p'] + list = fu_list(list) + fu_output_message "mkdir -p #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose] + return *list if options[:noop] + + list.map {|path| remove_tailing_slash(path)}.each do |path| + # optimize for the most common case + begin + fu_mkdir path, options[:mode] + next + rescue SystemCallError + next if File.directory?(path) + end + + stack = [] + until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/" + stack.push path + path = File.dirname(path) + end + stack.reverse_each do |dir| + begin + fu_mkdir dir, options[:mode] + rescue SystemCallError + raise unless File.directory?(dir) + end + end + end + + return *list + end + module_function :mkdir_p + + alias mkpath mkdir_p + alias makedirs mkdir_p + module_function :mkpath + module_function :makedirs + + OPT_TABLE['mkdir_p'] = + OPT_TABLE['mkpath'] = + OPT_TABLE['makedirs'] = [:mode, :noop, :verbose] + + def fu_mkdir(path, mode) #:nodoc: + path = remove_tailing_slash(path) + if mode + Dir.mkdir path, mode + File.chmod mode, path + else + Dir.mkdir path + end + end + private_module_function :fu_mkdir + + # + # Options: noop, verbose + # + # Removes one or more directories. + # + # FileUtils.rmdir 'somedir' + # FileUtils.rmdir %w(somedir anydir otherdir) + # # Does not really remove directory; outputs message. + # FileUtils.rmdir 'somedir', :verbose => true, :noop => true + # + def rmdir(list, options = {}) + fu_check_options options, OPT_TABLE['rmdir'] + list = fu_list(list) + parents = options[:parents] + fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if options[:verbose] + return if options[:noop] + list.each do |dir| + begin + Dir.rmdir(dir = remove_tailing_slash(dir)) + if parents + until (parent = File.dirname(dir)) == '.' or parent == dir + dir = parent + Dir.rmdir(dir) + end + end + rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT + end + end + end + module_function :rmdir + + OPT_TABLE['rmdir'] = [:parents, :noop, :verbose] + + # + # Options: force noop verbose + # + # ln(old, new, options = {}) + # + # Creates a hard link +new+ which points to +old+. + # If +new+ already exists and it is a directory, creates a link +new/old+. + # If +new+ already exists and it is not a directory, raises Errno::EEXIST. + # But if :force option is set, overwrite +new+. + # + # FileUtils.ln 'gcc', 'cc', :verbose => true + # FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs' + # + # ln(list, destdir, options = {}) + # + # Creates several hard links in a directory, with each one pointing to the + # item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR. + # + # include FileUtils + # cd '/sbin' + # FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked. + # + def ln(src, dest, options = {}) + fu_check_options options, OPT_TABLE['ln'] + fu_output_message "ln#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + fu_each_src_dest0(src, dest) do |s,d| + remove_file d, true if options[:force] + File.link s, d + end + end + module_function :ln + + alias link ln + module_function :link + + OPT_TABLE['ln'] = + OPT_TABLE['link'] = [:force, :noop, :verbose] + + # + # Options: force noop verbose + # + # ln_s(old, new, options = {}) + # + # Creates a symbolic link +new+ which points to +old+. If +new+ already + # exists and it is a directory, creates a symbolic link +new/old+. If +new+ + # already exists and it is not a directory, raises Errno::EEXIST. But if + # :force option is set, overwrite +new+. + # + # FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby' + # FileUtils.ln_s 'verylongsourcefilename.c', 'c', :force => true + # + # ln_s(list, destdir, options = {}) + # + # Creates several symbolic links in a directory, with each one pointing to the + # item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR. + # + # If +destdir+ is not a directory, raises Errno::ENOTDIR. + # + # FileUtils.ln_s Dir.glob('bin/*.rb'), '/home/aamine/bin' + # + def ln_s(src, dest, options = {}) + fu_check_options options, OPT_TABLE['ln_s'] + fu_output_message "ln -s#{options[:force] ? 'f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + fu_each_src_dest0(src, dest) do |s,d| + remove_file d, true if options[:force] + File.symlink s, d + end + end + module_function :ln_s + + alias symlink ln_s + module_function :symlink + + OPT_TABLE['ln_s'] = + OPT_TABLE['symlink'] = [:force, :noop, :verbose] + + # + # Options: noop verbose + # + # Same as + # #ln_s(src, dest, :force => true) + # + def ln_sf(src, dest, options = {}) + fu_check_options options, OPT_TABLE['ln_sf'] + options = options.dup + options[:force] = true + ln_s src, dest, options + end + module_function :ln_sf + + OPT_TABLE['ln_sf'] = [:noop, :verbose] + + # + # Options: preserve noop verbose + # + # Copies a file content +src+ to +dest+. If +dest+ is a directory, + # copies +src+ to +dest/src+. + # + # If +src+ is a list of files, then +dest+ must be a directory. + # + # FileUtils.cp 'eval.c', 'eval.c.org' + # FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6' + # FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true + # FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink + # + def cp(src, dest, options = {}) + fu_check_options options, OPT_TABLE['cp'] + fu_output_message "cp#{options[:preserve] ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + fu_each_src_dest(src, dest) do |s, d| + copy_file s, d, options[:preserve] + end + end + module_function :cp + + alias copy cp + module_function :copy + + OPT_TABLE['cp'] = + OPT_TABLE['copy'] = [:preserve, :noop, :verbose] + + # + # Options: preserve noop verbose dereference_root remove_destination + # + # Copies +src+ to +dest+. If +src+ is a directory, this method copies + # all its contents recursively. If +dest+ is a directory, copies + # +src+ to +dest/src+. + # + # +src+ can be a list of files. + # + # # Installing Ruby library "mylib" under the site_ruby + # FileUtils.rm_r site_ruby + '/mylib', :force + # FileUtils.cp_r 'lib/', site_ruby + '/mylib' + # + # # Examples of copying several files to target directory. + # FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail' + # FileUtils.cp_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true + # + # # If you want to copy all contents of a directory instead of the + # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y, + # # use following code. + # FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src, + # # but this doesn't. + # + def cp_r(src, dest, options = {}) + fu_check_options options, OPT_TABLE['cp_r'] + fu_output_message "cp -r#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + options = options.dup + options[:dereference_root] = true unless options.key?(:dereference_root) + fu_each_src_dest(src, dest) do |s, d| + copy_entry s, d, options[:preserve], options[:dereference_root], options[:remove_destination] + end + end + module_function :cp_r + + OPT_TABLE['cp_r'] = [:preserve, :noop, :verbose, + :dereference_root, :remove_destination] + + # + # Copies a file system entry +src+ to +dest+. + # If +src+ is a directory, this method copies its contents recursively. + # This method preserves file types, c.f. symlink, directory... + # (FIFO, device files and etc. are not supported yet) + # + # Both of +src+ and +dest+ must be a path name. + # +src+ must exist, +dest+ must not exist. + # + # If +preserve+ is true, this method preserves owner, group, and + # modified time. Permissions are copied regardless +preserve+. + # + # If +dereference_root+ is true, this method dereference tree root. + # + # If +remove_destination+ is true, this method removes each destination file before copy. + # + def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) + Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent| + destent = Entry_.new(dest, ent.rel, false) + File.unlink destent.path if remove_destination && File.file?(destent.path) + ent.copy destent.path + end, proc do |ent| + destent = Entry_.new(dest, ent.rel, false) + ent.copy_metadata destent.path if preserve + end) + end + module_function :copy_entry + + # + # Copies file contents of +src+ to +dest+. + # Both of +src+ and +dest+ must be a path name. + # + def copy_file(src, dest, preserve = false, dereference = true) + ent = Entry_.new(src, nil, dereference) + ent.copy_file dest + ent.copy_metadata dest if preserve + end + module_function :copy_file + + # + # Copies stream +src+ to +dest+. + # +src+ must respond to #read(n) and + # +dest+ must respond to #write(str). + # + def copy_stream(src, dest) + IO.copy_stream(src, dest) + end + module_function :copy_stream + + # + # Options: force noop verbose + # + # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different + # disk partition, the file is copied then the original file is removed. + # + # FileUtils.mv 'badname.rb', 'goodname.rb' + # FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error + # + # FileUtils.mv %w(junk.txt dust.txt), '/home/aamine/.trash/' + # FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true + # + def mv(src, dest, options = {}) + fu_check_options options, OPT_TABLE['mv'] + fu_output_message "mv#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + fu_each_src_dest(src, dest) do |s, d| + destent = Entry_.new(d, nil, true) + begin + if destent.exist? + if destent.directory? + raise Errno::EEXIST, dest + else + destent.remove_file if rename_cannot_overwrite_file? + end + end + begin + File.rename s, d + rescue Errno::EXDEV + copy_entry s, d, true + if options[:secure] + remove_entry_secure s, options[:force] + else + remove_entry s, options[:force] + end + end + rescue SystemCallError + raise unless options[:force] + end + end + end + module_function :mv + + alias move mv + module_function :move + + OPT_TABLE['mv'] = + OPT_TABLE['move'] = [:force, :noop, :verbose, :secure] + + def rename_cannot_overwrite_file? #:nodoc: + /cygwin|mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM + end + private_module_function :rename_cannot_overwrite_file? + + # + # Options: force noop verbose + # + # Remove file(s) specified in +list+. This method cannot remove directories. + # All StandardErrors are ignored when the :force option is set. + # + # FileUtils.rm %w( junk.txt dust.txt ) + # FileUtils.rm Dir.glob('*.so') + # FileUtils.rm 'NotExistFile', :force => true # never raises exception + # + def rm(list, options = {}) + fu_check_options options, OPT_TABLE['rm'] + list = fu_list(list) + fu_output_message "rm#{options[:force] ? ' -f' : ''} #{list.join ' '}" if options[:verbose] + return if options[:noop] + + list.each do |path| + remove_file path, options[:force] + end + end + module_function :rm + + alias remove rm + module_function :remove + + OPT_TABLE['rm'] = + OPT_TABLE['remove'] = [:force, :noop, :verbose] + + # + # Options: noop verbose + # + # Equivalent to + # + # #rm(list, :force => true) + # + def rm_f(list, options = {}) + fu_check_options options, OPT_TABLE['rm_f'] + options = options.dup + options[:force] = true + rm list, options + end + module_function :rm_f + + alias safe_unlink rm_f + module_function :safe_unlink + + OPT_TABLE['rm_f'] = + OPT_TABLE['safe_unlink'] = [:noop, :verbose] + + # + # Options: force noop verbose secure + # + # remove files +list+[0] +list+[1]... If +list+[n] is a directory, + # removes its all contents recursively. This method ignores + # StandardError when :force option is set. + # + # FileUtils.rm_r Dir.glob('/tmp/*') + # FileUtils.rm_r '/', :force => true # :-) + # + # WARNING: This method causes local vulnerability + # if one of parent directories or removing directory tree are world + # writable (including /tmp, whose permission is 1777), and the current + # process has strong privilege such as Unix super user (root), and the + # system has symbolic link. For secure removing, read the documentation + # of #remove_entry_secure carefully, and set :secure option to true. + # Default is :secure=>false. + # + # NOTE: This method calls #remove_entry_secure if :secure option is set. + # See also #remove_entry_secure. + # + def rm_r(list, options = {}) + fu_check_options options, OPT_TABLE['rm_r'] + # options[:secure] = true unless options.key?(:secure) + list = fu_list(list) + fu_output_message "rm -r#{options[:force] ? 'f' : ''} #{list.join ' '}" if options[:verbose] + return if options[:noop] + list.each do |path| + if options[:secure] + remove_entry_secure path, options[:force] + else + remove_entry path, options[:force] + end + end + end + module_function :rm_r + + OPT_TABLE['rm_r'] = [:force, :noop, :verbose, :secure] + + # + # Options: noop verbose secure + # + # Equivalent to + # + # #rm_r(list, :force => true) + # + # WARNING: This method causes local vulnerability. + # Read the documentation of #rm_r first. + # + def rm_rf(list, options = {}) + fu_check_options options, OPT_TABLE['rm_rf'] + options = options.dup + options[:force] = true + rm_r list, options + end + module_function :rm_rf + + alias rmtree rm_rf + module_function :rmtree + + OPT_TABLE['rm_rf'] = + OPT_TABLE['rmtree'] = [:noop, :verbose, :secure] + + # + # This method removes a file system entry +path+. +path+ shall be a + # regular file, a directory, or something. If +path+ is a directory, + # remove it recursively. This method is required to avoid TOCTTOU + # (time-of-check-to-time-of-use) local security vulnerability of #rm_r. + # #rm_r causes security hole when: + # + # * Parent directory is world writable (including /tmp). + # * Removing directory tree includes world writable directory. + # * The system has symbolic link. + # + # To avoid this security hole, this method applies special preprocess. + # If +path+ is a directory, this method chown(2) and chmod(2) all + # removing directories. This requires the current process is the + # owner of the removing whole directory tree, or is the super user (root). + # + # WARNING: You must ensure that *ALL* parent directories cannot be + # moved by other untrusted users. For example, parent directories + # should not be owned by untrusted users, and should not be world + # writable except when the sticky bit set. + # + # WARNING: Only the owner of the removing directory tree, or Unix super + # user (root) should invoke this method. Otherwise this method does not + # work. + # + # For details of this security vulnerability, see Perl's case: + # + # http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448 + # http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452 + # + # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100]. + # + def remove_entry_secure(path, force = false) + unless fu_have_symlink? + remove_entry path, force + return + end + fullpath = File.expand_path(path) + st = File.lstat(fullpath) + unless st.directory? + File.unlink fullpath + return + end + # is a directory. + parent_st = File.stat(File.dirname(fullpath)) + unless parent_st.world_writable? + remove_entry path, force + return + end + unless parent_st.sticky? + raise ArgumentError, "parent directory is world writable, FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})" + end + # freeze tree root + euid = Process.euid + File.open(fullpath + '/.') {|f| + unless fu_stat_identical_entry?(st, f.stat) + # symlink (TOC-to-TOU attack?) + File.unlink fullpath + return + end + f.chown euid, -1 + f.chmod 0700 + unless fu_stat_identical_entry?(st, File.lstat(fullpath)) + # TOC-to-TOU attack? + File.unlink fullpath + return + end + } + # ---- tree root is frozen ---- + root = Entry_.new(path) + root.preorder_traverse do |ent| + if ent.directory? + ent.chown euid, -1 + ent.chmod 0700 + end + end + root.postorder_traverse do |ent| + begin + ent.remove + rescue + raise unless force + end + end + rescue + raise unless force + end + module_function :remove_entry_secure + + def fu_have_symlink? #:nodoc: + File.symlink nil, nil + rescue NotImplementedError + return false + rescue TypeError + return true + end + private_module_function :fu_have_symlink? + + def fu_stat_identical_entry?(a, b) #:nodoc: + a.dev == b.dev and a.ino == b.ino + end + private_module_function :fu_stat_identical_entry? + + # + # This method removes a file system entry +path+. + # +path+ might be a regular file, a directory, or something. + # If +path+ is a directory, remove it recursively. + # + # See also #remove_entry_secure. + # + def remove_entry(path, force = false) + Entry_.new(path).postorder_traverse do |ent| + begin + ent.remove + rescue + raise unless force + end + end + rescue + raise unless force + end + module_function :remove_entry + + # + # Removes a file +path+. + # This method ignores StandardError if +force+ is true. + # + def remove_file(path, force = false) + Entry_.new(path).remove_file + rescue + raise unless force + end + module_function :remove_file + + # + # Removes a directory +dir+ and its contents recursively. + # This method ignores StandardError if +force+ is true. + # + def remove_dir(path, force = false) + remove_entry path, force # FIXME?? check if it is a directory + end + module_function :remove_dir + + # + # Returns true if the contents of a file A and a file B are identical. + # + # FileUtils.compare_file('somefile', 'somefile') #=> true + # FileUtils.compare_file('/bin/cp', '/bin/mv') #=> maybe false + # + def compare_file(a, b) + return false unless File.size(a) == File.size(b) + File.open(a, 'rb') {|fa| + File.open(b, 'rb') {|fb| + return compare_stream(fa, fb) + } + } + end + module_function :compare_file + + alias identical? compare_file + alias cmp compare_file + module_function :identical? + module_function :cmp + + # + # Returns true if the contents of a stream +a+ and +b+ are identical. + # + def compare_stream(a, b) + bsize = fu_stream_blksize(a, b) + sa = "" + sb = "" + begin + a.read(bsize, sa) + b.read(bsize, sb) + return true if sa.empty? && sb.empty? + end while sa == sb + false + end + module_function :compare_stream + + # + # Options: mode preserve noop verbose + # + # If +src+ is not same as +dest+, copies it and changes the permission + # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+. + # This method removes destination before copy. + # + # FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true + # FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true + # + def install(src, dest, options = {}) + fu_check_options options, OPT_TABLE['install'] + fu_output_message "install -c#{options[:preserve] && ' -p'}#{options[:mode] ? (' -m 0%o' % options[:mode]) : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] + return if options[:noop] + fu_each_src_dest(src, dest) do |s, d, st| + unless File.exist?(d) and compare_file(s, d) + remove_file d, true + copy_file s, d + File.utime st.atime, st.mtime, d if options[:preserve] + File.chmod options[:mode], d if options[:mode] + end + end + end + module_function :install + + OPT_TABLE['install'] = [:mode, :preserve, :noop, :verbose] + + def user_mask(target) #:nodoc: + target.each_char.inject(0) do |mask, chr| + case chr + when "u" + mask | 04700 + when "g" + mask | 02070 + when "o" + mask | 01007 + when "a" + mask | 07777 + else + raise ArgumentError, "invalid `who' symbol in file mode: #{chr}" + end + end + end + private_module_function :user_mask + + def apply_mask(mode, user_mask, op, mode_mask) + case op + when '=' + (mode & ~user_mask) | (user_mask & mode_mask) + when '+' + mode | (user_mask & mode_mask) + when '-' + mode & ~(user_mask & mode_mask) + end + end + private_module_function :apply_mask + + def symbolic_modes_to_i(mode_sym, path) #:nodoc: + mode_sym.split(/,/).inject(File.stat(path).mode & 07777) do |current_mode, clause| + target, *actions = clause.split(/([=+-])/) + raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty? + target = 'a' if target.empty? + user_mask = user_mask(target) + actions.each_slice(2) do |op, perm| + need_apply = op == '=' + mode_mask = (perm || '').each_char.inject(0) do |mask, chr| + case chr + when "r" + mask | 0444 + when "w" + mask | 0222 + when "x" + mask | 0111 + when "X" + if FileTest.directory? path + mask | 0111 + else + mask + end + when "s" + mask | 06000 + when "t" + mask | 01000 + when "u", "g", "o" + if mask.nonzero? + current_mode = apply_mask(current_mode, user_mask, op, mask) + end + need_apply = false + copy_mask = user_mask(chr) + (current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111) + else + raise ArgumentError, "invalid `perm' symbol in file mode: #{chr}" + end + end + + if mode_mask.nonzero? || need_apply + current_mode = apply_mask(current_mode, user_mask, op, mode_mask) + end + end + current_mode + end + end + private_module_function :symbolic_modes_to_i + + def fu_mode(mode, path) #:nodoc: + mode.is_a?(String) ? symbolic_modes_to_i(mode, path) : mode + end + private_module_function :fu_mode + + def mode_to_s(mode) #:nodoc: + mode.is_a?(String) ? mode : "%o" % mode + end + private_module_function :mode_to_s + + # + # Options: noop verbose + # + # Changes permission bits on the named files (in +list+) to the bit pattern + # represented by +mode+. + # + # +mode+ is the symbolic and absolute mode can be used. + # + # Absolute mode is + # FileUtils.chmod 0755, 'somecommand' + # FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb) + # FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true + # + # Symbolic mode is + # FileUtils.chmod "u=wrx,go=rx", 'somecommand' + # FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb) + # FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', :verbose => true + # + # "a" :: is user, group, other mask. + # "u" :: is user's mask. + # "g" :: is group's mask. + # "o" :: is other's mask. + # "w" :: is write permission. + # "r" :: is read permission. + # "x" :: is execute permission. + # "X" :: + # is execute permission for directories only, must be used in conjunction with "+" + # "s" :: is uid, gid. + # "t" :: is sticky bit. + # "+" :: is added to a class given the specified mode. + # "-" :: Is removed from a given class given mode. + # "=" :: Is the exact nature of the class will be given a specified mode. + + def chmod(mode, list, options = {}) + fu_check_options options, OPT_TABLE['chmod'] + list = fu_list(list) + fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if options[:verbose] + return if options[:noop] + list.each do |path| + Entry_.new(path).chmod(fu_mode(mode, path)) + end + end + module_function :chmod + + OPT_TABLE['chmod'] = [:noop, :verbose] + + # + # Options: noop verbose force + # + # Changes permission bits on the named files (in +list+) + # to the bit pattern represented by +mode+. + # + # FileUtils.chmod_R 0700, "/tmp/app.#{$$}" + # FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}" + # + def chmod_R(mode, list, options = {}) + fu_check_options options, OPT_TABLE['chmod_R'] + list = fu_list(list) + fu_output_message sprintf('chmod -R%s %s %s', + (options[:force] ? 'f' : ''), + mode_to_s(mode), list.join(' ')) if options[:verbose] + return if options[:noop] + list.each do |root| + Entry_.new(root).traverse do |ent| + begin + ent.chmod(fu_mode(mode, ent.path)) + rescue + raise unless options[:force] + end + end + end + end + module_function :chmod_R + + OPT_TABLE['chmod_R'] = [:noop, :verbose, :force] + + # + # Options: noop verbose + # + # Changes owner and group on the named files (in +list+) + # to the user +user+ and the group +group+. +user+ and +group+ + # may be an ID (Integer/String) or a name (String). + # If +user+ or +group+ is nil, this method does not change + # the attribute. + # + # FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby' + # FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true + # + def chown(user, group, list, options = {}) + fu_check_options options, OPT_TABLE['chown'] + list = fu_list(list) + fu_output_message sprintf('chown %s %s', + (group ? "#{user}:#{group}" : user || ':'), + list.join(' ')) if options[:verbose] + return if options[:noop] + uid = fu_get_uid(user) + gid = fu_get_gid(group) + list.each do |path| + Entry_.new(path).chown uid, gid + end + end + module_function :chown + + OPT_TABLE['chown'] = [:noop, :verbose] + + # + # Options: noop verbose force + # + # Changes owner and group on the named files (in +list+) + # to the user +user+ and the group +group+ recursively. + # +user+ and +group+ may be an ID (Integer/String) or + # a name (String). If +user+ or +group+ is nil, this + # method does not change the attribute. + # + # FileUtils.chown_R 'www', 'www', '/var/www/htdocs' + # FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true + # + def chown_R(user, group, list, options = {}) + fu_check_options options, OPT_TABLE['chown_R'] + list = fu_list(list) + fu_output_message sprintf('chown -R%s %s %s', + (options[:force] ? 'f' : ''), + (group ? "#{user}:#{group}" : user || ':'), + list.join(' ')) if options[:verbose] + return if options[:noop] + uid = fu_get_uid(user) + gid = fu_get_gid(group) + list.each do |root| + Entry_.new(root).traverse do |ent| + begin + ent.chown uid, gid + rescue + raise unless options[:force] + end + end + end + end + module_function :chown_R + + OPT_TABLE['chown_R'] = [:noop, :verbose, :force] + + begin + require 'etc' + + def fu_get_uid(user) #:nodoc: + return nil unless user + case user + when Integer + user + when /\A\d+\z/ + user.to_i + else + Etc.getpwnam(user).uid + end + end + private_module_function :fu_get_uid + + def fu_get_gid(group) #:nodoc: + return nil unless group + case group + when Integer + group + when /\A\d+\z/ + group.to_i + else + Etc.getgrnam(group).gid + end + end + private_module_function :fu_get_gid + + rescue LoadError + # need Win32 support??? + + def fu_get_uid(user) #:nodoc: + user # FIXME + end + private_module_function :fu_get_uid + + def fu_get_gid(group) #:nodoc: + group # FIXME + end + private_module_function :fu_get_gid + end + + # + # Options: noop verbose + # + # Updates modification time (mtime) and access time (atime) of file(s) in + # +list+. Files are created if they don't exist. + # + # FileUtils.touch 'timestamp' + # FileUtils.touch Dir.glob('*.c'); system 'make' + # + def touch(list, options = {}) + fu_check_options options, OPT_TABLE['touch'] + list = fu_list(list) + nocreate = options[:nocreate] + t = options[:mtime] + if options[:verbose] + fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}" + end + return if options[:noop] + list.each do |path| + created = nocreate + begin + File.utime(t, t, path) + rescue Errno::ENOENT + raise if created + File.open(path, 'a') { + ; + } + created = true + retry if t + end + end + end + module_function :touch + + OPT_TABLE['touch'] = [:noop, :verbose, :mtime, :nocreate] + + private + + module StreamUtils_ + private + + def fu_windows? + /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM + end + + def fu_copy_stream0(src, dest, blksize = nil) #:nodoc: + IO.copy_stream(src, dest) + end + + def fu_stream_blksize(*streams) + streams.each do |s| + next unless s.respond_to?(:stat) + size = fu_blksize(s.stat) + return size if size + end + fu_default_blksize() + end + + def fu_blksize(st) + s = st.blksize + return nil unless s + return nil if s == 0 + s + end + + def fu_default_blksize + 1024 + end + end + + include StreamUtils_ + extend StreamUtils_ + + class Entry_ #:nodoc: internal use only + include StreamUtils_ + + def initialize(a, b = nil, deref = false) + @prefix = @rel = @path = nil + if b + @prefix = a + @rel = b + else + @path = a + end + @deref = deref + @stat = nil + @lstat = nil + end + + def inspect + "\#<#{self.class} #{path()}>" + end + + def path + if @path + File.path(@path) + else + join(@prefix, @rel) + end + end + + def prefix + @prefix || @path + end + + def rel + @rel + end + + def dereference? + @deref + end + + def exist? + lstat! ? true : false + end + + def file? + s = lstat! + s and s.file? + end + + def directory? + s = lstat! + s and s.directory? + end + + def symlink? + s = lstat! + s and s.symlink? + end + + def chardev? + s = lstat! + s and s.chardev? + end + + def blockdev? + s = lstat! + s and s.blockdev? + end + + def socket? + s = lstat! + s and s.socket? + end + + def pipe? + s = lstat! + s and s.pipe? + end + + S_IF_DOOR = 0xD000 + + def door? + s = lstat! + s and (s.mode & 0xF000 == S_IF_DOOR) + end + + def entries + opts = {} + opts[:encoding] = ::Encoding::UTF_8 if fu_windows? + Dir.entries(path(), opts)\ + .reject {|n| n == '.' or n == '..' }\ + .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } + end + + def stat + return @stat if @stat + if lstat() and lstat().symlink? + @stat = File.stat(path()) + else + @stat = lstat() + end + @stat + end + + def stat! + return @stat if @stat + if lstat! and lstat!.symlink? + @stat = File.stat(path()) + else + @stat = lstat! + end + @stat + rescue SystemCallError + nil + end + + def lstat + if dereference? + @lstat ||= File.stat(path()) + else + @lstat ||= File.lstat(path()) + end + end + + def lstat! + lstat() + rescue SystemCallError + nil + end + + def chmod(mode) + if symlink? + File.lchmod mode, path() if have_lchmod? + else + File.chmod mode, path() + end + end + + def chown(uid, gid) + if symlink? + File.lchown uid, gid, path() if have_lchown? + else + File.chown uid, gid, path() + end + end + + def copy(dest) + case + when file? + copy_file dest + when directory? + if !File.exist?(dest) and descendant_diretory?(dest, path) + raise ArgumentError, "cannot copy directory %s to itself %s" % [path, dest] + end + begin + Dir.mkdir dest + rescue + raise unless File.directory?(dest) + end + when symlink? + File.symlink File.readlink(path()), dest + when chardev? + raise "cannot handle device file" unless File.respond_to?(:mknod) + mknod dest, ?c, 0666, lstat().rdev + when blockdev? + raise "cannot handle device file" unless File.respond_to?(:mknod) + mknod dest, ?b, 0666, lstat().rdev + when socket? + raise "cannot handle socket" unless File.respond_to?(:mknod) + mknod dest, nil, lstat().mode, 0 + when pipe? + raise "cannot handle FIFO" unless File.respond_to?(:mkfifo) + mkfifo dest, 0666 + when door? + raise "cannot handle door: #{path()}" + else + raise "unknown file type: #{path()}" + end + end + + def copy_file(dest) + File.open(path()) do |s| + File.open(dest, 'wb', s.stat.mode) do |f| + IO.copy_stream(s, f) + end + end + end + + def copy_metadata(path) + st = lstat() + if !st.symlink? + File.utime st.atime, st.mtime, path + end + begin + if st.symlink? + begin + File.lchown st.uid, st.gid, path + rescue NotImplementedError + end + else + File.chown st.uid, st.gid, path + end + rescue Errno::EPERM + # clear setuid/setgid + if st.symlink? + begin + File.lchmod st.mode & 01777, path + rescue NotImplementedError + end + else + File.chmod st.mode & 01777, path + end + else + if st.symlink? + begin + File.lchmod st.mode, path + rescue NotImplementedError + end + else + File.chmod st.mode, path + end + end + end + + def remove + if directory? + remove_dir1 + else + remove_file + end + end + + def remove_dir1 + platform_support { + Dir.rmdir path().chomp(?/) + } + end + + def remove_file + platform_support { + File.unlink path + } + end + + def platform_support + return yield unless fu_windows? + first_time_p = true + begin + yield + rescue Errno::ENOENT + raise + rescue => err + if first_time_p + first_time_p = false + begin + File.chmod 0700, path() # Windows does not have symlink + retry + rescue SystemCallError + end + end + raise err + end + end + + def preorder_traverse + stack = [self] + while ent = stack.pop + yield ent + stack.concat ent.entries.reverse if ent.directory? + end + end + + alias traverse preorder_traverse + + def postorder_traverse + if directory? + entries().each do |ent| + ent.postorder_traverse do |e| + yield e + end + end + end + yield self + end + + def wrap_traverse(pre, post) + pre.call self + if directory? + entries.each do |ent| + ent.wrap_traverse pre, post + end + end + post.call self + end + + private + + $fileutils_rb_have_lchmod = nil + + def have_lchmod? + # This is not MT-safe, but it does not matter. + if $fileutils_rb_have_lchmod == nil + $fileutils_rb_have_lchmod = check_have_lchmod? + end + $fileutils_rb_have_lchmod + end + + def check_have_lchmod? + return false unless File.respond_to?(:lchmod) + File.lchmod 0 + return true + rescue NotImplementedError + return false + end + + $fileutils_rb_have_lchown = nil + + def have_lchown? + # This is not MT-safe, but it does not matter. + if $fileutils_rb_have_lchown == nil + $fileutils_rb_have_lchown = check_have_lchown? + end + $fileutils_rb_have_lchown + end + + def check_have_lchown? + return false unless File.respond_to?(:lchown) + File.lchown nil, nil + return true + rescue NotImplementedError + return false + end + + def join(dir, base) + return File.path(dir) if not base or base == '.' + return File.path(base) if not dir or dir == '.' + File.join(dir, base) + end + + if File::ALT_SEPARATOR + DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)".freeze + else + DIRECTORY_TERM = "(?=/|\\z)".freeze + end + SYSCASE = File::FNM_SYSCASE.nonzero? ? "-i" : "" + + def descendant_diretory?(descendant, ascendant) + /\A(?#{SYSCASE}:#{Regexp.quote(ascendant)})#{DIRECTORY_TERM}/ =~ File.dirname(descendant) + end + end # class Entry_ + + def fu_list(arg) #:nodoc: + [arg].flatten.map {|path| File.path(path) } + end + private_module_function :fu_list + + def fu_each_src_dest(src, dest) #:nodoc: + fu_each_src_dest0(src, dest) do |s, d| + raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d) + yield s, d, File.stat(s) + end + end + private_module_function :fu_each_src_dest + + def fu_each_src_dest0(src, dest) #:nodoc: + if tmp = Array.try_convert(src) + tmp.each do |s| + s = File.path(s) + yield s, File.join(dest, File.basename(s)) + end + else + src = File.path(src) + if File.directory?(dest) + yield src, File.join(dest, File.basename(src)) + else + yield src, File.path(dest) + end + end + end + private_module_function :fu_each_src_dest0 + + def fu_same?(a, b) #:nodoc: + File.identical?(a, b) + end + private_module_function :fu_same? + + def fu_check_options(options, optdecl) #:nodoc: + h = options.dup + optdecl.each do |opt| + h.delete opt + end + raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty? + end + private_module_function :fu_check_options + + def fu_update_option(args, new) #:nodoc: + if tmp = Hash.try_convert(args.last) + args[-1] = tmp.dup.update(new) + else + args.push new + end + args + end + private_module_function :fu_update_option + + @fileutils_output = $stderr + @fileutils_label = '' + + def fu_output_message(msg) #:nodoc: + @fileutils_output ||= $stderr + @fileutils_label ||= '' + @fileutils_output.puts @fileutils_label + msg + end + private_module_function :fu_output_message + + # + # Returns an Array of method names which have any options. + # + # p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...] + # + def FileUtils.commands + OPT_TABLE.keys + end + + # + # Returns an Array of option names. + # + # p FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"] + # + def FileUtils.options + OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s } + end + + # + # Returns true if the method +mid+ have an option +opt+. + # + # p FileUtils.have_option?(:cp, :noop) #=> true + # p FileUtils.have_option?(:rm, :force) #=> true + # p FileUtils.have_option?(:rm, :preserve) #=> false + # + def FileUtils.have_option?(mid, opt) + li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}" + li.include?(opt) + end + + # + # Returns an Array of option names of the method +mid+. + # + # p FileUtils.options(:rm) #=> ["noop", "verbose", "force"] + # + def FileUtils.options_of(mid) + OPT_TABLE[mid.to_s].map {|sym| sym.to_s } + end + + # + # Returns an Array of method names which have the option +opt+. + # + # p FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"] + # + def FileUtils.collect_method(opt) + OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) } + end + + LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern) + module LowMethods + module_eval("private\n" + ::FileUtils::LOW_METHODS.map {|name| "def #{name}(*)end"}.join("\n"), + __FILE__, __LINE__) + end + + METHODS = singleton_methods() - [:private_module_function, + :commands, :options, :have_option?, :options_of, :collect_method] + + # + # This module has all methods of FileUtils module, but it outputs messages + # before acting. This equates to passing the :verbose flag to + # methods in FileUtils. + # + module Verbose + include FileUtils + @fileutils_output = $stderr + @fileutils_label = '' + ::FileUtils.collect_method(:verbose).each do |name| + module_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{name}(*args) + super(*fu_update_option(args, :verbose => true)) + end + private :#{name} + EOS + end + extend self + class << self + ::FileUtils::METHODS.each do |m| + public m + end + end + end + + # + # This module has all methods of FileUtils module, but never changes + # files/directories. This equates to passing the :noop flag + # to methods in FileUtils. + # + module NoWrite + include FileUtils + include LowMethods + @fileutils_output = $stderr + @fileutils_label = '' + ::FileUtils.collect_method(:noop).each do |name| + module_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{name}(*args) + super(*fu_update_option(args, :noop => true)) + end + private :#{name} + EOS + end + extend self + class << self + ::FileUtils::METHODS.each do |m| + public m + end + end + end + + # + # This module has all methods of FileUtils module, but never changes + # files/directories, with printing message before acting. + # This equates to passing the :noop and :verbose flag + # to methods in FileUtils. + # + module DryRun + include FileUtils + include LowMethods + @fileutils_output = $stderr + @fileutils_label = '' + ::FileUtils.collect_method(:noop).each do |name| + module_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{name}(*args) + super(*fu_update_option(args, :noop => true, :verbose => true)) + end + private :#{name} + EOS + end + extend self + class << self + ::FileUtils::METHODS.each do |m| + public m + end + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/find.rb b/ruby/lib/ruby/2.1.0/find.rb new file mode 100644 index 0000000..6f3e428 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/find.rb @@ -0,0 +1,85 @@ +# +# find.rb: the Find module for processing all files under a given directory. +# + +# +# The +Find+ module supports the top-down traversal of a set of file paths. +# +# For example, to total the size of all files under your home directory, +# ignoring anything in a "dot" directory (e.g. $HOME/.ssh): +# +# require 'find' +# +# total_size = 0 +# +# Find.find(ENV["HOME"]) do |path| +# if FileTest.directory?(path) +# if File.basename(path)[0] == ?. +# Find.prune # Don't look any further into this directory. +# else +# next +# end +# else +# total_size += FileTest.size(path) +# end +# end +# +module Find + + # + # Calls the associated block with the name of every file and directory listed + # as arguments, then recursively on their subdirectories, and so on. + # + # Returns an enumerator if no block is given. + # + # See the +Find+ module documentation for an example. + # + def find(*paths) # :yield: path + block_given? or return enum_for(__method__, *paths) + + fs_encoding = Encoding.find("filesystem") + + paths.collect!{|d| raise Errno::ENOENT unless File.exist?(d); d.dup}.each do |path| + enc = path.encoding == Encoding::US_ASCII ? fs_encoding : path.encoding + ps = [path] + while file = ps.shift + catch(:prune) do + yield file.dup.taint + begin + s = File.lstat(file) + rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG + next + end + if s.directory? then + begin + fs = Dir.entries(file, encoding: enc) + rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG + next + end + fs.sort! + fs.reverse_each {|f| + next if f == "." or f == ".." + f = File.join(file, f) + ps.unshift f.untaint + } + end + end + end + end + nil + end + + # + # Skips the current file or directory, restarting the loop with the next + # entry. If the current file is a directory, that directory will not be + # recursively entered. Meaningful only within the block associated with + # Find::find. + # + # See the +Find+ module documentation for an example. + # + def prune + throw :prune + end + + module_function :find, :prune +end diff --git a/ruby/lib/ruby/2.1.0/forwardable.rb b/ruby/lib/ruby/2.1.0/forwardable.rb new file mode 100644 index 0000000..694ab89 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/forwardable.rb @@ -0,0 +1,289 @@ +# +# forwardable.rb - +# $Release Version: 1.1$ +# $Revision: 40906 $ +# by Keiju ISHITSUKA(keiju@ishitsuka.com) +# original definition by delegator.rb +# Revised by Daniel J. Berger with suggestions from Florian Gross. +# +# Documentation by James Edward Gray II and Gavin Sinclair + + + +# The Forwardable module provides delegation of specified +# methods to a designated object, using the methods #def_delegator +# and #def_delegators. +# +# For example, say you have a class RecordCollection which +# contains an array @records. You could provide the lookup method +# #record_number(), which simply calls #[] on the @records +# array, like this: +# +# require 'forwardable' +# +# class RecordCollection +# attr_accessor :records +# extend Forwardable +# def_delegator :@records, :[], :record_number +# end +# +# We can use the lookup method like so: +# +# r = RecordCollection.new +# r.records = [4,5,6] +# r.record_number(0) # => 4 +# +# Further, if you wish to provide the methods #size, #<<, and #map, +# all of which delegate to @records, this is how you can do it: +# +# class RecordCollection # re-open RecordCollection class +# def_delegators :@records, :size, :<<, :map +# end +# +# r = RecordCollection.new +# r.records = [1,2,3] +# r.record_number(0) # => 1 +# r.size # => 3 +# r << 4 # => [1, 2, 3, 4] +# r.map { |x| x * 2 } # => [2, 4, 6, 8] +# +# You can even extend regular objects with Forwardable. +# +# my_hash = Hash.new +# my_hash.extend Forwardable # prepare object for delegation +# my_hash.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts() +# my_hash.puts "Howdy!" +# +# == Another example +# +# We want to rely on what has come before obviously, but with delegation we can +# take just the methods we need and even rename them as appropriate. In many +# cases this is preferable to inheritance, which gives us the entire old +# interface, even if much of it isn't needed. +# +# class Queue +# extend Forwardable +# +# def initialize +# @q = [ ] # prepare delegate object +# end +# +# # setup preferred interface, enq() and deq()... +# def_delegator :@q, :push, :enq +# def_delegator :@q, :shift, :deq +# +# # support some general Array methods that fit Queues well +# def_delegators :@q, :clear, :first, :push, :shift, :size +# end +# +# q = Queue.new +# q.enq 1, 2, 3, 4, 5 +# q.push 6 +# +# q.shift # => 1 +# while q.size > 0 +# puts q.deq +# end +# +# q.enq "Ruby", "Perl", "Python" +# puts q.first +# q.clear +# puts q.first +# +# This should output: +# +# 2 +# 3 +# 4 +# 5 +# 6 +# Ruby +# nil +# +# == Notes +# +# Be advised, RDoc will not detect delegated methods. +# +# +forwardable.rb+ provides single-method delegation via the def_delegator and +# def_delegators methods. For full-class delegation via DelegateClass, see +# +delegate.rb+. +# +module Forwardable + # Version of +forwardable.rb+ + FORWARDABLE_VERSION = "1.1.0" + + FILE_REGEXP = %r"#{Regexp.quote(__FILE__)}" + + @debug = nil + class << self + # If true, __FILE__ will remain in the backtrace in the event an + # Exception is raised. + attr_accessor :debug + end + + # Takes a hash as its argument. The key is a symbol or an array of + # symbols. These symbols correspond to method names. The value is + # the accessor to which the methods will be delegated. + # + # :call-seq: + # delegate method => accessor + # delegate [method, method, ...] => accessor + # + def instance_delegate(hash) + hash.each{ |methods, accessor| + methods = [methods] unless methods.respond_to?(:each) + methods.each{ |method| + def_instance_delegator(accessor, method) + } + } + end + + # + # Shortcut for defining multiple delegator methods, but with no + # provision for using a different name. The following two code + # samples have the same effect: + # + # def_delegators :@records, :size, :<<, :map + # + # def_delegator :@records, :size + # def_delegator :@records, :<< + # def_delegator :@records, :map + # + def def_instance_delegators(accessor, *methods) + methods.delete("__send__") + methods.delete("__id__") + for method in methods + def_instance_delegator(accessor, method) + end + end + + # Define +method+ as delegator instance method with an optional + # alias name +ali+. Method calls to +ali+ will be delegated to + # +accessor.method+. + # + # class MyQueue + # extend Forwardable + # attr_reader :queue + # def initialize + # @queue = [] + # end + # + # def_delegator :@queue, :push, :mypush + # end + # + # q = MyQueue.new + # q.mypush 42 + # q.queue #=> [42] + # q.push 23 #=> NoMethodError + # + def def_instance_delegator(accessor, method, ali = method) + line_no = __LINE__; str = %{ + def #{ali}(*args, &block) + begin + #{accessor}.__send__(:#{method}, *args, &block) + rescue Exception + $@.delete_if{|s| Forwardable::FILE_REGEXP =~ s} unless Forwardable::debug + ::Kernel::raise + end + end + } + # If it's not a class or module, it's an instance + begin + module_eval(str, __FILE__, line_no) + rescue + instance_eval(str, __FILE__, line_no) + end + + end + + alias delegate instance_delegate + alias def_delegators def_instance_delegators + alias def_delegator def_instance_delegator +end + +# SingleForwardable can be used to setup delegation at the object level as well. +# +# printer = String.new +# printer.extend SingleForwardable # prepare object for delegation +# printer.def_delegator "STDOUT", "puts" # add delegation for STDOUT.puts() +# printer.puts "Howdy!" +# +# Also, SingleForwardable can be used to set up delegation for a Class or Module. +# +# class Implementation +# def self.service +# puts "serviced!" +# end +# end +# +# module Facade +# extend SingleForwardable +# def_delegator :Implementation, :service +# end +# +# Facade.service #=> serviced! +# +# If you want to use both Forwardable and SingleForwardable, you can +# use methods def_instance_delegator and def_single_delegator, etc. +module SingleForwardable + # Takes a hash as its argument. The key is a symbol or an array of + # symbols. These symbols correspond to method names. The value is + # the accessor to which the methods will be delegated. + # + # :call-seq: + # delegate method => accessor + # delegate [method, method, ...] => accessor + # + def single_delegate(hash) + hash.each{ |methods, accessor| + methods = [methods] unless methods.respond_to?(:each) + methods.each{ |method| + def_single_delegator(accessor, method) + } + } + end + + # + # Shortcut for defining multiple delegator methods, but with no + # provision for using a different name. The following two code + # samples have the same effect: + # + # def_delegators :@records, :size, :<<, :map + # + # def_delegator :@records, :size + # def_delegator :@records, :<< + # def_delegator :@records, :map + # + def def_single_delegators(accessor, *methods) + methods.delete("__send__") + methods.delete("__id__") + for method in methods + def_single_delegator(accessor, method) + end + end + + # :call-seq: + # def_single_delegator(accessor, method, new_name=method) + # + # Defines a method _method_ which delegates to _accessor_ (i.e. it calls + # the method of the same name in _accessor_). If _new_name_ is + # provided, it is used as the name for the delegate method. + def def_single_delegator(accessor, method, ali = method) + str = %{ + def #{ali}(*args, &block) + begin + #{accessor}.__send__(:#{method}, *args, &block) + rescue Exception + $@.delete_if{|s| Forwardable::FILE_REGEXP =~ s} unless Forwardable::debug + ::Kernel::raise + end + end + } + + instance_eval(str, __FILE__, __LINE__) + end + + alias delegate single_delegate + alias def_delegators def_single_delegators + alias def_delegator def_single_delegator +end diff --git a/ruby/lib/ruby/2.1.0/getoptlong.rb b/ruby/lib/ruby/2.1.0/getoptlong.rb new file mode 100644 index 0000000..cf635f0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/getoptlong.rb @@ -0,0 +1,612 @@ +# +# GetoptLong for Ruby +# +# Copyright (C) 1998, 1999, 2000 Motoyuki Kasahara. +# +# You may redistribute and/or modify this library under the same license +# terms as Ruby. +# +# See GetoptLong for documentation. +# +# Additional documents and the latest version of `getoptlong.rb' can be +# found at http://www.sra.co.jp/people/m-kasahr/ruby/getoptlong/ + +# The GetoptLong class allows you to parse command line options similarly to +# the GNU getopt_long() C library call. Note, however, that GetoptLong is a +# pure Ruby implementation. +# +# GetoptLong allows for POSIX-style options like --file as well +# as single letter options like -f +# +# The empty option -- (two minus symbols) is used to end option +# processing. This can be particularly important if options have optional +# arguments. +# +# Here is a simple example of usage: +# +# require 'getoptlong' +# +# opts = GetoptLong.new( +# [ '--help', '-h', GetoptLong::NO_ARGUMENT ], +# [ '--repeat', '-n', GetoptLong::REQUIRED_ARGUMENT ], +# [ '--name', GetoptLong::OPTIONAL_ARGUMENT ] +# ) +# +# dir = nil +# name = nil +# repetitions = 1 +# opts.each do |opt, arg| +# case opt +# when '--help' +# puts <<-EOF +# hello [OPTION] ... DIR +# +# -h, --help: +# show help +# +# --repeat x, -n x: +# repeat x times +# +# --name [name]: +# greet user by name, if name not supplied default is John +# +# DIR: The directory in which to issue the greeting. +# EOF +# when '--repeat' +# repetitions = arg.to_i +# when '--name' +# if arg == '' +# name = 'John' +# else +# name = arg +# end +# end +# end +# +# if ARGV.length != 1 +# puts "Missing dir argument (try --help)" +# exit 0 +# end +# +# dir = ARGV.shift +# +# Dir.chdir(dir) +# for i in (1..repetitions) +# print "Hello" +# if name +# print ", #{name}" +# end +# puts +# end +# +# Example command line: +# +# hello -n 6 --name -- /tmp +# +class GetoptLong + # + # Orderings. + # + ORDERINGS = [REQUIRE_ORDER = 0, PERMUTE = 1, RETURN_IN_ORDER = 2] + + # + # Argument flags. + # + ARGUMENT_FLAGS = [NO_ARGUMENT = 0, REQUIRED_ARGUMENT = 1, + OPTIONAL_ARGUMENT = 2] + + # + # Status codes. + # + STATUS_YET, STATUS_STARTED, STATUS_TERMINATED = 0, 1, 2 + + # + # Error types. + # + class Error < StandardError; end + class AmbiguousOption < Error; end + class NeedlessArgument < Error; end + class MissingArgument < Error; end + class InvalidOption < Error; end + + # + # Set up option processing. + # + # The options to support are passed to new() as an array of arrays. + # Each sub-array contains any number of String option names which carry + # the same meaning, and one of the following flags: + # + # GetoptLong::NO_ARGUMENT :: Option does not take an argument. + # + # GetoptLong::REQUIRED_ARGUMENT :: Option always takes an argument. + # + # GetoptLong::OPTIONAL_ARGUMENT :: Option may or may not take an argument. + # + # The first option name is considered to be the preferred (canonical) name. + # Other than that, the elements of each sub-array can be in any order. + # + def initialize(*arguments) + # + # Current ordering. + # + if ENV.include?('POSIXLY_CORRECT') + @ordering = REQUIRE_ORDER + else + @ordering = PERMUTE + end + + # + # Hash table of option names. + # Keys of the table are option names, and their values are canonical + # names of the options. + # + @canonical_names = Hash.new + + # + # Hash table of argument flags. + # Keys of the table are option names, and their values are argument + # flags of the options. + # + @argument_flags = Hash.new + + # + # Whether error messages are output to $stderr. + # + @quiet = FALSE + + # + # Status code. + # + @status = STATUS_YET + + # + # Error code. + # + @error = nil + + # + # Error message. + # + @error_message = nil + + # + # Rest of catenated short options. + # + @rest_singles = '' + + # + # List of non-option-arguments. + # Append them to ARGV when option processing is terminated. + # + @non_option_arguments = Array.new + + if 0 < arguments.length + set_options(*arguments) + end + end + + # + # Set the handling of the ordering of options and arguments. + # A RuntimeError is raised if option processing has already started. + # + # The supplied value must be a member of GetoptLong::ORDERINGS. It alters + # the processing of options as follows: + # + # REQUIRE_ORDER : + # + # Options are required to occur before non-options. + # + # Processing of options ends as soon as a word is encountered that has not + # been preceded by an appropriate option flag. + # + # For example, if -a and -b are options which do not take arguments, + # parsing command line arguments of '-a one -b two' would result in + # 'one', '-b', 'two' being left in ARGV, and only ('-a', '') being + # processed as an option/arg pair. + # + # This is the default ordering, if the environment variable + # POSIXLY_CORRECT is set. (This is for compatibility with GNU getopt_long.) + # + # PERMUTE : + # + # Options can occur anywhere in the command line parsed. This is the + # default behavior. + # + # Every sequence of words which can be interpreted as an option (with or + # without argument) is treated as an option; non-option words are skipped. + # + # For example, if -a does not require an argument and -b optionally takes + # an argument, parsing '-a one -b two three' would result in ('-a','') and + # ('-b', 'two') being processed as option/arg pairs, and 'one','three' + # being left in ARGV. + # + # If the ordering is set to PERMUTE but the environment variable + # POSIXLY_CORRECT is set, REQUIRE_ORDER is used instead. This is for + # compatibility with GNU getopt_long. + # + # RETURN_IN_ORDER : + # + # All words on the command line are processed as options. Words not + # preceded by a short or long option flag are passed as arguments + # with an option of '' (empty string). + # + # For example, if -a requires an argument but -b does not, a command line + # of '-a one -b two three' would result in option/arg pairs of ('-a', 'one') + # ('-b', ''), ('', 'two'), ('', 'three') being processed. + # + def ordering=(ordering) + # + # The method is failed if option processing has already started. + # + if @status != STATUS_YET + set_error(ArgumentError, "argument error") + raise RuntimeError, + "invoke ordering=, but option processing has already started" + end + + # + # Check ordering. + # + if !ORDERINGS.include?(ordering) + raise ArgumentError, "invalid ordering `#{ordering}'" + end + if ordering == PERMUTE && ENV.include?('POSIXLY_CORRECT') + @ordering = REQUIRE_ORDER + else + @ordering = ordering + end + end + + # + # Return ordering. + # + attr_reader :ordering + + # + # Set options. Takes the same argument as GetoptLong.new. + # + # Raises a RuntimeError if option processing has already started. + # + def set_options(*arguments) + # + # The method is failed if option processing has already started. + # + if @status != STATUS_YET + raise RuntimeError, + "invoke set_options, but option processing has already started" + end + + # + # Clear tables of option names and argument flags. + # + @canonical_names.clear + @argument_flags.clear + + arguments.each do |arg| + if !arg.is_a?(Array) + raise ArgumentError, "the option list contains non-Array argument" + end + + # + # Find an argument flag and it set to `argument_flag'. + # + argument_flag = nil + arg.each do |i| + if ARGUMENT_FLAGS.include?(i) + if argument_flag != nil + raise ArgumentError, "too many argument-flags" + end + argument_flag = i + end + end + + raise ArgumentError, "no argument-flag" if argument_flag == nil + + canonical_name = nil + arg.each do |i| + # + # Check an option name. + # + next if i == argument_flag + begin + if !i.is_a?(String) || i !~ /^-([^-]|-.+)$/ + raise ArgumentError, "an invalid option `#{i}'" + end + if (@canonical_names.include?(i)) + raise ArgumentError, "option redefined `#{i}'" + end + rescue + @canonical_names.clear + @argument_flags.clear + raise + end + + # + # Register the option (`i') to the `@canonical_names' and + # `@canonical_names' Hashes. + # + if canonical_name == nil + canonical_name = i + end + @canonical_names[i] = canonical_name + @argument_flags[i] = argument_flag + end + raise ArgumentError, "no option name" if canonical_name == nil + end + return self + end + + # + # Set/Unset `quiet' mode. + # + attr_writer :quiet + + # + # Return the flag of `quiet' mode. + # + attr_reader :quiet + + # + # `quiet?' is an alias of `quiet'. + # + alias quiet? quiet + + # + # Explicitly terminate option processing. + # + def terminate + return nil if @status == STATUS_TERMINATED + raise RuntimeError, "an error has occurred" if @error != nil + + @status = STATUS_TERMINATED + @non_option_arguments.reverse_each do |argument| + ARGV.unshift(argument) + end + + @canonical_names = nil + @argument_flags = nil + @rest_singles = nil + @non_option_arguments = nil + + return self + end + + # + # Returns true if option processing has terminated, false otherwise. + # + def terminated? + return @status == STATUS_TERMINATED + end + + # + # Set an error (a protected method). + # + def set_error(type, message) + $stderr.print("#{$0}: #{message}\n") if !@quiet + + @error = type + @error_message = message + @canonical_names = nil + @argument_flags = nil + @rest_singles = nil + @non_option_arguments = nil + + raise type, message + end + protected :set_error + + # + # Examine whether an option processing is failed. + # + attr_reader :error + + # + # `error?' is an alias of `error'. + # + alias error? error + + # Return the appropriate error message in POSIX-defined format. + # If no error has occurred, returns nil. + # + def error_message + return @error_message + end + + # + # Get next option name and its argument, as an Array of two elements. + # + # The option name is always converted to the first (preferred) + # name given in the original options to GetoptLong.new. + # + # Example: ['--option', 'value'] + # + # Returns nil if the processing is complete (as determined by + # STATUS_TERMINATED). + # + def get + option_name, option_argument = nil, '' + + # + # Check status. + # + return nil if @error != nil + case @status + when STATUS_YET + @status = STATUS_STARTED + when STATUS_TERMINATED + return nil + end + + # + # Get next option argument. + # + if 0 < @rest_singles.length + argument = '-' + @rest_singles + elsif (ARGV.length == 0) + terminate + return nil + elsif @ordering == PERMUTE + while 0 < ARGV.length && ARGV[0] !~ /^-./ + @non_option_arguments.push(ARGV.shift) + end + if ARGV.length == 0 + terminate + return nil + end + argument = ARGV.shift + elsif @ordering == REQUIRE_ORDER + if (ARGV[0] !~ /^-./) + terminate + return nil + end + argument = ARGV.shift + else + argument = ARGV.shift + end + + # + # Check the special argument `--'. + # `--' indicates the end of the option list. + # + if argument == '--' && @rest_singles.length == 0 + terminate + return nil + end + + # + # Check for long and short options. + # + if argument =~ /^(--[^=]+)/ && @rest_singles.length == 0 + # + # This is a long style option, which start with `--'. + # + pattern = $1 + if @canonical_names.include?(pattern) + option_name = pattern + else + # + # The option `option_name' is not registered in `@canonical_names'. + # It may be an abbreviated. + # + matches = [] + @canonical_names.each_key do |key| + if key.index(pattern) == 0 + option_name = key + matches << key + end + end + if 2 <= matches.length + set_error(AmbiguousOption, "option `#{argument}' is ambiguous between #{matches.join(', ')}") + elsif matches.length == 0 + set_error(InvalidOption, "unrecognized option `#{argument}'") + end + end + + # + # Check an argument to the option. + # + if @argument_flags[option_name] == REQUIRED_ARGUMENT + if argument =~ /=(.*)$/ + option_argument = $1 + elsif 0 < ARGV.length + option_argument = ARGV.shift + else + set_error(MissingArgument, + "option `#{argument}' requires an argument") + end + elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT + if argument =~ /=(.*)$/ + option_argument = $1 + elsif 0 < ARGV.length && ARGV[0] !~ /^-./ + option_argument = ARGV.shift + else + option_argument = '' + end + elsif argument =~ /=(.*)$/ + set_error(NeedlessArgument, + "option `#{option_name}' doesn't allow an argument") + end + + elsif argument =~ /^(-(.))(.*)/ + # + # This is a short style option, which start with `-' (not `--'). + # Short options may be catenated (e.g. `-l -g' is equivalent to + # `-lg'). + # + option_name, ch, @rest_singles = $1, $2, $3 + + if @canonical_names.include?(option_name) + # + # The option `option_name' is found in `@canonical_names'. + # Check its argument. + # + if @argument_flags[option_name] == REQUIRED_ARGUMENT + if 0 < @rest_singles.length + option_argument = @rest_singles + @rest_singles = '' + elsif 0 < ARGV.length + option_argument = ARGV.shift + else + # 1003.2 specifies the format of this message. + set_error(MissingArgument, "option requires an argument -- #{ch}") + end + elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT + if 0 < @rest_singles.length + option_argument = @rest_singles + @rest_singles = '' + elsif 0 < ARGV.length && ARGV[0] !~ /^-./ + option_argument = ARGV.shift + else + option_argument = '' + end + end + else + # + # This is an invalid option. + # 1003.2 specifies the format of this message. + # + if ENV.include?('POSIXLY_CORRECT') + set_error(InvalidOption, "invalid option -- #{ch}") + else + set_error(InvalidOption, "invalid option -- #{ch}") + end + end + else + # + # This is a non-option argument. + # Only RETURN_IN_ORDER falled into here. + # + return '', argument + end + + return @canonical_names[option_name], option_argument + end + + # + # `get_option' is an alias of `get'. + # + alias get_option get + + # Iterator version of `get'. + # + # The block is called repeatedly with two arguments: + # The first is the option name. + # The second is the argument which followed it (if any). + # Example: ('--opt', 'value') + # + # The option name is always converted to the first (preferred) + # name given in the original options to GetoptLong.new. + # + def each + loop do + option_name, option_argument = get_option + break if option_name == nil + yield option_name, option_argument + end + end + + # + # `each_option' is an alias of `each'. + # + alias each_option each +end diff --git a/ruby/lib/ruby/2.1.0/gserver.rb b/ruby/lib/ruby/2.1.0/gserver.rb new file mode 100644 index 0000000..d7b4a07 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/gserver.rb @@ -0,0 +1,310 @@ +# +# Copyright (C) 2001 John W. Small All Rights Reserved +# +# Author:: John W. Small +# Documentation:: Gavin Sinclair +# Licence:: Ruby License + +require "socket" +require "thread" + +# +# GServer implements a generic server, featuring thread pool management, +# simple logging, and multi-server management. See HttpServer in +# xmlrpc/httpserver.rb in the Ruby standard library for an example of +# GServer in action. +# +# Any kind of application-level server can be implemented using this class. +# It accepts multiple simultaneous connections from clients, up to an optional +# maximum number. Several _services_ (i.e. one service per TCP port) can be +# run simultaneously, and stopped at any time through the class method +# GServer.stop(port). All the threading issues are handled, saving +# you the effort. All events are optionally logged, but you can provide your +# own event handlers if you wish. +# +# == Example +# +# Using GServer is simple. Below we implement a simple time server, run it, +# query it, and shut it down. Try this code in +irb+: +# +# require 'gserver' +# +# # +# # A server that returns the time in seconds since 1970. +# # +# class TimeServer < GServer +# def initialize(port=10001, *args) +# super(port, *args) +# end +# def serve(io) +# io.puts(Time.now.to_i) +# end +# end +# +# # Run the server with logging enabled (it's a separate thread). +# server = TimeServer.new +# server.audit = true # Turn logging on. +# server.start +# +# # *** Now point your browser to http://localhost:10001 to see it working *** +# +# # See if it's still running. +# GServer.in_service?(10001) # -> true +# server.stopped? # -> false +# +# # Shut the server down gracefully. +# server.shutdown +# +# # Alternatively, stop it immediately. +# GServer.stop(10001) +# # or, of course, "server.stop". +# +# All the business of accepting connections and exception handling is taken +# care of. All we have to do is implement the method that actually serves the +# client. +# +# === Advanced +# +# As the example above shows, the way to use GServer is to subclass it to +# create a specific server, overriding the +serve+ method. You can override +# other methods as well if you wish, perhaps to collect statistics, or emit +# more detailed logging. +# +# * #connecting +# * #disconnecting +# * #starting +# * #stopping +# +# The above methods are only called if auditing is enabled, via #audit=. +# +# You can also override #log and #error if, for example, you wish to use a +# more sophisticated logging system. +# +class GServer + + DEFAULT_HOST = "127.0.0.1" + + def serve(io) + end + + @@services = {} # Hash of opened ports, i.e. services + @@servicesMutex = Mutex.new + + # Stop the server running on the given port, bound to the given host + # + # +port+:: port, as a Fixnum, of the server to stop + # +host+:: host on which to find the server to stop + def GServer.stop(port, host = DEFAULT_HOST) + @@servicesMutex.synchronize { + @@services[host][port].stop + } + end + + # Check if a server is running on the given port and host + # + # +port+:: port, as a Fixnum, of the server to check + # +host+:: host on which to find the server to check + # + # Returns true if a server is running on that port and host. + def GServer.in_service?(port, host = DEFAULT_HOST) + @@services.has_key?(host) and + @@services[host].has_key?(port) + end + + # Stop the server + def stop + @connectionsMutex.synchronize { + if @tcpServerThread + @tcpServerThread.raise "stop" + end + } + end + + # Returns true if the server has stopped. + def stopped? + @tcpServerThread == nil + end + + # Schedule a shutdown for the server + def shutdown + @shutdown = true + end + + # Return the current number of connected clients + def connections + @connections.size + end + + # Join with the server thread + def join + @tcpServerThread.join if @tcpServerThread + end + + # Port on which to listen, as a Fixnum + attr_reader :port + # Host on which to bind, as a String + attr_reader :host + # Maximum number of connections to accept at a time, as a Fixnum + attr_reader :maxConnections + # IO Device on which log messages should be written + attr_accessor :stdlog + # Set to true to cause the callbacks #connecting, #disconnecting, #starting, + # and #stopping to be called during the server's lifecycle + attr_accessor :audit + # Set to true to show more detailed logging + attr_accessor :debug + + # Called when a client connects, if auditing is enabled. + # + # +client+:: a TCPSocket instance representing the client that connected + # + # Return true to allow this client to connect, false to prevent it. + def connecting(client) + addr = client.peeraddr + log("#{self.class.to_s} #{@host}:#{@port} client:#{addr[1]} " + + "#{addr[2]}<#{addr[3]}> connect") + true + end + + + # Called when a client disconnects, if audition is enabled. + # + # +clientPort+:: the port of the client that is connecting + def disconnecting(clientPort) + log("#{self.class.to_s} #{@host}:#{@port} " + + "client:#{clientPort} disconnect") + end + + protected :connecting, :disconnecting + + # Called when the server is starting up, if auditing is enabled. + def starting() + log("#{self.class.to_s} #{@host}:#{@port} start") + end + + # Called when the server is shutting down, if auditing is enabled. + def stopping() + log("#{self.class.to_s} #{@host}:#{@port} stop") + end + + protected :starting, :stopping + + # Called if #debug is true whenever an unhandled exception is raised. + # This implementation simply logs the backtrace. + # + # +detail+:: the Exception that was caught + def error(detail) + log(detail.backtrace.join("\n")) + end + + # Log a message to #stdlog, if it's defined. This implementation + # outputs the timestamp and message to the log. + # + # +msg+:: the message to log + def log(msg) + if @stdlog + @stdlog.puts("[#{Time.new.ctime}] %s" % msg) + @stdlog.flush + end + end + + protected :error, :log + + # Create a new server + # + # +port+:: the port, as a Fixnum, on which to listen + # +host+:: the host to bind to + # +maxConnections+:: the maximum number of simultaneous connections to + # accept + # +stdlog+:: IO device on which to log messages + # +audit+:: if true, lifecycle callbacks will be called. See #audit + # +debug+:: if true, error messages are logged. See #debug + def initialize(port, host = DEFAULT_HOST, maxConnections = 4, + stdlog = $stderr, audit = false, debug = false) + @tcpServerThread = nil + @port = port + @host = host + @maxConnections = maxConnections + @connections = [] + @connectionsMutex = Mutex.new + @connectionsCV = ConditionVariable.new + @stdlog = stdlog + @audit = audit + @debug = debug + end + + # Start the server if it isn't already running + # + # +maxConnections+:: + # override +maxConnections+ given to the constructor. A negative + # value indicates that the value from the constructor should be used. + def start(maxConnections = -1) + raise "server is already running" if !stopped? + @shutdown = false + @maxConnections = maxConnections if maxConnections > 0 + @@servicesMutex.synchronize { + if GServer.in_service?(@port,@host) + raise "Port already in use: #{host}:#{@port}!" + end + @tcpServer = TCPServer.new(@host,@port) + @port = @tcpServer.addr[1] + @@services[@host] = {} unless @@services.has_key?(@host) + @@services[@host][@port] = self; + } + @tcpServerThread = Thread.new { + begin + starting if @audit + while !@shutdown + @connectionsMutex.synchronize { + while @connections.size >= @maxConnections + @connectionsCV.wait(@connectionsMutex) + end + } + client = @tcpServer.accept + Thread.new(client) { |myClient| + @connections << Thread.current + begin + myPort = myClient.peeraddr[1] + serve(myClient) if !@audit or connecting(myClient) + rescue => detail + error(detail) if @debug + ensure + begin + myClient.close + rescue + end + @connectionsMutex.synchronize { + @connections.delete(Thread.current) + @connectionsCV.signal + } + disconnecting(myPort) if @audit + end + } + end + rescue => detail + error(detail) if @debug + ensure + begin + @tcpServer.close + rescue + end + if @shutdown + @connectionsMutex.synchronize { + while @connections.size > 0 + @connectionsCV.wait(@connectionsMutex) + end + } + else + @connections.each { |c| c.raise "stop" } + end + @tcpServerThread = nil + @@servicesMutex.synchronize { + @@services[@host].delete(@port) + } + stopping if @audit + end + } + self + end + +end diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/bigdecimal.so b/ruby/lib/ruby/2.1.0/i386-mingw32/bigdecimal.so new file mode 100644 index 0000000..4098afc Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/bigdecimal.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/continuation.so b/ruby/lib/ruby/2.1.0/i386-mingw32/continuation.so new file mode 100644 index 0000000..f9d4db6 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/continuation.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/coverage.so b/ruby/lib/ruby/2.1.0/i386-mingw32/coverage.so new file mode 100644 index 0000000..54732fa Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/coverage.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/date_core.so b/ruby/lib/ruby/2.1.0/i386-mingw32/date_core.so new file mode 100644 index 0000000..f1d75fa Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/date_core.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/dbm.so b/ruby/lib/ruby/2.1.0/i386-mingw32/dbm.so new file mode 100644 index 0000000..b80e35f Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/dbm.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/digest.so b/ruby/lib/ruby/2.1.0/i386-mingw32/digest.so new file mode 100644 index 0000000..1f6cc6c Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/digest.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/digest/bubblebabble.so b/ruby/lib/ruby/2.1.0/i386-mingw32/digest/bubblebabble.so new file mode 100644 index 0000000..70163e6 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/digest/bubblebabble.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/digest/md5.so b/ruby/lib/ruby/2.1.0/i386-mingw32/digest/md5.so new file mode 100644 index 0000000..88413af Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/digest/md5.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/digest/rmd160.so b/ruby/lib/ruby/2.1.0/i386-mingw32/digest/rmd160.so new file mode 100644 index 0000000..4161efc Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/digest/rmd160.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/digest/sha1.so b/ruby/lib/ruby/2.1.0/i386-mingw32/digest/sha1.so new file mode 100644 index 0000000..31348f3 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/digest/sha1.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/digest/sha2.so b/ruby/lib/ruby/2.1.0/i386-mingw32/digest/sha2.so new file mode 100644 index 0000000..cdc73f1 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/digest/sha2.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/dl.so b/ruby/lib/ruby/2.1.0/i386-mingw32/dl.so new file mode 100644 index 0000000..c72dda9 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/dl.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/dl/callback.so b/ruby/lib/ruby/2.1.0/i386-mingw32/dl/callback.so new file mode 100644 index 0000000..d1feb34 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/dl/callback.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/big5.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/big5.so new file mode 100644 index 0000000..3130e40 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/big5.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/cp949.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/cp949.so new file mode 100644 index 0000000..03ca7bd Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/cp949.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/emacs_mule.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/emacs_mule.so new file mode 100644 index 0000000..9dd62a7 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/emacs_mule.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/encdb.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/encdb.so new file mode 100644 index 0000000..986daae Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/encdb.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/euc_jp.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/euc_jp.so new file mode 100644 index 0000000..1978dc5 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/euc_jp.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/euc_kr.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/euc_kr.so new file mode 100644 index 0000000..617345a Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/euc_kr.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/euc_tw.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/euc_tw.so new file mode 100644 index 0000000..406a2a1 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/euc_tw.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/gb18030.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/gb18030.so new file mode 100644 index 0000000..11ef3dc Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/gb18030.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/gb2312.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/gb2312.so new file mode 100644 index 0000000..aa4ea6e Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/gb2312.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/gbk.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/gbk.so new file mode 100644 index 0000000..2c24542 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/gbk.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_1.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_1.so new file mode 100644 index 0000000..e63c522 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_1.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_10.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_10.so new file mode 100644 index 0000000..d12868f Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_10.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_11.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_11.so new file mode 100644 index 0000000..ccaddcc Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_11.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_13.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_13.so new file mode 100644 index 0000000..531ebae Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_13.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_14.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_14.so new file mode 100644 index 0000000..35863c9 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_14.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_15.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_15.so new file mode 100644 index 0000000..85a54e5 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_15.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_16.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_16.so new file mode 100644 index 0000000..2854785 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_16.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_2.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_2.so new file mode 100644 index 0000000..9be8268 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_2.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_3.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_3.so new file mode 100644 index 0000000..63ab544 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_3.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_4.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_4.so new file mode 100644 index 0000000..868c8cc Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_4.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_5.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_5.so new file mode 100644 index 0000000..0ae1266 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_5.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_6.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_6.so new file mode 100644 index 0000000..b91c451 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_6.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_7.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_7.so new file mode 100644 index 0000000..3a8df04 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_7.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_8.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_8.so new file mode 100644 index 0000000..5ccdfbc Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_8.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_9.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_9.so new file mode 100644 index 0000000..4fb0892 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/iso_8859_9.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/koi8_r.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/koi8_r.so new file mode 100644 index 0000000..e79bece Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/koi8_r.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/koi8_u.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/koi8_u.so new file mode 100644 index 0000000..ab74212 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/koi8_u.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/shift_jis.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/shift_jis.so new file mode 100644 index 0000000..3fe998b Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/shift_jis.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/big5.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/big5.so new file mode 100644 index 0000000..eb30d8b Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/big5.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/chinese.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/chinese.so new file mode 100644 index 0000000..a88b234 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/chinese.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji.so new file mode 100644 index 0000000..aa1d06a Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_iso2022_kddi.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_iso2022_kddi.so new file mode 100644 index 0000000..c9bbea6 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_iso2022_kddi.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_sjis_docomo.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_sjis_docomo.so new file mode 100644 index 0000000..45b53b5 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_sjis_docomo.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_sjis_kddi.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_sjis_kddi.so new file mode 100644 index 0000000..9eb4a0e Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_sjis_kddi.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_sjis_softbank.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_sjis_softbank.so new file mode 100644 index 0000000..c93abba Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/emoji_sjis_softbank.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/escape.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/escape.so new file mode 100644 index 0000000..3c2e2c7 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/escape.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/gb18030.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/gb18030.so new file mode 100644 index 0000000..74e8362 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/gb18030.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/gbk.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/gbk.so new file mode 100644 index 0000000..ccff898 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/gbk.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/iso2022.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/iso2022.so new file mode 100644 index 0000000..e0adaf9 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/iso2022.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/japanese.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/japanese.so new file mode 100644 index 0000000..3346fe1 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/japanese.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/japanese_euc.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/japanese_euc.so new file mode 100644 index 0000000..80671ec Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/japanese_euc.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/japanese_sjis.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/japanese_sjis.so new file mode 100644 index 0000000..d3d30da Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/japanese_sjis.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/korean.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/korean.so new file mode 100644 index 0000000..b08b73e Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/korean.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/single_byte.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/single_byte.so new file mode 100644 index 0000000..f51c1a1 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/single_byte.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/transdb.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/transdb.so new file mode 100644 index 0000000..19a6ab8 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/transdb.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/utf8_mac.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/utf8_mac.so new file mode 100644 index 0000000..bf8a817 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/utf8_mac.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/utf_16_32.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/utf_16_32.so new file mode 100644 index 0000000..ecd5484 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/trans/utf_16_32.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_16be.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_16be.so new file mode 100644 index 0000000..f6d1755 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_16be.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_16le.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_16le.so new file mode 100644 index 0000000..6a8921a Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_16le.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_32be.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_32be.so new file mode 100644 index 0000000..7d0a9c2 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_32be.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_32le.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_32le.so new file mode 100644 index 0000000..927a022 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/utf_32le.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/windows_1251.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/windows_1251.so new file mode 100644 index 0000000..3a08c1d Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/windows_1251.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/enc/windows_31j.so b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/windows_31j.so new file mode 100644 index 0000000..629a453 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/enc/windows_31j.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/etc.so b/ruby/lib/ruby/2.1.0/i386-mingw32/etc.so new file mode 100644 index 0000000..ab8c6fd Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/etc.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/fcntl.so b/ruby/lib/ruby/2.1.0/i386-mingw32/fcntl.so new file mode 100644 index 0000000..5ea5194 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/fcntl.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/fiber.so b/ruby/lib/ruby/2.1.0/i386-mingw32/fiber.so new file mode 100644 index 0000000..a77d1fd Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/fiber.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/fiddle.so b/ruby/lib/ruby/2.1.0/i386-mingw32/fiddle.so new file mode 100644 index 0000000..1380489 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/fiddle.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/gdbm.so b/ruby/lib/ruby/2.1.0/i386-mingw32/gdbm.so new file mode 100644 index 0000000..21c9136 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/gdbm.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/io/console.so b/ruby/lib/ruby/2.1.0/i386-mingw32/io/console.so new file mode 100644 index 0000000..5bab8f7 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/io/console.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/io/nonblock.so b/ruby/lib/ruby/2.1.0/i386-mingw32/io/nonblock.so new file mode 100644 index 0000000..930f92f Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/io/nonblock.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/io/wait.so b/ruby/lib/ruby/2.1.0/i386-mingw32/io/wait.so new file mode 100644 index 0000000..3a5d1dc Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/io/wait.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/json/ext/generator.so b/ruby/lib/ruby/2.1.0/i386-mingw32/json/ext/generator.so new file mode 100644 index 0000000..d6082e0 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/json/ext/generator.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/json/ext/parser.so b/ruby/lib/ruby/2.1.0/i386-mingw32/json/ext/parser.so new file mode 100644 index 0000000..a67eb86 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/json/ext/parser.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/mathn/complex.so b/ruby/lib/ruby/2.1.0/i386-mingw32/mathn/complex.so new file mode 100644 index 0000000..fdf3ced Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/mathn/complex.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/mathn/rational.so b/ruby/lib/ruby/2.1.0/i386-mingw32/mathn/rational.so new file mode 100644 index 0000000..70d9766 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/mathn/rational.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/nkf.so b/ruby/lib/ruby/2.1.0/i386-mingw32/nkf.so new file mode 100644 index 0000000..a8efe0b Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/nkf.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/objspace.so b/ruby/lib/ruby/2.1.0/i386-mingw32/objspace.so new file mode 100644 index 0000000..607af58 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/objspace.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/openssl.so b/ruby/lib/ruby/2.1.0/i386-mingw32/openssl.so new file mode 100644 index 0000000..5fa6dd5 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/openssl.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/pathname.so b/ruby/lib/ruby/2.1.0/i386-mingw32/pathname.so new file mode 100644 index 0000000..b4c7b40 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/pathname.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/psych.so b/ruby/lib/ruby/2.1.0/i386-mingw32/psych.so new file mode 100644 index 0000000..dae2c6c Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/psych.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/racc/cparse.so b/ruby/lib/ruby/2.1.0/i386-mingw32/racc/cparse.so new file mode 100644 index 0000000..a32d7c9 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/racc/cparse.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/rbconfig.rb b/ruby/lib/ruby/2.1.0/i386-mingw32/rbconfig.rb new file mode 100644 index 0000000..4ff4274 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/i386-mingw32/rbconfig.rb @@ -0,0 +1,268 @@ + +# This file was created by mkconfig.rb when ruby was built. Any +# changes made to this file will be lost the next time ruby is built. + +module RbConfig + RUBY_VERSION == "2.1.3" or + raise "ruby lib version (2.1.3) doesn't match executable version (#{RUBY_VERSION})" + + TOPDIR = File.dirname(__FILE__).chomp!("/lib/ruby/2.1.0/i386-mingw32") + DESTDIR = TOPDIR && TOPDIR[/\A[a-z]:/i] || '' unless defined? DESTDIR + CONFIG = {} + CONFIG["DESTDIR"] = DESTDIR + CONFIG["MAJOR"] = "2" + CONFIG["MINOR"] = "1" + CONFIG["TEENY"] = "0" + CONFIG["PATCHLEVEL"] = "242" + CONFIG["INSTALL"] = '/usr/bin/install -c' + CONFIG["EXEEXT"] = ".exe" + CONFIG["prefix"] = (TOPDIR || DESTDIR + "") + CONFIG["ruby_install_name"] = "ruby" + CONFIG["RUBY_INSTALL_NAME"] = "ruby" + CONFIG["RUBY_SO_NAME"] = "msvcrt-ruby210" + CONFIG["exec"] = "exec" + CONFIG["ruby_pc"] = "ruby-2.1.pc" + CONFIG["PACKAGE"] = "ruby" + CONFIG["BUILTIN_TRANSSRCS"] = " newline.c" + CONFIG["USE_RUBYGEMS"] = "YES" + CONFIG["MANTYPE"] = "man" + CONFIG["NROFF"] = "/bin/false" + CONFIG["vendorarchhdrdir"] = "$(vendorhdrdir)/$(sitearch)" + CONFIG["sitearchhdrdir"] = "$(sitehdrdir)/$(sitearch)" + CONFIG["rubyarchhdrdir"] = "$(rubyhdrdir)/$(arch)" + CONFIG["vendorhdrdir"] = "$(rubyhdrdir)/vendor_ruby" + CONFIG["sitehdrdir"] = "$(rubyhdrdir)/site_ruby" + CONFIG["rubyhdrdir"] = "$(includedir)/$(RUBY_VERSION_NAME)" + CONFIG["RUBY_SEARCH_PATH"] = "" + CONFIG["UNIVERSAL_INTS"] = "" + CONFIG["UNIVERSAL_ARCHNAMES"] = "" + CONFIG["configure_args"] = " '--enable-shared' '--disable-install-doc' 'debugflags=-g' 'CPPFLAGS=-DFD_SETSIZE=2048' '--prefix=' 'LDFLAGS='" + CONFIG["vendorarchdir"] = "$(vendorlibdir)/$(sitearch)" + CONFIG["vendorlibdir"] = "$(vendordir)/$(ruby_version)" + CONFIG["vendordir"] = "$(rubylibprefix)/vendor_ruby" + CONFIG["sitearchdir"] = "$(sitelibdir)/$(sitearch)" + CONFIG["sitelibdir"] = "$(sitedir)/$(ruby_version)" + CONFIG["sitedir"] = "$(rubylibprefix)/site_ruby" + CONFIG["rubyarchdir"] = "$(rubylibdir)/$(arch)" + CONFIG["rubylibdir"] = "$(rubylibprefix)/$(ruby_version)" + CONFIG["ruby_version"] = "2.1.0" + CONFIG["sitearch"] = "i386-msvcrt" + CONFIG["arch"] = "i386-mingw32" + CONFIG["sitearchincludedir"] = "$(includedir)/$(sitearch)" + CONFIG["archincludedir"] = "$(includedir)/$(arch)" + CONFIG["sitearchlibdir"] = "$(libdir)/$(sitearch)" + CONFIG["archlibdir"] = "$(libdir)/$(arch)" + CONFIG["libdirname"] = "libdir" + CONFIG["RUBY_EXEC_PREFIX"] = "" + CONFIG["RUBY_LIB_VERSION"] = "" + CONFIG["RUBY_LIB_VERSION_STYLE"] = "3\t/* full */" + CONFIG["RI_BASE_NAME"] = "ri" + CONFIG["ridir"] = "$(datarootdir)/$(RI_BASE_NAME)" + CONFIG["rubysitearchprefix"] = "$(rubylibprefix)/$(sitearch)" + CONFIG["rubyarchprefix"] = "$(rubylibprefix)/$(arch)" + CONFIG["MAKEFILES"] = "Makefile GNUmakefile" + CONFIG["PLATFORM_DIR"] = "win32" + CONFIG["THREAD_MODEL"] = "win32" + CONFIG["SYMBOL_PREFIX"] = "_" + CONFIG["EXPORT_PREFIX"] = " " + CONFIG["COMMON_HEADERS"] = "winsock2.h windows.h" + CONFIG["COMMON_MACROS"] = "" + CONFIG["COMMON_LIBS"] = "m" + CONFIG["MAINLIBS"] = "" + CONFIG["ENABLE_SHARED"] = "yes" + CONFIG["DLDLIBS"] = "" + CONFIG["SOLIBS"] = "$(LIBS)" + CONFIG["LIBRUBYARG_SHARED"] = "-l$(RUBY_SO_NAME)" + CONFIG["LIBRUBYARG_STATIC"] = "-l$(RUBY_SO_NAME)-static" + CONFIG["LIBRUBYARG"] = "$(LIBRUBYARG_SHARED)" + CONFIG["LIBRUBY"] = "lib$(RUBY_SO_NAME).dll.a" + CONFIG["LIBRUBY_ALIASES"] = "" + CONFIG["LIBRUBY_SO"] = "$(RUBY_SO_NAME).dll" + CONFIG["LIBRUBY_A"] = "lib$(RUBY_SO_NAME)-static.a" + CONFIG["RUBYW_INSTALL_NAME"] = "$(RUBYW_BASE_NAME)" + CONFIG["rubyw_install_name"] = "$(RUBYW_INSTALL_NAME)" + CONFIG["EXTDLDFLAGS"] = "" + CONFIG["EXTLDFLAGS"] = "" + CONFIG["strict_warnflags"] = "" + CONFIG["warnflags"] = "-Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wno-long-long -Wno-missing-field-initializers -Wunused-variable -Wpointer-arith -Wwrite-strings -Wdeclaration-after-statement -Wimplicit-function-declaration" + CONFIG["debugflags"] = "-g" + CONFIG["optflags"] = "-O3 -fno-omit-frame-pointer -fno-fast-math" + CONFIG["cxxflags"] = " $(optflags) $(debugflags) $(warnflags)" + CONFIG["cflags"] = " $(optflags) $(debugflags) $(warnflags)" + CONFIG["cppflags"] = "" + CONFIG["NULLCMD"] = ":" + CONFIG["DLNOBJ"] = "dln.o" + CONFIG["INSTALLDOC"] = "nodoc" + CONFIG["CAPITARGET"] = "nodoc" + CONFIG["RDOCTARGET"] = "nodoc" + CONFIG["DTRACE_GLOMMED_OBJ"] = "" + CONFIG["DTRACE_OBJ"] = "" + CONFIG["DTRACE_EXT"] = "dmyh" + CONFIG["EXECUTABLE_EXTS"] = ".exe .com .cmd .bat" + CONFIG["ARCHFILE"] = "" + CONFIG["LIBRUBY_RELATIVE"] = "yes" + CONFIG["EXTOUT"] = ".ext" + CONFIG["RUNRUBY_COMMAND"] = "$(MINIRUBY) $(srcdir)/tool/runruby.rb --extout=$(EXTOUT) $(RUNRUBYOPT)" + CONFIG["PREP"] = "miniruby$(EXEEXT)" + CONFIG["BTESTRUBY"] = "$(MINIRUBY)" + CONFIG["CROSS_COMPILING"] = "no" + CONFIG["TEST_RUNNABLE"] = "yes" + CONFIG["rubylibprefix"] = "$(libdir)/$(RUBY_BASE_NAME)" + CONFIG["setup"] = "Setup" + CONFIG["EXTSTATIC"] = "" + CONFIG["STRIP"] = "strip" + CONFIG["TRY_LINK"] = "" + CONFIG["LIBPATHENV"] = "" + CONFIG["RPATHFLAG"] = "" + CONFIG["LIBPATHFLAG"] = " -L%s" + CONFIG["LINK_SO"] = "" + CONFIG["LIBEXT"] = "a" + CONFIG["DLEXT2"] = "" + CONFIG["DLEXT"] = "so" + CONFIG["LDSHAREDXX"] = "$(CXX) -shared $(if $(filter-out -g -g0,$(debugflags)),,-s)" + CONFIG["LDSHARED"] = "$(CC) -shared $(if $(filter-out -g -g0,$(debugflags)),,-s)" + CONFIG["CCDLFLAGS"] = "" + CONFIG["STATIC"] = "" + CONFIG["ARCH_FLAG"] = "" + CONFIG["DLDFLAGS"] = " -Wl,--enable-auto-image-base,--enable-auto-import $(DEFFILE)" + CONFIG["ALLOCA"] = "" + CONFIG["codesign"] = "" + CONFIG["POSTLINK"] = ":" + CONFIG["WERRORFLAG"] = "-Werror" + CONFIG["CHDIR"] = "cd" + CONFIG["RMALL"] = "rm -fr" + CONFIG["RMDIRS"] = "rmdir --ignore-fail-on-non-empty -p" + CONFIG["RMDIR"] = "rmdir --ignore-fail-on-non-empty" + CONFIG["CP"] = "cp" + CONFIG["RM"] = "rm -f" + CONFIG["PKG_CONFIG"] = "" + CONFIG["PYTHON"] = "" + CONFIG["DOXYGEN"] = "" + CONFIG["DOT"] = "" + CONFIG["DTRACE"] = "" + CONFIG["MAKEDIRS"] = "/usr/bin/mkdir -p" + CONFIG["MKDIR_P"] = "/usr/bin/mkdir -p" + CONFIG["INSTALL_DATA"] = "$(INSTALL) -m 644" + CONFIG["INSTALL_SCRIPT"] = "$(INSTALL)" + CONFIG["INSTALL_PROGRAM"] = "$(INSTALL)" + CONFIG["SET_MAKE"] = "" + CONFIG["LN_S"] = "cp -pR" + CONFIG["NM"] = "nm" + CONFIG["DLLWRAP"] = "dllwrap" + CONFIG["WINDRES"] = "windres" + CONFIG["OBJCOPY"] = ":" + CONFIG["OBJDUMP"] = "objdump" + CONFIG["ASFLAGS"] = "" + CONFIG["AS"] = "as" + CONFIG["AR"] = "ar" + CONFIG["RANLIB"] = "ranlib" + CONFIG["try_header"] = "" + CONFIG["CC_VERSION"] = "$(CC) -v" + CONFIG["COUTFLAG"] = "-o " + CONFIG["OUTFLAG"] = "-o " + CONFIG["CPPOUTFILE"] = "-o conftest.i" + CONFIG["GNU_LD"] = "yes" + CONFIG["LD"] = "ld" + CONFIG["GCC"] = "yes" + CONFIG["EGREP"] = "/usr/bin/grep -E" + CONFIG["GREP"] = "/usr/bin/grep" + CONFIG["CPP"] = "$(CC) -E" + CONFIG["CXXFLAGS"] = "$(cxxflags)" + CONFIG["CXX"] = "g++" + CONFIG["OBJEXT"] = "o" + CONFIG["CPPFLAGS"] = "-DFD_SETSIZE=2048 -D_WIN32_WINNT=0x0501 -D__MINGW_USE_VC2005_COMPAT $(DEFS) $(cppflags)" + CONFIG["LDFLAGS"] = "-L. " + CONFIG["CFLAGS"] = "$(cflags)" + CONFIG["CC"] = "gcc" + CONFIG["NACL_SDK_VARIANT"] = "" + CONFIG["NACL_SDK_ROOT"] = "" + CONFIG["NACL_TOOLCHAIN"] = "" + CONFIG["target_os"] = "mingw32" + CONFIG["target_vendor"] = "pc" + CONFIG["target_cpu"] = "i386" + CONFIG["target"] = "i386-pc-mingw32" + CONFIG["host_os"] = "mingw32" + CONFIG["host_vendor"] = "pc" + CONFIG["host_cpu"] = "i686" + CONFIG["host"] = "i686-pc-mingw32" + CONFIG["RUBY_VERSION_NAME"] = "$(RUBY_BASE_NAME)-$(ruby_version)" + CONFIG["RUBYW_BASE_NAME"] = "rubyw" + CONFIG["RUBY_BASE_NAME"] = "ruby" + CONFIG["build_os"] = "mingw32" + CONFIG["build_vendor"] = "pc" + CONFIG["build_cpu"] = "i686" + CONFIG["build"] = "i686-pc-mingw32" + CONFIG["RUBY_RELEASE_DATE"] = "2014-09-19" + CONFIG["RUBY_PROGRAM_VERSION"] = "2.1.3" + CONFIG["target_alias"] = "" + CONFIG["host_alias"] = "" + CONFIG["build_alias"] = "" + CONFIG["LIBS"] = "-lshell32 -lws2_32 -liphlpapi -limagehlp -lshlwapi " + CONFIG["ECHO_T"] = "" + CONFIG["ECHO_N"] = "-n" + CONFIG["ECHO_C"] = "" + CONFIG["DEFS"] = "-D_FILE_OFFSET_BITS=64" + CONFIG["mandir"] = "$(datarootdir)/man" + CONFIG["localedir"] = "$(datarootdir)/locale" + CONFIG["libdir"] = "$(exec_prefix)/lib" + CONFIG["psdir"] = "$(docdir)" + CONFIG["pdfdir"] = "$(docdir)" + CONFIG["dvidir"] = "$(docdir)" + CONFIG["htmldir"] = "$(docdir)" + CONFIG["infodir"] = "$(datarootdir)/info" + CONFIG["docdir"] = "$(datarootdir)/doc/$(PACKAGE)" + CONFIG["oldincludedir"] = "/usr/include" + CONFIG["includedir"] = "$(prefix)/include" + CONFIG["localstatedir"] = "$(prefix)/var" + CONFIG["sharedstatedir"] = "$(prefix)/com" + CONFIG["sysconfdir"] = "$(prefix)/etc" + CONFIG["datadir"] = "$(datarootdir)" + CONFIG["datarootdir"] = "$(prefix)/share" + CONFIG["libexecdir"] = "$(exec_prefix)/libexec" + CONFIG["sbindir"] = "$(exec_prefix)/sbin" + CONFIG["bindir"] = "$(exec_prefix)/bin" + CONFIG["exec_prefix"] = "$(prefix)" + CONFIG["PACKAGE_URL"] = "" + CONFIG["PACKAGE_BUGREPORT"] = "" + CONFIG["PACKAGE_STRING"] = "" + CONFIG["PACKAGE_VERSION"] = "" + CONFIG["PACKAGE_TARNAME"] = "" + CONFIG["PACKAGE_NAME"] = "" + CONFIG["PATH_SEPARATOR"] = ":" + CONFIG["SHELL"] = "/bin/sh" + CONFIG["archdir"] = "$(rubyarchdir)" + CONFIG["topdir"] = File.dirname(__FILE__) + MAKEFILE_CONFIG = {} + CONFIG.each{|k,v| MAKEFILE_CONFIG[k] = v.dup} + def RbConfig::expand(val, config = CONFIG) + newval = val.gsub(/\$\$|\$\(([^()]+)\)|\$\{([^{}]+)\}/) { + var = $& + if !(v = $1 || $2) + '$' + elsif key = config[v = v[/\A[^:]+(?=(?::(.*?)=(.*))?\z)/]] + pat, sub = $1, $2 + config[v] = false + config[v] = RbConfig::expand(key, config) + key = key.gsub(/#{Regexp.quote(pat)}(?=\s|\z)/n) {sub} if pat + key + else + var + end + } + val.replace(newval) unless newval == val + val + end + CONFIG.each_value do |val| + RbConfig::expand(val) + end + + # returns the absolute pathname of the ruby command. + def RbConfig.ruby + File.join( + RbConfig::CONFIG["bindir"], + RbConfig::CONFIG["ruby_install_name"] + RbConfig::CONFIG["EXEEXT"] + ) + end +end +autoload :Config, "rbconfig/obsolete.rb" # compatibility for ruby-1.8.4 and older. +CROSS_COMPILING = nil unless defined? CROSS_COMPILING diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/rbconfig/sizeof.so b/ruby/lib/ruby/2.1.0/i386-mingw32/rbconfig/sizeof.so new file mode 100644 index 0000000..bed6a42 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/rbconfig/sizeof.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/ripper.so b/ruby/lib/ruby/2.1.0/i386-mingw32/ripper.so new file mode 100644 index 0000000..e9b3659 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/ripper.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/sdbm.so b/ruby/lib/ruby/2.1.0/i386-mingw32/sdbm.so new file mode 100644 index 0000000..9412d93 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/sdbm.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/socket.so b/ruby/lib/ruby/2.1.0/i386-mingw32/socket.so new file mode 100644 index 0000000..b929023 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/socket.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/stringio.so b/ruby/lib/ruby/2.1.0/i386-mingw32/stringio.so new file mode 100644 index 0000000..6eb0eec Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/stringio.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/strscan.so b/ruby/lib/ruby/2.1.0/i386-mingw32/strscan.so new file mode 100644 index 0000000..b9290bc Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/strscan.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/tcltklib.so b/ruby/lib/ruby/2.1.0/i386-mingw32/tcltklib.so new file mode 100644 index 0000000..2bdaeb1 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/tcltklib.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/thread.so b/ruby/lib/ruby/2.1.0/i386-mingw32/thread.so new file mode 100644 index 0000000..e667e06 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/thread.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/tkutil.so b/ruby/lib/ruby/2.1.0/i386-mingw32/tkutil.so new file mode 100644 index 0000000..73451be Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/tkutil.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/win32ole.so b/ruby/lib/ruby/2.1.0/i386-mingw32/win32ole.so new file mode 100644 index 0000000..939053a Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/win32ole.so differ diff --git a/ruby/lib/ruby/2.1.0/i386-mingw32/zlib.so b/ruby/lib/ruby/2.1.0/i386-mingw32/zlib.so new file mode 100644 index 0000000..3a54eff Binary files /dev/null and b/ruby/lib/ruby/2.1.0/i386-mingw32/zlib.so differ diff --git a/ruby/lib/ruby/2.1.0/io/console/size.rb b/ruby/lib/ruby/2.1.0/io/console/size.rb new file mode 100644 index 0000000..519bc3b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/io/console/size.rb @@ -0,0 +1,22 @@ +# fallback to console window size +def IO.default_console_size + [ + ENV["LINES"].to_i.nonzero? || 25, + ENV["COLUMNS"].to_i.nonzero? || 80, + ] +end + +begin + require 'io/console' +rescue LoadError + class IO + alias console_size default_console_size + end +else + # returns console window size + def IO.console_size + console.winsize + rescue NoMethodError + default_console_size + end +end diff --git a/ruby/lib/ruby/2.1.0/ipaddr.rb b/ruby/lib/ruby/2.1.0/ipaddr.rb new file mode 100644 index 0000000..cadc040 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/ipaddr.rb @@ -0,0 +1,935 @@ +# +# ipaddr.rb - A class to manipulate an IP address +# +# Copyright (c) 2002 Hajimu UMEMOTO . +# Copyright (c) 2007, 2009, 2012 Akinori MUSHA . +# All rights reserved. +# +# You can redistribute and/or modify it under the same terms as Ruby. +# +# $Id: ipaddr.rb 39300 2013-02-18 07:31:17Z knu $ +# +# Contact: +# - Akinori MUSHA (current maintainer) +# +# TODO: +# - scope_id support +# +require 'socket' + +# IPAddr provides a set of methods to manipulate an IP address. Both IPv4 and +# IPv6 are supported. +# +# == Example +# +# require 'ipaddr' +# +# ipaddr1 = IPAddr.new "3ffe:505:2::1" +# +# p ipaddr1 #=> # +# +# p ipaddr1.to_s #=> "3ffe:505:2::1" +# +# ipaddr2 = ipaddr1.mask(48) #=> # +# +# p ipaddr2.to_s #=> "3ffe:505:2::" +# +# ipaddr3 = IPAddr.new "192.168.2.0/24" +# +# p ipaddr3 #=> # + +class IPAddr + + # 32 bit mask for IPv4 + IN4MASK = 0xffffffff + # 128 bit mask for IPv4 + IN6MASK = 0xffffffffffffffffffffffffffffffff + # Format string for IPv6 + IN6FORMAT = (["%.4x"] * 8).join(':') + + # Regexp _internally_ used for parsing IPv4 address. + RE_IPV4ADDRLIKE = %r{ + \A + (\d+) \. (\d+) \. (\d+) \. (\d+) + \z + }x + + # Regexp _internally_ used for parsing IPv6 address. + RE_IPV6ADDRLIKE_FULL = %r{ + \A + (?: + (?: [\da-f]{1,4} : ){7} [\da-f]{1,4} + | + ( (?: [\da-f]{1,4} : ){6} ) + (\d+) \. (\d+) \. (\d+) \. (\d+) + ) + \z + }xi + + # Regexp _internally_ used for parsing IPv6 address. + RE_IPV6ADDRLIKE_COMPRESSED = %r{ + \A + ( (?: (?: [\da-f]{1,4} : )* [\da-f]{1,4} )? ) + :: + ( (?: + ( (?: [\da-f]{1,4} : )* ) + (?: + [\da-f]{1,4} + | + (\d+) \. (\d+) \. (\d+) \. (\d+) + ) + )? ) + \z + }xi + + # Generic IPAddr related error. Exceptions raised in this class should + # inherit from Error. + class Error < ArgumentError; end + + # Raised when the provided IP address is an invalid address. + class InvalidAddressError < Error; end + + # Raised when the address family is invalid such as an address with an + # unsupported family, an address with an inconsistent family, or an address + # who's family cannot be determined. + class AddressFamilyError < Error; end + + # Raised when the address is an invalid length. + class InvalidPrefixError < InvalidAddressError; end + + # Returns the address family of this IP address. + attr_reader :family + + # Creates a new ipaddr containing the given network byte ordered + # string form of an IP address. + def IPAddr::new_ntoh(addr) + return IPAddr.new(IPAddr::ntop(addr)) + end + + # Convert a network byte ordered string form of an IP address into + # human readable form. + def IPAddr::ntop(addr) + case addr.size + when 4 + s = addr.unpack('C4').join('.') + when 16 + s = IN6FORMAT % addr.unpack('n8') + else + raise AddressFamilyError, "unsupported address family" + end + return s + end + + # Returns a new ipaddr built by bitwise AND. + def &(other) + return self.clone.set(@addr & coerce_other(other).to_i) + end + + # Returns a new ipaddr built by bitwise OR. + def |(other) + return self.clone.set(@addr | coerce_other(other).to_i) + end + + # Returns a new ipaddr built by bitwise right-shift. + def >>(num) + return self.clone.set(@addr >> num) + end + + # Returns a new ipaddr built by bitwise left shift. + def <<(num) + return self.clone.set(addr_mask(@addr << num)) + end + + # Returns a new ipaddr built by bitwise negation. + def ~ + return self.clone.set(addr_mask(~@addr)) + end + + # Returns true if two ipaddrs are equal. + def ==(other) + other = coerce_other(other) + return @family == other.family && @addr == other.to_i + end + + # Returns a new ipaddr built by masking IP address with the given + # prefixlen/netmask. (e.g. 8, 64, "255.255.255.0", etc.) + def mask(prefixlen) + return self.clone.mask!(prefixlen) + end + + # Returns true if the given ipaddr is in the range. + # + # e.g.: + # require 'ipaddr' + # net1 = IPAddr.new("192.168.2.0/24") + # net2 = IPAddr.new("192.168.2.100") + # net3 = IPAddr.new("192.168.3.0") + # p net1.include?(net2) #=> true + # p net1.include?(net3) #=> false + def include?(other) + other = coerce_other(other) + if ipv4_mapped? + if (@mask_addr >> 32) != 0xffffffffffffffffffffffff + return false + end + mask_addr = (@mask_addr & IN4MASK) + addr = (@addr & IN4MASK) + family = Socket::AF_INET + else + mask_addr = @mask_addr + addr = @addr + family = @family + end + if other.ipv4_mapped? + other_addr = (other.to_i & IN4MASK) + other_family = Socket::AF_INET + else + other_addr = other.to_i + other_family = other.family + end + + if family != other_family + return false + end + return ((addr & mask_addr) == (other_addr & mask_addr)) + end + alias === include? + + # Returns the integer representation of the ipaddr. + def to_i + return @addr + end + + # Returns a string containing the IP address representation. + def to_s + str = to_string + return str if ipv4? + + str.gsub!(/\b0{1,3}([\da-f]+)\b/i, '\1') + loop do + break if str.sub!(/\A0:0:0:0:0:0:0:0\z/, '::') + break if str.sub!(/\b0:0:0:0:0:0:0\b/, ':') + break if str.sub!(/\b0:0:0:0:0:0\b/, ':') + break if str.sub!(/\b0:0:0:0:0\b/, ':') + break if str.sub!(/\b0:0:0:0\b/, ':') + break if str.sub!(/\b0:0:0\b/, ':') + break if str.sub!(/\b0:0\b/, ':') + break + end + str.sub!(/:{3,}/, '::') + + if /\A::(ffff:)?([\da-f]{1,4}):([\da-f]{1,4})\z/i =~ str + str = sprintf('::%s%d.%d.%d.%d', $1, $2.hex / 256, $2.hex % 256, $3.hex / 256, $3.hex % 256) + end + + str + end + + # Returns a string containing the IP address representation in + # canonical form. + def to_string + return _to_string(@addr) + end + + # Returns a network byte ordered string form of the IP address. + def hton + case @family + when Socket::AF_INET + return [@addr].pack('N') + when Socket::AF_INET6 + return (0..7).map { |i| + (@addr >> (112 - 16 * i)) & 0xffff + }.pack('n8') + else + raise AddressFamilyError, "unsupported address family" + end + end + + # Returns true if the ipaddr is an IPv4 address. + def ipv4? + return @family == Socket::AF_INET + end + + # Returns true if the ipaddr is an IPv6 address. + def ipv6? + return @family == Socket::AF_INET6 + end + + # Returns true if the ipaddr is an IPv4-mapped IPv6 address. + def ipv4_mapped? + return ipv6? && (@addr >> 32) == 0xffff + end + + # Returns true if the ipaddr is an IPv4-compatible IPv6 address. + def ipv4_compat? + if !ipv6? || (@addr >> 32) != 0 + return false + end + a = (@addr & IN4MASK) + return a != 0 && a != 1 + end + + # Returns a new ipaddr built by converting the native IPv4 address + # into an IPv4-mapped IPv6 address. + def ipv4_mapped + if !ipv4? + raise InvalidAddressError, "not an IPv4 address" + end + return self.clone.set(@addr | 0xffff00000000, Socket::AF_INET6) + end + + # Returns a new ipaddr built by converting the native IPv4 address + # into an IPv4-compatible IPv6 address. + def ipv4_compat + if !ipv4? + raise InvalidAddressError, "not an IPv4 address" + end + return self.clone.set(@addr, Socket::AF_INET6) + end + + # Returns a new ipaddr built by converting the IPv6 address into a + # native IPv4 address. If the IP address is not an IPv4-mapped or + # IPv4-compatible IPv6 address, returns self. + def native + if !ipv4_mapped? && !ipv4_compat? + return self + end + return self.clone.set(@addr & IN4MASK, Socket::AF_INET) + end + + # Returns a string for DNS reverse lookup. It returns a string in + # RFC3172 form for an IPv6 address. + def reverse + case @family + when Socket::AF_INET + return _reverse + ".in-addr.arpa" + when Socket::AF_INET6 + return ip6_arpa + else + raise AddressFamilyError, "unsupported address family" + end + end + + # Returns a string for DNS reverse lookup compatible with RFC3172. + def ip6_arpa + if !ipv6? + raise InvalidAddressError, "not an IPv6 address" + end + return _reverse + ".ip6.arpa" + end + + # Returns a string for DNS reverse lookup compatible with RFC1886. + def ip6_int + if !ipv6? + raise InvalidAddressError, "not an IPv6 address" + end + return _reverse + ".ip6.int" + end + + # Returns the successor to the ipaddr. + def succ + return self.clone.set(@addr + 1, @family) + end + + # Compares the ipaddr with another. + def <=>(other) + other = coerce_other(other) + + return nil if other.family != @family + + return @addr <=> other.to_i + end + include Comparable + + # Checks equality used by Hash. + def eql?(other) + return self.class == other.class && self.hash == other.hash && self == other + end + + # Returns a hash value used by Hash, Set, and Array classes + def hash + return ([@addr, @mask_addr].hash << 1) | (ipv4? ? 0 : 1) + end + + # Creates a Range object for the network address. + def to_range + begin_addr = (@addr & @mask_addr) + + case @family + when Socket::AF_INET + end_addr = (@addr | (IN4MASK ^ @mask_addr)) + when Socket::AF_INET6 + end_addr = (@addr | (IN6MASK ^ @mask_addr)) + else + raise AddressFamilyError, "unsupported address family" + end + + return clone.set(begin_addr, @family)..clone.set(end_addr, @family) + end + + # Returns a string containing a human-readable representation of the + # ipaddr. ("#") + def inspect + case @family + when Socket::AF_INET + af = "IPv4" + when Socket::AF_INET6 + af = "IPv6" + else + raise AddressFamilyError, "unsupported address family" + end + return sprintf("#<%s: %s:%s/%s>", self.class.name, + af, _to_string(@addr), _to_string(@mask_addr)) + end + + protected + + # Set +@addr+, the internal stored ip address, to given +addr+. The + # parameter +addr+ is validated using the first +family+ member, + # which is +Socket::AF_INET+ or +Socket::AF_INET6+. + def set(addr, *family) + case family[0] ? family[0] : @family + when Socket::AF_INET + if addr < 0 || addr > IN4MASK + raise InvalidAddressError, "invalid address" + end + when Socket::AF_INET6 + if addr < 0 || addr > IN6MASK + raise InvalidAddressError, "invalid address" + end + else + raise AddressFamilyError, "unsupported address family" + end + @addr = addr + if family[0] + @family = family[0] + end + return self + end + + # Set current netmask to given mask. + def mask!(mask) + if mask.kind_of?(String) + if mask =~ /^\d+$/ + prefixlen = mask.to_i + else + m = IPAddr.new(mask) + if m.family != @family + raise InvalidPrefixError, "address family is not same" + end + @mask_addr = m.to_i + @addr &= @mask_addr + return self + end + else + prefixlen = mask + end + case @family + when Socket::AF_INET + if prefixlen < 0 || prefixlen > 32 + raise InvalidPrefixError, "invalid length" + end + masklen = 32 - prefixlen + @mask_addr = ((IN4MASK >> masklen) << masklen) + when Socket::AF_INET6 + if prefixlen < 0 || prefixlen > 128 + raise InvalidPrefixError, "invalid length" + end + masklen = 128 - prefixlen + @mask_addr = ((IN6MASK >> masklen) << masklen) + else + raise AddressFamilyError, "unsupported address family" + end + @addr = ((@addr >> masklen) << masklen) + return self + end + + private + + # Creates a new ipaddr object either from a human readable IP + # address representation in string, or from a packed in_addr value + # followed by an address family. + # + # In the former case, the following are the valid formats that will + # be recognized: "address", "address/prefixlen" and "address/mask", + # where IPv6 address may be enclosed in square brackets (`[' and + # `]'). If a prefixlen or a mask is specified, it returns a masked + # IP address. Although the address family is determined + # automatically from a specified string, you can specify one + # explicitly by the optional second argument. + # + # Otherwise an IP address is generated from a packed in_addr value + # and an address family. + # + # The IPAddr class defines many methods and operators, and some of + # those, such as &, |, include? and ==, accept a string, or a packed + # in_addr value instead of an IPAddr object. + def initialize(addr = '::', family = Socket::AF_UNSPEC) + if !addr.kind_of?(String) + case family + when Socket::AF_INET, Socket::AF_INET6 + set(addr.to_i, family) + @mask_addr = (family == Socket::AF_INET) ? IN4MASK : IN6MASK + return + when Socket::AF_UNSPEC + raise AddressFamilyError, "address family must be specified" + else + raise AddressFamilyError, "unsupported address family: #{family}" + end + end + prefix, prefixlen = addr.split('/') + if prefix =~ /^\[(.*)\]$/i + prefix = $1 + family = Socket::AF_INET6 + end + # It seems AI_NUMERICHOST doesn't do the job. + #Socket.getaddrinfo(left, nil, Socket::AF_INET6, Socket::SOCK_STREAM, nil, + # Socket::AI_NUMERICHOST) + @addr = @family = nil + if family == Socket::AF_UNSPEC || family == Socket::AF_INET + @addr = in_addr(prefix) + if @addr + @family = Socket::AF_INET + end + end + if !@addr && (family == Socket::AF_UNSPEC || family == Socket::AF_INET6) + @addr = in6_addr(prefix) + @family = Socket::AF_INET6 + end + if family != Socket::AF_UNSPEC && @family != family + raise AddressFamilyError, "address family mismatch" + end + if prefixlen + mask!(prefixlen) + else + @mask_addr = (@family == Socket::AF_INET) ? IN4MASK : IN6MASK + end + end + + def coerce_other(other) + case other + when IPAddr + other + when String + self.class.new(other) + else + self.class.new(other, @family) + end + end + + def in_addr(addr) + case addr + when Array + octets = addr + else + m = RE_IPV4ADDRLIKE.match(addr) or return nil + octets = m.captures + end + octets.inject(0) { |i, s| + (n = s.to_i) < 256 or raise InvalidAddressError, "invalid address" + s.match(/\A0./) and raise InvalidAddressError, "zero-filled number in IPv4 address is ambiguous" + i << 8 | n + } + end + + def in6_addr(left) + case left + when RE_IPV6ADDRLIKE_FULL + if $2 + addr = in_addr($~[2,4]) + left = $1 + ':' + else + addr = 0 + end + right = '' + when RE_IPV6ADDRLIKE_COMPRESSED + if $4 + left.count(':') <= 6 or raise InvalidAddressError, "invalid address" + addr = in_addr($~[4,4]) + left = $1 + right = $3 + '0:0' + else + left.count(':') <= ($1.empty? || $2.empty? ? 8 : 7) or + raise InvalidAddressError, "invalid address" + left = $1 + right = $2 + addr = 0 + end + else + raise InvalidAddressError, "invalid address" + end + l = left.split(':') + r = right.split(':') + rest = 8 - l.size - r.size + if rest < 0 + return nil + end + (l + Array.new(rest, '0') + r).inject(0) { |i, s| + i << 16 | s.hex + } | addr + end + + def addr_mask(addr) + case @family + when Socket::AF_INET + return addr & IN4MASK + when Socket::AF_INET6 + return addr & IN6MASK + else + raise AddressFamilyError, "unsupported address family" + end + end + + def _reverse + case @family + when Socket::AF_INET + return (0..3).map { |i| + (@addr >> (8 * i)) & 0xff + }.join('.') + when Socket::AF_INET6 + return ("%.32x" % @addr).reverse!.gsub!(/.(?!$)/, '\&.') + else + raise AddressFamilyError, "unsupported address family" + end + end + + def _to_string(addr) + case @family + when Socket::AF_INET + return (0..3).map { |i| + (addr >> (24 - 8 * i)) & 0xff + }.join('.') + when Socket::AF_INET6 + return (("%.32x" % addr).gsub!(/.{4}(?!$)/, '\&:')) + else + raise AddressFamilyError, "unsupported address family" + end + end + +end + +unless Socket.const_defined? :AF_INET6 + class Socket < BasicSocket + # IPv6 protocol family + AF_INET6 = Object.new + end + + class << IPSocket + private + + def valid_v6?(addr) + case addr + when IPAddr::RE_IPV6ADDRLIKE_FULL + if $2 + $~[2,4].all? {|i| i.to_i < 256 } + else + true + end + when IPAddr::RE_IPV6ADDRLIKE_COMPRESSED + if $4 + addr.count(':') <= 6 && $~[4,4].all? {|i| i.to_i < 256} + else + addr.count(':') <= 7 + end + else + false + end + end + + alias getaddress_orig getaddress + + public + + # Returns a +String+ based representation of a valid DNS hostname, + # IPv4 or IPv6 address. + # + # IPSocket.getaddress 'localhost' #=> "::1" + # IPSocket.getaddress 'broadcasthost' #=> "255.255.255.255" + # IPSocket.getaddress 'www.ruby-lang.org' #=> "221.186.184.68" + # IPSocket.getaddress 'www.ccc.de' #=> "2a00:1328:e102:ccc0::122" + def getaddress(s) + if valid_v6?(s) + s + else + getaddress_orig(s) + end + end + end +end + +if $0 == __FILE__ + eval DATA.read, nil, $0, __LINE__+4 +end + +__END__ + +require 'test/unit' + +class TC_IPAddr < Test::Unit::TestCase + def test_s_new + [ + ["3FFE:505:ffff::/48"], + ["0:0:0:1::"], + ["2001:200:300::/48"], + ["2001:200:300::192.168.1.2/48"], + ["1:2:3:4:5:6:7::"], + ["::2:3:4:5:6:7:8"], + ].each { |args| + assert_nothing_raised { + IPAddr.new(*args) + } + } + + a = IPAddr.new + assert_equal("::", a.to_s) + assert_equal("0000:0000:0000:0000:0000:0000:0000:0000", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + + a = IPAddr.new("0123:4567:89ab:cdef:0ABC:DEF0:1234:5678") + assert_equal("123:4567:89ab:cdef:abc:def0:1234:5678", a.to_s) + assert_equal("0123:4567:89ab:cdef:0abc:def0:1234:5678", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + + a = IPAddr.new("3ffe:505:2::/48") + assert_equal("3ffe:505:2::", a.to_s) + assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + assert_equal(false, a.ipv4?) + assert_equal(true, a.ipv6?) + assert_equal("#", a.inspect) + + a = IPAddr.new("3ffe:505:2::/ffff:ffff:ffff::") + assert_equal("3ffe:505:2::", a.to_s) + assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0000", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + + a = IPAddr.new("0.0.0.0") + assert_equal("0.0.0.0", a.to_s) + assert_equal("0.0.0.0", a.to_string) + assert_equal(Socket::AF_INET, a.family) + + a = IPAddr.new("192.168.1.2") + assert_equal("192.168.1.2", a.to_s) + assert_equal("192.168.1.2", a.to_string) + assert_equal(Socket::AF_INET, a.family) + assert_equal(true, a.ipv4?) + assert_equal(false, a.ipv6?) + + a = IPAddr.new("192.168.1.2/24") + assert_equal("192.168.1.0", a.to_s) + assert_equal("192.168.1.0", a.to_string) + assert_equal(Socket::AF_INET, a.family) + assert_equal("#", a.inspect) + + a = IPAddr.new("192.168.1.2/255.255.255.0") + assert_equal("192.168.1.0", a.to_s) + assert_equal("192.168.1.0", a.to_string) + assert_equal(Socket::AF_INET, a.family) + + assert_equal("0:0:0:1::", IPAddr.new("0:0:0:1::").to_s) + assert_equal("2001:200:300::", IPAddr.new("2001:200:300::/48").to_s) + + assert_equal("2001:200:300::", IPAddr.new("[2001:200:300::]/48").to_s) + assert_equal("1:2:3:4:5:6:7:0", IPAddr.new("1:2:3:4:5:6:7::").to_s) + assert_equal("0:2:3:4:5:6:7:8", IPAddr.new("::2:3:4:5:6:7:8").to_s) + + assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.256") } + assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("192.168.0.011") } + assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("fe80::1%fxp0") } + assert_raises(IPAddr::InvalidAddressError) { IPAddr.new("[192.168.1.2]/120") } + assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("::1/255.255.255.0") } + assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("::1/129") } + assert_raises(IPAddr::InvalidPrefixError) { IPAddr.new("192.168.0.1/33") } + assert_raises(IPAddr::AddressFamilyError) { IPAddr.new(1) } + assert_raises(IPAddr::AddressFamilyError) { IPAddr.new("::ffff:192.168.1.2/120", Socket::AF_INET) } + end + + def test_s_new_ntoh + addr = '' + IPAddr.new("1234:5678:9abc:def0:1234:5678:9abc:def0").hton.each_byte { |c| + addr += sprintf("%02x", c) + } + assert_equal("123456789abcdef0123456789abcdef0", addr) + addr = '' + IPAddr.new("123.45.67.89").hton.each_byte { |c| + addr += sprintf("%02x", c) + } + assert_equal(sprintf("%02x%02x%02x%02x", 123, 45, 67, 89), addr) + a = IPAddr.new("3ffe:505:2::") + assert_equal("3ffe:505:2::", IPAddr.new_ntoh(a.hton).to_s) + a = IPAddr.new("192.168.2.1") + assert_equal("192.168.2.1", IPAddr.new_ntoh(a.hton).to_s) + end + + def test_ipv4_compat + a = IPAddr.new("::192.168.1.2") + assert_equal("::192.168.1.2", a.to_s) + assert_equal("0000:0000:0000:0000:0000:0000:c0a8:0102", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + assert_equal(true, a.ipv4_compat?) + b = a.native + assert_equal("192.168.1.2", b.to_s) + assert_equal(Socket::AF_INET, b.family) + assert_equal(false, b.ipv4_compat?) + + a = IPAddr.new("192.168.1.2") + b = a.ipv4_compat + assert_equal("::192.168.1.2", b.to_s) + assert_equal(Socket::AF_INET6, b.family) + end + + def test_ipv4_mapped + a = IPAddr.new("::ffff:192.168.1.2") + assert_equal("::ffff:192.168.1.2", a.to_s) + assert_equal("0000:0000:0000:0000:0000:ffff:c0a8:0102", a.to_string) + assert_equal(Socket::AF_INET6, a.family) + assert_equal(true, a.ipv4_mapped?) + b = a.native + assert_equal("192.168.1.2", b.to_s) + assert_equal(Socket::AF_INET, b.family) + assert_equal(false, b.ipv4_mapped?) + + a = IPAddr.new("192.168.1.2") + b = a.ipv4_mapped + assert_equal("::ffff:192.168.1.2", b.to_s) + assert_equal(Socket::AF_INET6, b.family) + end + + def test_reverse + assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa", IPAddr.new("3ffe:505:2::f").reverse) + assert_equal("1.2.168.192.in-addr.arpa", IPAddr.new("192.168.2.1").reverse) + end + + def test_ip6_arpa + assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.arpa", IPAddr.new("3ffe:505:2::f").ip6_arpa) + assert_raises(IPAddr::InvalidAddressError) { + IPAddr.new("192.168.2.1").ip6_arpa + } + end + + def test_ip6_int + assert_equal("f.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.5.0.5.0.e.f.f.3.ip6.int", IPAddr.new("3ffe:505:2::f").ip6_int) + assert_raises(IPAddr::InvalidAddressError) { + IPAddr.new("192.168.2.1").ip6_int + } + end + + def test_to_s + assert_equal("3ffe:0505:0002:0000:0000:0000:0000:0001", IPAddr.new("3ffe:505:2::1").to_string) + assert_equal("3ffe:505:2::1", IPAddr.new("3ffe:505:2::1").to_s) + end +end + +class TC_Operator < Test::Unit::TestCase + + IN6MASK32 = "ffff:ffff::" + IN6MASK128 = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" + + def setup + @in6_addr_any = IPAddr.new() + @a = IPAddr.new("3ffe:505:2::/48") + @b = IPAddr.new("0:0:0:1::") + @c = IPAddr.new(IN6MASK32) + end + alias set_up setup + + def test_or + assert_equal("3ffe:505:2:1::", (@a | @b).to_s) + a = @a + a |= @b + assert_equal("3ffe:505:2:1::", a.to_s) + assert_equal("3ffe:505:2::", @a.to_s) + assert_equal("3ffe:505:2:1::", + (@a | 0x00000000000000010000000000000000).to_s) + end + + def test_and + assert_equal("3ffe:505::", (@a & @c).to_s) + a = @a + a &= @c + assert_equal("3ffe:505::", a.to_s) + assert_equal("3ffe:505:2::", @a.to_s) + assert_equal("3ffe:505::", (@a & 0xffffffff000000000000000000000000).to_s) + end + + def test_shift_right + assert_equal("0:3ffe:505:2::", (@a >> 16).to_s) + a = @a + a >>= 16 + assert_equal("0:3ffe:505:2::", a.to_s) + assert_equal("3ffe:505:2::", @a.to_s) + end + + def test_shift_left + assert_equal("505:2::", (@a << 16).to_s) + a = @a + a <<= 16 + assert_equal("505:2::", a.to_s) + assert_equal("3ffe:505:2::", @a.to_s) + end + + def test_carrot + a = ~@in6_addr_any + assert_equal(IN6MASK128, a.to_s) + assert_equal("::", @in6_addr_any.to_s) + end + + def test_equal + assert_equal(true, @a == IPAddr.new("3FFE:505:2::")) + assert_equal(true, @a == IPAddr.new("3ffe:0505:0002::")) + assert_equal(true, @a == IPAddr.new("3ffe:0505:0002:0:0:0:0:0")) + assert_equal(false, @a == IPAddr.new("3ffe:505:3::")) + assert_equal(true, @a != IPAddr.new("3ffe:505:3::")) + assert_equal(false, @a != IPAddr.new("3ffe:505:2::")) + end + + def test_mask + a = @a.mask(32) + assert_equal("3ffe:505::", a.to_s) + assert_equal("3ffe:505:2::", @a.to_s) + end + + def test_include? + assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::"))) + assert_equal(true, @a.include?(IPAddr.new("3ffe:505:2::1"))) + assert_equal(false, @a.include?(IPAddr.new("3ffe:505:3::"))) + net1 = IPAddr.new("192.168.2.0/24") + assert_equal(true, net1.include?(IPAddr.new("192.168.2.0"))) + assert_equal(true, net1.include?(IPAddr.new("192.168.2.255"))) + assert_equal(false, net1.include?(IPAddr.new("192.168.3.0"))) + # test with integer parameter + int = (192 << 24) + (168 << 16) + (2 << 8) + 13 + + assert_equal(true, net1.include?(int)) + assert_equal(false, net1.include?(int+255)) + + end + + def test_hash + a1 = IPAddr.new('192.168.2.0') + a2 = IPAddr.new('192.168.2.0') + a3 = IPAddr.new('3ffe:505:2::1') + a4 = IPAddr.new('3ffe:505:2::1') + a5 = IPAddr.new('127.0.0.1') + a6 = IPAddr.new('::1') + a7 = IPAddr.new('192.168.2.0/25') + a8 = IPAddr.new('192.168.2.0/25') + + h = { a1 => 'ipv4', a2 => 'ipv4', a3 => 'ipv6', a4 => 'ipv6', a5 => 'ipv4', a6 => 'ipv6', a7 => 'ipv4', a8 => 'ipv4'} + assert_equal(5, h.size) + assert_equal('ipv4', h[a1]) + assert_equal('ipv4', h[a2]) + assert_equal('ipv6', h[a3]) + assert_equal('ipv6', h[a4]) + + require 'set' + s = Set[a1, a2, a3, a4, a5, a6, a7, a8] + assert_equal(5, s.size) + assert_equal(true, s.include?(a1)) + assert_equal(true, s.include?(a2)) + assert_equal(true, s.include?(a3)) + assert_equal(true, s.include?(a4)) + assert_equal(true, s.include?(a5)) + assert_equal(true, s.include?(a6)) + end +end diff --git a/ruby/lib/ruby/2.1.0/irb.rb b/ruby/lib/ruby/2.1.0/irb.rb new file mode 100644 index 0000000..6099fc8 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb.rb @@ -0,0 +1,704 @@ +# +# irb.rb - irb main module +# $Release Version: 0.9.6 $ +# $Revision: 42045 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "e2mmap" + +require "irb/init" +require "irb/context" +require "irb/extend-command" +#require "irb/workspace" + +require "irb/ruby-lex" +require "irb/input-method" +require "irb/locale" + +STDOUT.sync = true + +# IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby +# expressions read from the standard input. +# +# The +irb+ command from your shell will start the interpreter. +# +# == Usage +# +# Use of irb is easy if you know Ruby. +# +# When executing irb, prompts are displayed as follows. Then, enter the Ruby +# expression. An input is executed when it is syntactically complete. +# +# $ irb +# irb(main):001:0> 1+2 +# #=> 3 +# irb(main):002:0> class Foo +# irb(main):003:1> def foo +# irb(main):004:2> print 1 +# irb(main):005:2> end +# irb(main):006:1> end +# #=> nil +# +# The Readline extension module can be used with irb. Use of Readline is +# default if it's installed. +# +# == Command line options +# +# Usage: irb.rb [options] [programfile] [arguments] +# -f Suppress read of ~/.irbrc +# -m Bc mode (load mathn, fraction or matrix are available) +# -d Set $DEBUG to true (same as `ruby -d') +# -r load-module Same as `ruby -r' +# -I path Specify $LOAD_PATH directory +# -U Same as `ruby -U` +# -E enc Same as `ruby -E` +# -w Same as `ruby -w` +# -W[level=2] Same as `ruby -W` +# --inspect Use `inspect' for output (default except for bc mode) +# --noinspect Don't use inspect for output +# --readline Use Readline extension module +# --noreadline Don't use Readline extension module +# --prompt prompt-mode +# --prompt-mode prompt-mode +# Switch prompt mode. Pre-defined prompt modes are +# `default', `simple', `xmp' and `inf-ruby' +# --inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs. +# Suppresses --readline. +# --simple-prompt Simple prompt mode +# --noprompt No prompt mode +# --tracer Display trace for each execution of commands. +# --back-trace-limit n +# Display backtrace top n and tail n. The default +# value is 16. +# --irb_debug n Set internal debug level to n (not for popular use) +# -v, --version Print the version of irb +# +# == Configuration +# +# IRB reads from ~/.irbrc when it's invoked. +# +# If ~/.irbrc doesn't exist, +irb+ will try to read in the following order: +# +# * +.irbrc+ +# * +irb.rc+ +# * +_irbrc+ +# * $irbrc +# +# The following are alternatives to the command line options. To use them type +# as follows in an +irb+ session: +# +# IRB.conf[:IRB_NAME]="irb" +# IRB.conf[:MATH_MODE]=false +# IRB.conf[:INSPECT_MODE]=nil +# IRB.conf[:IRB_RC] = nil +# IRB.conf[:BACK_TRACE_LIMIT]=16 +# IRB.conf[:USE_LOADER] = false +# IRB.conf[:USE_READLINE] = nil +# IRB.conf[:USE_TRACER] = false +# IRB.conf[:IGNORE_SIGINT] = true +# IRB.conf[:IGNORE_EOF] = false +# IRB.conf[:PROMPT_MODE] = :DEFAULT +# IRB.conf[:PROMPT] = {...} +# IRB.conf[:DEBUG_LEVEL]=0 +# +# === Auto indentation +# +# To enable auto-indent mode in irb, add the following to your +.irbrc+: +# +# IRB.conf[:AUTO_INDENT] = true +# +# === Autocompletion +# +# To enable autocompletion for irb, add the following to your +.irbrc+: +# +# require 'irb/completion' +# +# === History +# +# By default, irb disables history and will not store any commands you used. +# +# If you want to enable history, add the following to your +.irbrc+: +# +# IRB.conf[:SAVE_HISTORY] = 1000 +# +# This will now store the last 1000 commands in ~/.irb_history. +# +# See IRB::Context#save_history= for more information. +# +# == Customizing the IRB Prompt +# +# In order to customize the prompt, you can change the following Hash: +# +# IRB.conf[:PROMPT] +# +# This example can be used in your +.irbrc+ +# +# IRB.conf[:PROMPT][:MY_PROMPT] = { # name of prompt mode +# :AUTO_INDENT => true # enables auto-indent mode +# :PROMPT_I => nil, # normal prompt +# :PROMPT_S => nil, # prompt for continuated strings +# :PROMPT_C => nil, # prompt for continuated statement +# :RETURN => " ==>%s\n" # format to return value +# } +# +# IRB.conf[:PROMPT_MODE] = :MY_PROMPT +# +# Or, invoke irb with the above prompt mode by: +# +# irb --prompt my-prompt +# +# Constants +PROMPT_I+, +PROMPT_S+ and +PROMPT_C+ specify the format. In the +# prompt specification, some special strings are available: +# +# %N # command name which is running +# %m # to_s of main object (self) +# %M # inspect of main object (self) +# %l # type of string(", ', /, ]), `]' is inner %w[...] +# %NNi # indent level. NN is degits and means as same as printf("%NNd"). +# # It can be ommited +# %NNn # line number. +# %% # % +# +# For instance, the default prompt mode is defined as follows: +# +# IRB.conf[:PROMPT_MODE][:DEFAULT] = { +# :PROMPT_I => "%N(%m):%03n:%i> ", +# :PROMPT_S => "%N(%m):%03n:%i%l ", +# :PROMPT_C => "%N(%m):%03n:%i* ", +# :RETURN => "%s\n" # used to printf +# } +# +# irb comes with a number of available modes: +# +# # :NULL: +# # :PROMPT_I: +# # :PROMPT_N: +# # :PROMPT_S: +# # :PROMPT_C: +# # :RETURN: | +# # %s +# # :DEFAULT: +# # :PROMPT_I: ! '%N(%m):%03n:%i> ' +# # :PROMPT_N: ! '%N(%m):%03n:%i> ' +# # :PROMPT_S: ! '%N(%m):%03n:%i%l ' +# # :PROMPT_C: ! '%N(%m):%03n:%i* ' +# # :RETURN: | +# # => %s +# # :CLASSIC: +# # :PROMPT_I: ! '%N(%m):%03n:%i> ' +# # :PROMPT_N: ! '%N(%m):%03n:%i> ' +# # :PROMPT_S: ! '%N(%m):%03n:%i%l ' +# # :PROMPT_C: ! '%N(%m):%03n:%i* ' +# # :RETURN: | +# # %s +# # :SIMPLE: +# # :PROMPT_I: ! '>> ' +# # :PROMPT_N: ! '>> ' +# # :PROMPT_S: +# # :PROMPT_C: ! '?> ' +# # :RETURN: | +# # => %s +# # :INF_RUBY: +# # :PROMPT_I: ! '%N(%m):%03n:%i> ' +# # :PROMPT_N: +# # :PROMPT_S: +# # :PROMPT_C: +# # :RETURN: | +# # %s +# # :AUTO_INDENT: true +# # :XMP: +# # :PROMPT_I: +# # :PROMPT_N: +# # :PROMPT_S: +# # :PROMPT_C: +# # :RETURN: |2 +# # ==>%s +# +# == Restrictions +# +# Because irb evaluates input immediately after it is syntactically complete, +# the results may be slightly different than directly using Ruby. +# +# == IRB Sessions +# +# IRB has a special feature, that allows you to manage many sessions at once. +# +# You can create new sessions with Irb.irb, and get a list of current sessions +# with the +jobs+ command in the prompt. +# +# === Commands +# +# JobManager provides commands to handle the current sessions: +# +# jobs # List of current sessions +# fg # Switches to the session of the given number +# kill # Kills the session with the given number +# +# The +exit+ command, or ::irb_exit, will quit the current session and call any +# exit hooks with IRB.irb_at_exit. +# +# A few commands for loading files within the session are also available: +# +# +source+:: +# Loads a given file in the current session and displays the source lines, +# see IrbLoader#source_file +# +irb_load+:: +# Loads the given file similarly to Kernel#load, see IrbLoader#irb_load +# +irb_require+:: +# Loads the given file similarly to Kernel#require +# +# === Configuration +# +# The command line options, or IRB.conf, specify the default behavior of +# Irb.irb. +# +# On the other hand, each conf in IRB@Command+line+options is used to +# individually configure IRB.irb. +# +# If a proc is set for IRB.conf[:IRB_RC], its will be invoked after execution +# of that proc with the context of the current session as its argument. Each +# session can be configured using this mechanism. +# +# === Session variables +# +# There are a few variables in every Irb session that can come in handy: +# +# _:: +# The value command executed, as a local variable +# __:: +# The history of evaluated commands +# __[line_no]:: +# Returns the evaluation value at the given line number, +line_no+. +# If +line_no+ is a negative, the return value +line_no+ many lines before +# the most recent return value. +# +# === Example using IRB Sessions +# +# # invoke a new session +# irb(main):001:0> irb +# # list open sessions +# irb.1(main):001:0> jobs +# #0->irb on main (# : stop) +# #1->irb#1 on main (# : running) +# +# # change the active session +# irb.1(main):002:0> fg 0 +# # define class Foo in top-level session +# irb(main):002:0> class Foo;end +# # invoke a new session with the context of Foo +# irb(main):003:0> irb Foo +# # define Foo#foo +# irb.2(Foo):001:0> def foo +# irb.2(Foo):002:1> print 1 +# irb.2(Foo):003:1> end +# +# # change the active session +# irb.2(Foo):004:0> fg 0 +# # list open sessions +# irb(main):004:0> jobs +# #0->irb on main (# : running) +# #1->irb#1 on main (# : stop) +# #2->irb#2 on Foo (# : stop) +# # check if Foo#foo is available +# irb(main):005:0> Foo.instance_methods #=> [:foo, ...] +# +# # change the active sesssion +# irb(main):006:0> fg 2 +# # define Foo#bar in the context of Foo +# irb.2(Foo):005:0> def bar +# irb.2(Foo):006:1> print "bar" +# irb.2(Foo):007:1> end +# irb.2(Foo):010:0> Foo.instance_methods #=> [:bar, :foo, ...] +# +# # change the active session +# irb.2(Foo):011:0> fg 0 +# irb(main):007:0> f = Foo.new #=> # +# # invoke a new session with the context of f (instance of Foo) +# irb(main):008:0> irb f +# # list open sessions +# irb.3():001:0> jobs +# #0->irb on main (# : stop) +# #1->irb#1 on main (# : stop) +# #2->irb#2 on Foo (# : stop) +# #3->irb#3 on # (# : running) +# # evaluate f.foo +# irb.3():002:0> foo #=> 1 => nil +# # evaluate f.bar +# irb.3():003:0> bar #=> bar => nil +# # kill jobs 1, 2, and 3 +# irb.3():004:0> kill 1, 2, 3 +# # list open sesssions, should only include main session +# irb(main):009:0> jobs +# #0->irb on main (# : running) +# # quit irb +# irb(main):010:0> exit +module IRB + @RCS_ID='-$Id: irb.rb 42045 2013-07-18 13:50:32Z zzak $-' + + # An exception raised by IRB.irb_abort + class Abort < Exception;end + + @CONF = {} + + + # Displays current configuration. + # + # Modifing the configuration is achieved by sending a message to IRB.conf. + # + # See IRB@Configuration for more information. + def IRB.conf + @CONF + end + + # Returns the current version of IRB, including release version and last + # updated date. + def IRB.version + if v = @CONF[:VERSION] then return v end + + require "irb/version" + rv = @RELEASE_VERSION.sub(/\.0/, "") + @CONF[:VERSION] = format("irb %s(%s)", rv, @LAST_UPDATE_DATE) + end + + # The current IRB::Context of the session, see IRB.conf + # + # irb + # irb(main):001:0> IRB.CurrentContext.irb_name = "foo" + # foo(main):002:0> IRB.conf[:MAIN_CONTEXT].irb_name #=> "foo" + def IRB.CurrentContext + IRB.conf[:MAIN_CONTEXT] + end + + # Initializes IRB and creates a new Irb.irb object at the +TOPLEVEL_BINDING+ + def IRB.start(ap_path = nil) + $0 = File::basename(ap_path, ".rb") if ap_path + + IRB.setup(ap_path) + + if @CONF[:SCRIPT] + irb = Irb.new(nil, @CONF[:SCRIPT]) + else + irb = Irb.new + end + + @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC] + @CONF[:MAIN_CONTEXT] = irb.context + + trap("SIGINT") do + irb.signal_handle + end + + begin + catch(:IRB_EXIT) do + irb.eval_input + end + ensure + irb_at_exit + end +# print "\n" + end + + # Calls each event hook of IRB.conf[:AT_EXIT] when the current session quits. + def IRB.irb_at_exit + @CONF[:AT_EXIT].each{|hook| hook.call} + end + + # Quits irb + def IRB.irb_exit(irb, ret) + throw :IRB_EXIT, ret + end + + # Aborts then interrupts irb. + # + # Will raise an Abort exception, or the given +exception+. + def IRB.irb_abort(irb, exception = Abort) + if defined? Thread + irb.context.thread.raise exception, "abort then interrupt!" + else + raise exception, "abort then interrupt!" + end + end + + class Irb + # Creates a new irb session + def initialize(workspace = nil, input_method = nil, output_method = nil) + @context = Context.new(self, workspace, input_method, output_method) + @context.main.extend ExtendCommandBundle + @signal_status = :IN_IRB + + @scanner = RubyLex.new + @scanner.exception_on_syntax_error = false + end + # Returns the current context of this irb session + attr_reader :context + # The lexer used by this irb session + attr_accessor :scanner + + # Evaluates input for this session. + def eval_input + @scanner.set_prompt do + |ltype, indent, continue, line_no| + if ltype + f = @context.prompt_s + elsif continue + f = @context.prompt_c + elsif indent > 0 + f = @context.prompt_n + else + f = @context.prompt_i + end + f = "" unless f + if @context.prompting? + @context.io.prompt = p = prompt(f, ltype, indent, line_no) + else + @context.io.prompt = p = "" + end + if @context.auto_indent_mode + unless ltype + ind = prompt(@context.prompt_i, ltype, indent, line_no)[/.*\z/].size + + indent * 2 - p.size + ind += 2 if continue + @context.io.prompt = p + " " * ind if ind > 0 + end + end + end + + @scanner.set_input(@context.io) do + signal_status(:IN_INPUT) do + if l = @context.io.gets + print l if @context.verbose? + else + if @context.ignore_eof? and @context.io.readable_after_eof? + l = "\n" + if @context.verbose? + printf "Use \"exit\" to leave %s\n", @context.ap_name + end + else + print "\n" + end + end + l + end + end + + @scanner.each_top_level_statement do |line, line_no| + signal_status(:IN_EVAL) do + begin + line.untaint + @context.evaluate(line, line_no) + output_value if @context.echo? + exc = nil + rescue Interrupt => exc + rescue SystemExit, SignalException + raise + rescue Exception => exc + end + if exc + print exc.class, ": ", exc, "\n" + if exc.backtrace[0] =~ /irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ && + !(SyntaxError === exc) + irb_bug = true + else + irb_bug = false + end + + messages = [] + lasts = [] + levels = 0 + for m in exc.backtrace + m = @context.workspace.filter_backtrace(m) unless irb_bug + if m + if messages.size < @context.back_trace_limit + messages.push "\tfrom "+m + else + lasts.push "\tfrom "+m + if lasts.size > @context.back_trace_limit + lasts.shift + levels += 1 + end + end + end + end + print messages.join("\n"), "\n" + unless lasts.empty? + printf "... %d levels...\n", levels if levels > 0 + print lasts.join("\n") + end + print "Maybe IRB bug!\n" if irb_bug + end + if $SAFE > 2 + abort "Error: irb does not work for $SAFE level higher than 2" + end + end + end + end + + # Evaluates the given block using the given +path+ as the Context#irb_path + # and +name+ as the Context#irb_name. + # + # Used by the irb command +source+, see IRB@IRB+Sessions for more + # information. + def suspend_name(path = nil, name = nil) + @context.irb_path, back_path = path, @context.irb_path if path + @context.irb_name, back_name = name, @context.irb_name if name + begin + yield back_path, back_name + ensure + @context.irb_path = back_path if path + @context.irb_name = back_name if name + end + end + + # Evaluates the given block using the given +workspace+ as the + # Context#workspace. + # + # Used by the irb command +irb_load+, see IRB@IRB+Sessions for more + # information. + def suspend_workspace(workspace) + @context.workspace, back_workspace = workspace, @context.workspace + begin + yield back_workspace + ensure + @context.workspace = back_workspace + end + end + + # Evaluates the given block using the given +input_method+ as the + # Context#io. + # + # Used by the irb commands +source+ and +irb_load+, see IRB@IRB+Sessions + # for more information. + def suspend_input_method(input_method) + back_io = @context.io + @context.instance_eval{@io = input_method} + begin + yield back_io + ensure + @context.instance_eval{@io = back_io} + end + end + + # Evaluates the given block using the given +context+ as the Context. + def suspend_context(context) + @context, back_context = context, @context + begin + yield back_context + ensure + @context = back_context + end + end + + # Handler for the signal SIGINT, see Kernel#trap for more information. + def signal_handle + unless @context.ignore_sigint? + print "\nabort!\n" if @context.verbose? + exit + end + + case @signal_status + when :IN_INPUT + print "^C\n" + raise RubyLex::TerminateLineInput + when :IN_EVAL + IRB.irb_abort(self) + when :IN_LOAD + IRB.irb_abort(self, LoadAbort) + when :IN_IRB + # ignore + else + # ignore other cases as well + end + end + + # Evaluates the given block using the given +status+. + def signal_status(status) + return yield if @signal_status == :IN_LOAD + + signal_status_back = @signal_status + @signal_status = status + begin + yield + ensure + @signal_status = signal_status_back + end + end + + def prompt(prompt, ltype, indent, line_no) # :nodoc: + p = prompt.dup + p.gsub!(/%([0-9]+)?([a-zA-Z])/) do + case $2 + when "N" + @context.irb_name + when "m" + @context.main.to_s + when "M" + @context.main.inspect + when "l" + ltype + when "i" + if $1 + format("%" + $1 + "d", indent) + else + indent.to_s + end + when "n" + if $1 + format("%" + $1 + "d", line_no) + else + line_no.to_s + end + when "%" + "%" + end + end + p + end + + def output_value # :nodoc: + printf @context.return_format, @context.inspect_last_value + end + + # Outputs the local variables to this current session, including + # #signal_status and #context, using IRB::Locale. + def inspect + ary = [] + for iv in instance_variables + case (iv = iv.to_s) + when "@signal_status" + ary.push format("%s=:%s", iv, @signal_status.id2name) + when "@context" + ary.push format("%s=%s", iv, eval(iv).__to_s__) + else + ary.push format("%s=%s", iv, eval(iv)) + end + end + format("#<%s: %s>", self.class, ary.join(", ")) + end + end + + def @CONF.inspect + IRB.version unless self[:VERSION] + + array = [] + for k, v in sort{|a1, a2| a1[0].id2name <=> a2[0].id2name} + case k + when :MAIN_CONTEXT, :__TMP__EHV__ + array.push format("CONF[:%s]=...myself...", k.id2name) + when :PROMPT + s = v.collect{ + |kk, vv| + ss = vv.collect{|kkk, vvv| ":#{kkk.id2name}=>#{vvv.inspect}"} + format(":%s=>{%s}", kk.id2name, ss.join(", ")) + } + array.push format("CONF[:%s]={%s}", k.id2name, s.join(", ")) + else + array.push format("CONF[:%s]=%s", k.id2name, v.inspect) + end + end + array.join("\n") + end +end diff --git a/ruby/lib/ruby/2.1.0/irb/cmd/chws.rb b/ruby/lib/ruby/2.1.0/irb/cmd/chws.rb new file mode 100644 index 0000000..1f9c207 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/cmd/chws.rb @@ -0,0 +1,33 @@ +# +# change-ws.rb - +# $Release Version: 0.9.6$ +# $Revision: 38358 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +require "irb/cmd/nop.rb" +require "irb/ext/change-ws.rb" + +# :stopdoc: +module IRB + module ExtendCommand + + class CurrentWorkingWorkspace == === =~ > >= >> [] []= ^ ! != !~] + + def self.select_message(receiver, message, candidates, sep = ".") + candidates.grep(/^#{message}/).collect do |e| + case e + when /^[a-zA-Z_]/ + receiver + sep + e + when /^[0-9]/ + when *Operators + #receiver + " " + e + end + end + end + end +end + +if Readline.respond_to?("basic_word_break_characters=") +# Readline.basic_word_break_characters= " \t\n\"\\'`><=;|&{(" + Readline.basic_word_break_characters= " \t\n`><=;|&{(" +end +Readline.completion_append_character = nil +Readline.completion_proc = IRB::InputCompletor::CompletionProc diff --git a/ruby/lib/ruby/2.1.0/irb/context.rb b/ruby/lib/ruby/2.1.0/irb/context.rb new file mode 100644 index 0000000..6ca90eb --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/context.rb @@ -0,0 +1,422 @@ +# +# irb/context.rb - irb context +# $Release Version: 0.9.6$ +# $Revision: 39048 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "irb/workspace" +require "irb/inspector" + +module IRB + # A class that wraps the current state of the irb session, including the + # configuration of IRB.conf. + class Context + # Creates a new IRB context. + # + # The optional +input_method+ argument: + # + # +nil+:: uses stdin or Readline + # +String+:: uses a File + # +other+:: uses this as InputMethod + def initialize(irb, workspace = nil, input_method = nil, output_method = nil) + @irb = irb + if workspace + @workspace = workspace + else + @workspace = WorkSpace.new + end + @thread = Thread.current if defined? Thread +# @irb_level = 0 + + # copy of default configuration + @ap_name = IRB.conf[:AP_NAME] + @rc = IRB.conf[:RC] + @load_modules = IRB.conf[:LOAD_MODULES] + + @use_readline = IRB.conf[:USE_READLINE] + @verbose = IRB.conf[:VERBOSE] + @io = nil + + self.inspect_mode = IRB.conf[:INSPECT_MODE] + self.math_mode = IRB.conf[:MATH_MODE] if IRB.conf[:MATH_MODE] + self.use_tracer = IRB.conf[:USE_TRACER] if IRB.conf[:USE_TRACER] + self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER] + self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY] + + @ignore_sigint = IRB.conf[:IGNORE_SIGINT] + @ignore_eof = IRB.conf[:IGNORE_EOF] + + @back_trace_limit = IRB.conf[:BACK_TRACE_LIMIT] + + self.prompt_mode = IRB.conf[:PROMPT_MODE] + + if IRB.conf[:SINGLE_IRB] or !defined?(IRB::JobManager) + @irb_name = IRB.conf[:IRB_NAME] + else + @irb_name = IRB.conf[:IRB_NAME]+"#"+IRB.JobManager.n_jobs.to_s + end + @irb_path = "(" + @irb_name + ")" + + case input_method + when nil + case use_readline? + when nil + if (defined?(ReadlineInputMethod) && STDIN.tty? && + IRB.conf[:PROMPT_MODE] != :INF_RUBY) + @io = ReadlineInputMethod.new + else + @io = StdioInputMethod.new + end + when false + @io = StdioInputMethod.new + when true + if defined?(ReadlineInputMethod) + @io = ReadlineInputMethod.new + else + @io = StdioInputMethod.new + end + end + + when String + @io = FileInputMethod.new(input_method) + @irb_name = File.basename(input_method) + @irb_path = input_method + else + @io = input_method + end + self.save_history = IRB.conf[:SAVE_HISTORY] if IRB.conf[:SAVE_HISTORY] + + if output_method + @output_method = output_method + else + @output_method = StdioOutputMethod.new + end + + @echo = IRB.conf[:ECHO] + if @echo.nil? + @echo = true + end + self.debug_level = IRB.conf[:DEBUG_LEVEL] + end + + # The top-level workspace, see WorkSpace#main + def main + @workspace.main + end + + # The toplevel workspace, see #home_workspace + attr_reader :workspace_home + # WorkSpace in the current context + attr_accessor :workspace + # The current thread in this context + attr_reader :thread + # The current input method + # + # Can be either StdioInputMethod, ReadlineInputMethod, FileInputMethod or + # other specified when the context is created. See ::new for more + # information on +input_method+. + attr_accessor :io + + # Current irb session + attr_accessor :irb + # A copy of the default IRB.conf[:AP_NAME] + attr_accessor :ap_name + # A copy of the default IRB.conf[:RC] + attr_accessor :rc + # A copy of the default IRB.conf[:LOAD_MODULES] + attr_accessor :load_modules + # Can be either name from IRB.conf[:IRB_NAME], or the number of + # the current job set by JobManager, such as irb#2 + attr_accessor :irb_name + # Can be either the #irb_name surrounded by parenthesis, or the + # +input_method+ passed to Context.new + attr_accessor :irb_path + + # Whether +Readline+ is enabled or not. + # + # A copy of the default IRB.conf[:USE_READLINE] + # + # See #use_readline= for more information. + attr_reader :use_readline + # A copy of the default IRB.conf[:INSPECT_MODE] + attr_reader :inspect_mode + + # A copy of the default IRB.conf[:PROMPT_MODE] + attr_reader :prompt_mode + # Standard IRB prompt + # + # See IRB@Customizing+the+IRB+Prompt for more information. + attr_accessor :prompt_i + # IRB prompt for continuated strings + # + # See IRB@Customizing+the+IRB+Prompt for more information. + attr_accessor :prompt_s + # IRB prompt for continuated statement (e.g. immediately after an +if+) + # + # See IRB@Customizing+the+IRB+Prompt for more information. + attr_accessor :prompt_c + # See IRB@Customizing+the+IRB+Prompt for more information. + attr_accessor :prompt_n + # Can be either the default IRB.conf[:AUTO_INDENT], or the + # mode set by #prompt_mode= + # + # To enable auto-indentation in irb: + # + # IRB.conf[:AUTO_INDENT] = true + # + # or + # + # irb_context.auto_indent_mode = true + # + # or + # + # IRB.CurrentContext.auto_indent_mode = true + # + # See IRB@Configuration for more information. + attr_accessor :auto_indent_mode + # The format of the return statement, set by #prompt_mode= using the + # +:RETURN+ of the +mode+ passed to set the current #prompt_mode. + attr_accessor :return_format + + # Whether ^C (+control-c+) will be ignored or not. + # + # If set to +false+, ^C will quit irb. + # + # If set to +true+, + # + # * during input: cancel input then return to top level. + # * during execute: abandon current execution. + attr_accessor :ignore_sigint + # Whether ^D (+control-d+) will be ignored or not. + # + # If set to +false+, ^D will quit irb. + attr_accessor :ignore_eof + # Whether to echo the return value to output or not. + # + # Uses IRB.conf[:ECHO] if available, or defaults to +true+. + # + # puts "hello" + # # hello + # #=> nil + # IRB.CurrentContext.echo = false + # puts "omg" + # # omg + attr_accessor :echo + # Whether verbose messages are displayed or not. + # + # A copy of the default IRB.conf[:VERBOSE] + attr_accessor :verbose + # The debug level of irb + # + # See #debug_level= for more information. + attr_reader :debug_level + + # The limit of backtrace lines displayed as top +n+ and tail +n+. + # + # The default value is 16. + # + # Can also be set using the +--back-trace-limit+ command line option. + # + # See IRB@Command+line+options for more command line options. + attr_accessor :back_trace_limit + + # Alias for #use_readline + alias use_readline? use_readline + # Alias for #rc + alias rc? rc + alias ignore_sigint? ignore_sigint + alias ignore_eof? ignore_eof + alias echo? echo + + # Returns whether messages are displayed or not. + def verbose? + if @verbose.nil? + if defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod) + false + elsif !STDIN.tty? or @io.kind_of?(FileInputMethod) + true + else + false + end + else + @verbose + end + end + + # Whether #verbose? is +true+, and +input_method+ is either + # StdioInputMethod or ReadlineInputMethod, see #io for more information. + def prompting? + verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) || + (defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod))) + end + + # The return value of the last statement evaluated. + attr_reader :last_value + + # Sets the return value from the last statement evaluated in this context + # to #last_value. + def set_last_value(value) + @last_value = value + @workspace.evaluate self, "_ = IRB.CurrentContext.last_value" + end + + # Sets the +mode+ of the prompt in this context. + # + # See IRB@Customizing+the+IRB+Prompt for more information. + def prompt_mode=(mode) + @prompt_mode = mode + pconf = IRB.conf[:PROMPT][mode] + @prompt_i = pconf[:PROMPT_I] + @prompt_s = pconf[:PROMPT_S] + @prompt_c = pconf[:PROMPT_C] + @prompt_n = pconf[:PROMPT_N] + @return_format = pconf[:RETURN] + if ai = pconf.include?(:AUTO_INDENT) + @auto_indent_mode = ai + else + @auto_indent_mode = IRB.conf[:AUTO_INDENT] + end + end + + # Whether #inspect_mode is set or not, see #inspect_mode= for more detail. + def inspect? + @inspect_mode.nil? or @inspect_mode + end + + # Whether #io uses a File for the +input_method+ passed when creating the + # current context, see ::new + def file_input? + @io.class == FileInputMethod + end + + # Specifies the inspect mode with +opt+: + # + # +true+:: display +inspect+ + # +false+:: display +to_s+ + # +nil+:: inspect mode in non-math mode, + # non-inspect mode in math mode + # + # See IRB::Inspector for more information. + # + # Can also be set using the +--inspect+ and +--noinspect+ command line + # options. + # + # See IRB@Command+line+options for more command line options. + def inspect_mode=(opt) + + if i = Inspector::INSPECTORS[opt] + @inspect_mode = opt + @inspect_method = i + i.init + else + case opt + when nil + if Inspector.keys_with_inspector(Inspector::INSPECTORS[true]).include?(@inspect_mode) + self.inspect_mode = false + elsif Inspector.keys_with_inspector(Inspector::INSPECTORS[false]).include?(@inspect_mode) + self.inspect_mode = true + else + puts "Can't switch inspect mode." + return + end + when /^\s*\{.*\}\s*$/ + begin + inspector = eval "proc#{opt}" + rescue Exception + puts "Can't switch inspect mode(#{opt})." + return + end + self.inspect_mode = inspector + when Proc + self.inspect_mode = IRB::Inspector(opt) + when Inspector + prefix = "usr%d" + i = 1 + while Inspector::INSPECTORS[format(prefix, i)]; i += 1; end + @inspect_mode = format(prefix, i) + @inspect_method = opt + Inspector.def_inspector(format(prefix, i), @inspect_method) + else + puts "Can't switch inspect mode(#{opt})." + return + end + end + print "Switch to#{unless @inspect_mode; ' non';end} inspect mode.\n" if verbose? + @inspect_mode + end + + # Obsolete method. + # + # Can be set using the +--noreadline+ and +--readline+ command line + # options. + # + # See IRB@Command+line+options for more command line options. + def use_readline=(opt) + print "This method is obsolete." + print "Do nothing." + end + + # Sets the debug level of irb + # + # Can also be set using the +--irb_debug+ command line option. + # + # See IRB@Command+line+options for more command line options. + def debug_level=(value) + @debug_level = value + RubyLex.debug_level = value + end + + # Whether or not debug mode is enabled, see #debug_level=. + def debug? + @debug_level > 0 + end + + def evaluate(line, line_no) # :nodoc: + @line_no = line_no + set_last_value(@workspace.evaluate(self, line, irb_path, line_no)) +# @workspace.evaluate("_ = IRB.conf[:MAIN_CONTEXT]._") +# @_ = @workspace.evaluate(line, irb_path, line_no) + end + + def inspect_last_value # :nodoc: + @inspect_method.inspect_value(@last_value) + end + + alias __exit__ exit + # Exits the current session, see IRB.irb_exit + def exit(ret = 0) + IRB.irb_exit(@irb, ret) + end + + NOPRINTING_IVARS = ["@last_value"] # :nodoc: + NO_INSPECTING_IVARS = ["@irb", "@io"] # :nodoc: + IDNAME_IVARS = ["@prompt_mode"] # :nodoc: + + alias __inspect__ inspect + def inspect # :nodoc: + array = [] + for ivar in instance_variables.sort{|e1, e2| e1 <=> e2} + ivar = ivar.to_s + name = ivar.sub(/^@(.*)$/, '\1') + val = instance_eval(ivar) + case ivar + when *NOPRINTING_IVARS + array.push format("conf.%s=%s", name, "...") + when *NO_INSPECTING_IVARS + array.push format("conf.%s=%s", name, val.to_s) + when *IDNAME_IVARS + array.push format("conf.%s=:%s", name, val.id2name) + else + array.push format("conf.%s=%s", name, val.inspect) + end + end + array.join("\n") + end + alias __to_s__ to_s + alias to_s inspect + end +end diff --git a/ruby/lib/ruby/2.1.0/irb/ext/change-ws.rb b/ruby/lib/ruby/2.1.0/irb/ext/change-ws.rb new file mode 100644 index 0000000..98ec2cd --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/ext/change-ws.rb @@ -0,0 +1,69 @@ +# +# irb/ext/cb.rb - +# $Release Version: 0.9.6$ +# $Revision: 38515 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +module IRB # :nodoc: + class Context + + # Inherited from +TOPLEVEL_BINDING+. + def home_workspace + if defined? @home_workspace + @home_workspace + else + @home_workspace = @workspace + end + end + + # Changes the current workspace to given object or binding. + # + # If the optional argument is omitted, the workspace will be + # #home_workspace which is inherited from +TOPLEVEL_BINDING+ or the main + # object, IRB.conf[:MAIN_CONTEXT] when irb was initialized. + # + # See IRB::WorkSpace.new for more information. + def change_workspace(*_main) + if _main.empty? + @workspace = home_workspace + return main + end + + @workspace = WorkSpace.new(_main[0]) + + if !(class<= 0 + @contents.find{|no, val| no == idx}[1] + else + @contents[idx][1] + end + rescue NameError + nil + end + end + + def push(no, val) + @contents.push [no, val] + @contents.shift if @size != 0 && @contents.size > @size + end + + alias real_inspect inspect + + def inspect + if @contents.empty? + return real_inspect + end + + unless (last = @contents.pop)[1].equal?(self) + @contents.push last + last = nil + end + str = @contents.collect{|no, val| + if val.equal?(self) + "#{no} ...self-history..." + else + "#{no} #{val.inspect}" + end + }.join("\n") + if str == "" + str = "Empty." + end + @contents.push last if last + str + end + end +end + + diff --git a/ruby/lib/ruby/2.1.0/irb/ext/loader.rb b/ruby/lib/ruby/2.1.0/irb/ext/loader.rb new file mode 100644 index 0000000..81341db --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/ext/loader.rb @@ -0,0 +1,130 @@ +# +# loader.rb - +# $Release Version: 0.9.6$ +# $Revision: 38515 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + + +module IRB # :nodoc: + # Raised in the event of an exception in a file loaded from an Irb session + class LoadAbort < Exception;end + + # Provides a few commands for loading files within an irb session. + # + # See ExtendCommandBundle for more information. + module IrbLoader + @RCS_ID='-$Id: loader.rb 38515 2012-12-21 05:45:50Z zzak $-' + + alias ruby_load load + alias ruby_require require + + # Loads the given file similarly to Kernel#load + def irb_load(fn, priv = nil) + path = search_file_from_ruby_path(fn) + raise LoadError, "No such file to load -- #{fn}" unless path + + load_file(path, priv) + end + + def search_file_from_ruby_path(fn) # :nodoc: + if /^#{Regexp.quote(File::Separator)}/ =~ fn + return fn if File.exist?(fn) + return nil + end + + for path in $: + if File.exist?(f = File.join(path, fn)) + return f + end + end + return nil + end + + # Loads a given file in the current session and displays the source lines + # + # See Irb#suspend_input_method for more information. + def source_file(path) + irb.suspend_name(path, File.basename(path)) do + irb.suspend_input_method(FileInputMethod.new(path)) do + |back_io| + irb.signal_status(:IN_LOAD) do + if back_io.kind_of?(FileInputMethod) + irb.eval_input + else + begin + irb.eval_input + rescue LoadAbort + print "load abort!!\n" + end + end + end + end + end + end + + # Loads the given file in the current session's context and evaluates it. + # + # See Irb#suspend_input_method for more information. + def load_file(path, priv = nil) + irb.suspend_name(path, File.basename(path)) do + + if priv + ws = WorkSpace.new(Module.new) + else + ws = WorkSpace.new + end + irb.suspend_workspace(ws) do + irb.suspend_input_method(FileInputMethod.new(path)) do + |back_io| + irb.signal_status(:IN_LOAD) do +# p irb.conf + if back_io.kind_of?(FileInputMethod) + irb.eval_input + else + begin + irb.eval_input + rescue LoadAbort + print "load abort!!\n" + end + end + end + end + end + end + end + + def old # :nodoc: + back_io = @io + back_path = @irb_path + back_name = @irb_name + back_scanner = @irb.scanner + begin + @io = FileInputMethod.new(path) + @irb_name = File.basename(path) + @irb_path = path + @irb.signal_status(:IN_LOAD) do + if back_io.kind_of?(FileInputMethod) + @irb.eval_input + else + begin + @irb.eval_input + rescue LoadAbort + print "load abort!!\n" + end + end + end + ensure + @io = back_io + @irb_name = back_name + @irb_path = back_path + @irb.scanner = back_scanner + end + end + end +end + diff --git a/ruby/lib/ruby/2.1.0/irb/ext/math-mode.rb b/ruby/lib/ruby/2.1.0/irb/ext/math-mode.rb new file mode 100644 index 0000000..8ffafe1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/ext/math-mode.rb @@ -0,0 +1,47 @@ +# +# math-mode.rb - +# $Release Version: 0.9.6$ +# $Revision: 38621 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "mathn" + +module IRB + class Context + # Returns whether bc mode is enabled. + # + # See #math_mode= + attr_reader :math_mode + # Alias for #math_mode + alias math? math_mode + + # Sets bc mode, which loads +lib/mathn.rb+ so fractions or matrix are + # available. + # + # Also available as the +-m+ command line option. + # + # See IRB@Command+line+options and the unix manpage bc(1) for + # more information. + def math_mode=(opt) + if @math_mode == true && !opt + IRB.fail CantReturnToNormalMode + return + end + + @math_mode = opt + if math_mode + main.extend Math + print "start math mode\n" if verbose? + end + end + + def inspect? + @inspect_mode.nil? && !@math_mode or @inspect_mode + end + end +end + diff --git a/ruby/lib/ruby/2.1.0/irb/ext/multi-irb.rb b/ruby/lib/ruby/2.1.0/irb/ext/multi-irb.rb new file mode 100644 index 0000000..59d054a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/ext/multi-irb.rb @@ -0,0 +1,286 @@ +# +# irb/multi-irb.rb - multiple irb module +# $Release Version: 0.9.6$ +# $Revision: 38515 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +IRB.fail CantShiftToMultiIrbMode unless defined?(Thread) +require "thread" + +module IRB + class JobManager + @RCS_ID='-$Id: multi-irb.rb 38515 2012-12-21 05:45:50Z zzak $-' + + # Creates a new JobManager object + def initialize + # @jobs = [[thread, irb],...] + @jobs = [] + @current_job = nil + end + + # The active irb session + attr_accessor :current_job + + # The total number of irb sessions, used to set +irb_name+ of the current + # Context. + def n_jobs + @jobs.size + end + + # Returns the thread for the given +key+ object, see #search for more + # information. + def thread(key) + th, = search(key) + th + end + + # Returns the irb session for the given +key+ object, see #search for more + # information. + def irb(key) + _, irb = search(key) + irb + end + + # Returns the top level thread. + def main_thread + @jobs[0][0] + end + + # Returns the top level irb session. + def main_irb + @jobs[0][1] + end + + # Add the given +irb+ session to the jobs Array. + def insert(irb) + @jobs.push [Thread.current, irb] + end + + # Changes the current active irb session to the given +key+ in the jobs + # Array. + # + # Raises an IrbAlreadyDead exception if the given +key+ is no longer alive. + # + # If the given irb session is already active, an IrbSwitchedToCurrentThread + # exception is raised. + def switch(key) + th, irb = search(key) + IRB.fail IrbAlreadyDead unless th.alive? + IRB.fail IrbSwitchedToCurrentThread if th == Thread.current + @current_job = irb + th.run + Thread.stop + @current_job = irb(Thread.current) + end + + # Terminates the irb sessions specified by the given +keys+. + # + # Raises an IrbAlreadyDead exception if one of the given +keys+ is already + # terminated. + # + # See Thread#exit for more information. + def kill(*keys) + for key in keys + th, _ = search(key) + IRB.fail IrbAlreadyDead unless th.alive? + th.exit + end + end + + # Returns the associated job for the given +key+. + # + # If given an Integer, it will return the +key+ index for the jobs Array. + # + # When an instance of Irb is given, it will return the irb session + # associated with +key+. + # + # If given an instance of Thread, it will return the associated thread + # +key+ using Object#=== on the jobs Array. + # + # Otherwise returns the irb session with the same top-level binding as the + # given +key+. + # + # Raises a NoSuchJob exception if no job can be found with the given +key+. + def search(key) + job = case key + when Integer + @jobs[key] + when Irb + @jobs.find{|k, v| v.equal?(key)} + when Thread + @jobs.assoc(key) + else + @jobs.find{|k, v| v.context.main.equal?(key)} + end + IRB.fail NoSuchJob, key if job.nil? + job + end + + # Deletes the job at the given +key+. + def delete(key) + case key + when Integer + IRB.fail NoSuchJob, key unless @jobs[key] + @jobs[key] = nil + else + catch(:EXISTS) do + @jobs.each_index do + |i| + if @jobs[i] and (@jobs[i][0] == key || + @jobs[i][1] == key || + @jobs[i][1].context.main.equal?(key)) + @jobs[i] = nil + throw :EXISTS + end + end + IRB.fail NoSuchJob, key + end + end + until assoc = @jobs.pop; end unless @jobs.empty? + @jobs.push assoc + end + + # Outputs a list of jobs, see the irb command +irb_jobs+, or +jobs+. + def inspect + ary = [] + @jobs.each_index do + |i| + th, irb = @jobs[i] + next if th.nil? + + if th.alive? + if th.stop? + t_status = "stop" + else + t_status = "running" + end + else + t_status = "exited" + end + ary.push format("#%d->%s on %s (%s: %s)", + i, + irb.context.irb_name, + irb.context.main, + th, + t_status) + end + ary.join("\n") + end + end + + @JobManager = JobManager.new + + # The current JobManager in the session + def IRB.JobManager + @JobManager + end + + # The current Context in this session + def IRB.CurrentContext + IRB.JobManager.irb(Thread.current).context + end + + # Creates a new IRB session, see Irb.new. + # + # The optional +file+ argument is given to Context.new, along with the + # workspace created with the remaining arguments, see WorkSpace.new + def IRB.irb(file = nil, *main) + workspace = WorkSpace.new(*main) + parent_thread = Thread.current + Thread.start do + begin + irb = Irb.new(workspace, file) + rescue + print "Subirb can't start with context(self): ", workspace.main.inspect, "\n" + print "return to main irb\n" + Thread.pass + Thread.main.wakeup + Thread.exit + end + @CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC] + @JobManager.insert(irb) + @JobManager.current_job = irb + begin + system_exit = false + catch(:IRB_EXIT) do + irb.eval_input + end + rescue SystemExit + system_exit = true + raise + #fail + ensure + unless system_exit + @JobManager.delete(irb) + if @JobManager.current_job == irb + if parent_thread.alive? + @JobManager.current_job = @JobManager.irb(parent_thread) + parent_thread.run + else + @JobManager.current_job = @JobManager.main_irb + @JobManager.main_thread.run + end + end + end + end + end + Thread.stop + @JobManager.current_job = @JobManager.irb(Thread.current) + end + +# class Context +# def set_last_value(value) +# @last_value = value +# @workspace.evaluate "_ = IRB.JobManager.irb(Thread.current).context.last_value" +# if @eval_history #and !@__.equal?(@last_value) +# @eval_history_values.push @line_no, @last_value +# @workspace.evaluate "__ = IRB.JobManager.irb(Thread.current).context.instance_eval{@eval_history_values}" +# end +# @last_value +# end +# end + +# module ExtendCommand +# def irb_context +# IRB.JobManager.irb(Thread.current).context +# end +# # alias conf irb_context +# end + + @CONF[:SINGLE_IRB_MODE] = false + @JobManager.insert(@CONF[:MAIN_CONTEXT].irb) + @JobManager.current_job = @CONF[:MAIN_CONTEXT].irb + + class Irb + def signal_handle + unless @context.ignore_sigint? + print "\nabort!!\n" if @context.verbose? + exit + end + + case @signal_status + when :IN_INPUT + print "^C\n" + IRB.JobManager.thread(self).raise RubyLex::TerminateLineInput + when :IN_EVAL + IRB.irb_abort(self) + when :IN_LOAD + IRB.irb_abort(self, LoadAbort) + when :IN_IRB + # ignore + else + # ignore other cases as well + end + end + end + + trap("SIGINT") do + @JobManager.current_job.signal_handle + Thread.stop + end + +end diff --git a/ruby/lib/ruby/2.1.0/irb/ext/save-history.rb b/ruby/lib/ruby/2.1.0/irb/ext/save-history.rb new file mode 100644 index 0000000..d99b1da --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/ext/save-history.rb @@ -0,0 +1,120 @@ +# save-history.rb - +# $Release Version: 0.9.6$ +# $Revision: 39049 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +require "readline" + +module IRB + module HistorySavingAbility # :nodoc: + @RCS_ID='-$Id: save-history.rb 39049 2013-02-04 23:04:09Z zzak $-' + end + + class Context + def init_save_history# :nodoc: + unless (class<<@io;self;end).include?(HistorySavingAbility) + @io.extend(HistorySavingAbility) + end + end + + # A copy of the default IRB.conf[:SAVE_HISTORY] + def save_history + IRB.conf[:SAVE_HISTORY] + end + + # Sets IRB.conf[:SAVE_HISTORY] to the given +val+ and calls + # #init_save_history with this context. + # + # Will store the number of +val+ entries of history in the #history_file + # + # Add the following to your +.irbrc+ to change the number of history + # entries stored to 1000: + # + # IRB.conf[:SAVE_HISTORY] = 1000 + def save_history=(val) + IRB.conf[:SAVE_HISTORY] = val + if val + main_context = IRB.conf[:MAIN_CONTEXT] + main_context = self unless main_context + main_context.init_save_history + end + end + + # A copy of the default IRB.conf[:HISTORY_FILE] + def history_file + IRB.conf[:HISTORY_FILE] + end + + # Set IRB.conf[:HISTORY_FILE] to the given +hist+. + def history_file=(hist) + IRB.conf[:HISTORY_FILE] = hist + end + end + + module HistorySavingAbility # :nodoc: + include Readline + +# def HistorySavingAbility.create_finalizer +# proc do +# if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) > 0 +# if hf = IRB.conf[:HISTORY_FILE] +# file = File.expand_path(hf) +# end +# file = IRB.rc_file("_history") unless file +# open(file, 'w' ) do |f| +# hist = HISTORY.to_a +# f.puts(hist[-num..-1] || hist) +# end +# end +# end +# end + + def HistorySavingAbility.extended(obj) +# ObjectSpace.define_finalizer(obj, HistorySavingAbility.create_finalizer) + IRB.conf[:AT_EXIT].push proc{obj.save_history} + obj.load_history + obj + end + + def load_history + if history_file = IRB.conf[:HISTORY_FILE] + history_file = File.expand_path(history_file) + end + history_file = IRB.rc_file("_history") unless history_file + if File.exist?(history_file) + open(history_file) do |f| + f.each {|l| HISTORY << l.chomp} + end + end + end + + def save_history + if num = IRB.conf[:SAVE_HISTORY] and (num = num.to_i) > 0 + if history_file = IRB.conf[:HISTORY_FILE] + history_file = File.expand_path(history_file) + end + history_file = IRB.rc_file("_history") unless history_file + + # Change the permission of a file that already exists[BUG #7694] + begin + if File.stat(history_file).mode & 066 != 0 + File.chmod(0600, history_file) + end + rescue Errno::ENOENT + rescue + raise + end + + open(history_file, 'w', 0600 ) do |f| + hist = HISTORY.to_a + f.puts(hist[-num..-1] || hist) + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/irb/ext/tracer.rb b/ruby/lib/ruby/2.1.0/irb/ext/tracer.rb new file mode 100644 index 0000000..40b2eb9 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/ext/tracer.rb @@ -0,0 +1,71 @@ +# +# irb/lib/tracer.rb - +# $Release Version: 0.9.6$ +# $Revision: 38515 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "tracer" + +module IRB + + # initialize tracing function + def IRB.initialize_tracer + Tracer.verbose = false + Tracer.add_filter { + |event, file, line, id, binding, *rests| + /^#{Regexp.quote(@CONF[:IRB_LIB_PATH])}/ !~ file and + File::basename(file) != "irb.rb" + } + end + + class Context + # Whether Tracer is used when evaluating statements in this context. + # + # See +lib/tracer.rb+ for more information. + attr_reader :use_tracer + alias use_tracer? use_tracer + + # Sets whether or not to use the Tracer library when evaluating statements + # in this context. + # + # See +lib/tracer.rb+ for more information. + def use_tracer=(opt) + if opt + Tracer.set_get_line_procs(@irb_path) { + |line_no, *rests| + @io.line(line_no) + } + elsif !opt && @use_tracer + Tracer.off + end + @use_tracer=opt + end + end + + class WorkSpace + alias __evaluate__ evaluate + # Evaluate the context of this workspace and use the Tracer library to + # output the exact lines of code are being executed in chronological order. + # + # See +lib/tracer.rb+ for more information. + def evaluate(context, statements, file = nil, line = nil) + if context.use_tracer? && file != nil && line != nil + Tracer.on + begin + __evaluate__(context, statements, file, line) + ensure + Tracer.off + end + else + __evaluate__(context, statements, file || __FILE__, line || __LINE__) + end + end + end + + IRB.initialize_tracer +end + diff --git a/ruby/lib/ruby/2.1.0/irb/ext/use-loader.rb b/ruby/lib/ruby/2.1.0/irb/ext/use-loader.rb new file mode 100644 index 0000000..007e297 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/ext/use-loader.rb @@ -0,0 +1,73 @@ +# +# use-loader.rb - +# $Release Version: 0.9.6$ +# $Revision: 38515 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +require "irb/cmd/load" +require "irb/ext/loader" + +class Object + alias __original__load__IRB_use_loader__ load + alias __original__require__IRB_use_loader__ require +end + +module IRB + module ExtendCommandBundle + # Loads the given file similarly to Kernel#load, see IrbLoader#irb_load + def irb_load(*opts, &b) + ExtendCommand::Load.execute(irb_context, *opts, &b) + end + # Loads the given file similarly to Kernel#require + def irb_require(*opts, &b) + ExtendCommand::Require.execute(irb_context, *opts, &b) + end + end + + class Context + + IRB.conf[:USE_LOADER] = false + + # Returns whether +irb+'s own file reader method is used by + # +load+/+require+ or not. + # + # This mode is globally affected (irb-wide). + def use_loader + IRB.conf[:USE_LOADER] + end + + alias use_loader? use_loader + + # Sets IRB.conf[:USE_LOADER] + # + # See #use_loader for more information. + def use_loader=(opt) + + if IRB.conf[:USE_LOADER] != opt + IRB.conf[:USE_LOADER] = opt + if opt + if !$".include?("irb/cmd/load") + end + (class<<@workspace.main;self;end).instance_eval { + alias_method :load, :irb_load + alias_method :require, :irb_require + } + else + (class<<@workspace.main;self;end).instance_eval { + alias_method :load, :__original__load__IRB_use_loader__ + alias_method :require, :__original__require__IRB_use_loader__ + } + end + end + print "Switch to load/require#{unless use_loader; ' non';end} trace mode.\n" if verbose? + opt + end + end +end + + diff --git a/ruby/lib/ruby/2.1.0/irb/ext/workspaces.rb b/ruby/lib/ruby/2.1.0/irb/ext/workspaces.rb new file mode 100644 index 0000000..566feb4 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/ext/workspaces.rb @@ -0,0 +1,66 @@ +# +# push-ws.rb - +# $Release Version: 0.9.6$ +# $Revision: 38515 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +module IRB # :nodoc: + class Context + + # Size of the current WorkSpace stack + def irb_level + workspace_stack.size + end + + # WorkSpaces in the current stack + def workspaces + if defined? @workspaces + @workspaces + else + @workspaces = [] + end + end + + # Creates a new workspace with the given object or binding, and appends it + # onto the current #workspaces stack. + # + # See IRB::Context#change_workspace and IRB::WorkSpace.new for more + # information. + def push_workspace(*_main) + if _main.empty? + if workspaces.empty? + print "No other workspace\n" + return nil + end + ws = workspaces.pop + workspaces.push @workspace + @workspace = ws + return workspaces + end + + workspaces.push @workspace + @workspace = WorkSpace.new(@workspace.binding, _main[0]) + if !(class<IRB.CurrentContext.exit. + def irb_exit(ret = 0) + irb_context.exit(ret) + end + + # Displays current configuration. + # + # Modifing the configuration is achieved by sending a message to IRB.conf. + def irb_context + IRB.CurrentContext + end + + @ALIASES = [ + [:context, :irb_context, NO_OVERRIDE], + [:conf, :irb_context, NO_OVERRIDE], + [:irb_quit, :irb_exit, OVERRIDE_PRIVATE_ONLY], + [:exit, :irb_exit, OVERRIDE_PRIVATE_ONLY], + [:quit, :irb_exit, OVERRIDE_PRIVATE_ONLY], + ] + + @EXTEND_COMMANDS = [ + [:irb_current_working_workspace, :CurrentWorkingWorkspace, "irb/cmd/chws", + [:irb_print_working_workspace, OVERRIDE_ALL], + [:irb_cwws, OVERRIDE_ALL], + [:irb_pwws, OVERRIDE_ALL], +# [:irb_cww, OVERRIDE_ALL], +# [:irb_pww, OVERRIDE_ALL], + [:cwws, NO_OVERRIDE], + [:pwws, NO_OVERRIDE], +# [:cww, NO_OVERRIDE], +# [:pww, NO_OVERRIDE], + [:irb_current_working_binding, OVERRIDE_ALL], + [:irb_print_working_binding, OVERRIDE_ALL], + [:irb_cwb, OVERRIDE_ALL], + [:irb_pwb, OVERRIDE_ALL], +# [:cwb, NO_OVERRIDE], +# [:pwb, NO_OVERRIDE] + ], + [:irb_change_workspace, :ChangeWorkspace, "irb/cmd/chws", + [:irb_chws, OVERRIDE_ALL], +# [:irb_chw, OVERRIDE_ALL], + [:irb_cws, OVERRIDE_ALL], +# [:irb_cw, OVERRIDE_ALL], + [:chws, NO_OVERRIDE], +# [:chw, NO_OVERRIDE], + [:cws, NO_OVERRIDE], +# [:cw, NO_OVERRIDE], + [:irb_change_binding, OVERRIDE_ALL], + [:irb_cb, OVERRIDE_ALL], + [:cb, NO_OVERRIDE]], + + [:irb_workspaces, :Workspaces, "irb/cmd/pushws", + [:workspaces, NO_OVERRIDE], + [:irb_bindings, OVERRIDE_ALL], + [:bindings, NO_OVERRIDE]], + [:irb_push_workspace, :PushWorkspace, "irb/cmd/pushws", + [:irb_pushws, OVERRIDE_ALL], +# [:irb_pushw, OVERRIDE_ALL], + [:pushws, NO_OVERRIDE], +# [:pushw, NO_OVERRIDE], + [:irb_push_binding, OVERRIDE_ALL], + [:irb_pushb, OVERRIDE_ALL], + [:pushb, NO_OVERRIDE]], + [:irb_pop_workspace, :PopWorkspace, "irb/cmd/pushws", + [:irb_popws, OVERRIDE_ALL], +# [:irb_popw, OVERRIDE_ALL], + [:popws, NO_OVERRIDE], +# [:popw, NO_OVERRIDE], + [:irb_pop_binding, OVERRIDE_ALL], + [:irb_popb, OVERRIDE_ALL], + [:popb, NO_OVERRIDE]], + + [:irb_load, :Load, "irb/cmd/load"], + [:irb_require, :Require, "irb/cmd/load"], + [:irb_source, :Source, "irb/cmd/load", + [:source, NO_OVERRIDE]], + + [:irb, :IrbCommand, "irb/cmd/subirb"], + [:irb_jobs, :Jobs, "irb/cmd/subirb", + [:jobs, NO_OVERRIDE]], + [:irb_fg, :Foreground, "irb/cmd/subirb", + [:fg, NO_OVERRIDE]], + [:irb_kill, :Kill, "irb/cmd/subirb", + [:kill, OVERRIDE_PRIVATE_ONLY]], + + [:irb_help, :Help, "irb/cmd/help", + [:help, NO_OVERRIDE]], + + ] + + # Installs the default irb commands: + # + # +irb_current_working_workspace+:: Context#main + # +irb_change_workspace+:: Context#change_workspace + # +irb_workspaces+:: Context#workspaces + # +irb_push_workspace+:: Context#push_workspace + # +irb_pop_workspace+:: Context#pop_workspace + # +irb_load+:: #irb_load + # +irb_require+:: #irb_require + # +irb_source+:: IrbLoader#source_file + # +irb+:: IRB.irb + # +irb_jobs+:: JobManager + # +irb_fg+:: JobManager#switch + # +irb_kill+:: JobManager#kill + # +irb_help+:: IRB@Command+line+options + def self.install_extend_commands + for args in @EXTEND_COMMANDS + def_extend_command(*args) + end + end + + # Evaluate the given +cmd_name+ on the given +cmd_class+ Class. + # + # Will also define any given +aliases+ for the method. + # + # The optional +load_file+ parameter will be required within the method + # definition. + def self.def_extend_command(cmd_name, cmd_class, load_file = nil, *aliases) + case cmd_class + when Symbol + cmd_class = cmd_class.id2name + when String + when Class + cmd_class = cmd_class.name + end + + if load_file + line = __LINE__; eval %[ + def #{cmd_name}(*opts, &b) + require "#{load_file}" + arity = ExtendCommand::#{cmd_class}.instance_method(:execute).arity + args = (1..(arity < 0 ? ~arity : arity)).map {|i| "arg" + i.to_s } + args << "*opts" if arity < 0 + args << "&block" + args = args.join(", ") + line = __LINE__; eval %[ + def #{cmd_name}(\#{args}) + ExtendCommand::#{cmd_class}.execute(irb_context, \#{args}) + end + ], nil, __FILE__, line + send :#{cmd_name}, *opts, &b + end + ], nil, __FILE__, line + else + line = __LINE__; eval %[ + def #{cmd_name}(*opts, &b) + ExtendCommand::#{cmd_class}.execute(irb_context, *opts, &b) + end + ], nil, __FILE__, line + end + + for ali, flag in aliases + @ALIASES.push [ali, cmd_name, flag] + end + end + + # Installs alias methods for the default irb commands, see + # ::install_extend_commands. + def install_alias_method(to, from, override = NO_OVERRIDE) + to = to.id2name unless to.kind_of?(String) + from = from.id2name unless from.kind_of?(String) + + if override == OVERRIDE_ALL or + (override == OVERRIDE_PRIVATE_ONLY) && !respond_to?(to) or + (override == NO_OVERRIDE) && !respond_to?(to, true) + target = self + (class << self; self; end).instance_eval{ + if target.respond_to?(to, true) && + !target.respond_to?(EXCB.irb_original_method_name(to), true) + alias_method(EXCB.irb_original_method_name(to), to) + end + alias_method to, from + } + else + print "irb: warn: can't alias #{to} from #{from}.\n" + end + end + + def self.irb_original_method_name(method_name) # :nodoc: + "irb_" + method_name + "_org" + end + + # Installs alias methods for the default irb commands on the given object + # using #install_alias_method. + def self.extend_object(obj) + unless (class << obj; ancestors; end).include?(EXCB) + super + for ali, com, flg in @ALIASES + obj.install_alias_method(ali, com, flg) + end + end + end + + install_extend_commands + end + + # Extends methods for the Context module + module ContextExtender + CE = ContextExtender # :nodoc: + + @EXTEND_COMMANDS = [ + [:eval_history=, "irb/ext/history.rb"], + [:use_tracer=, "irb/ext/tracer.rb"], + [:math_mode=, "irb/ext/math-mode.rb"], + [:use_loader=, "irb/ext/use-loader.rb"], + [:save_history=, "irb/ext/save-history.rb"], + ] + + # Installs the default context extensions as irb commands: + # + # Context#eval_history=:: +irb/ext/history.rb+ + # Context#use_tracer=:: +irb/ext/tracer.rb+ + # Context#math_mode=:: +irb/ext/math-mode.rb+ + # Context#use_loader=:: +irb/ext/use-loader.rb+ + # Context#save_history=:: +irb/ext/save-history.rb+ + def self.install_extend_commands + for args in @EXTEND_COMMANDS + def_extend_command(*args) + end + end + + # Evaluate the given +command+ from the given +load_file+ on the Context + # module. + # + # Will also define any given +aliases+ for the method. + def self.def_extend_command(cmd_name, load_file, *aliases) + line = __LINE__; Context.module_eval %[ + def #{cmd_name}(*opts, &b) + Context.module_eval {remove_method(:#{cmd_name})} + require "#{load_file}" + send :#{cmd_name}, *opts, &b + end + for ali in aliases + alias_method ali, cmd_name + end + ], __FILE__, line + end + + CE.install_extend_commands + end + + # A convenience module for extending Ruby methods. + module MethodExtender + # Extends the given +base_method+ with a prefix call to the given + # +extend_method+. + def def_pre_proc(base_method, extend_method) + base_method = base_method.to_s + extend_method = extend_method.to_s + + alias_name = new_alias_name(base_method) + module_eval %[ + alias_method alias_name, base_method + def #{base_method}(*opts) + send :#{extend_method}, *opts + send :#{alias_name}, *opts + end + ] + end + + # Extends the given +base_method+ with a postfix call to the given + # +extend_method+. + def def_post_proc(base_method, extend_method) + base_method = base_method.to_s + extend_method = extend_method.to_s + + alias_name = new_alias_name(base_method) + module_eval %[ + alias_method alias_name, base_method + def #{base_method}(*opts) + send :#{alias_name}, *opts + send :#{extend_method}, *opts + end + ] + end + + # Returns a unique method name to use as an alias for the given +name+. + # + # Usually returns #{prefix}#{name}#{postfix}, example: + # + # new_alias_name('foo') #=> __alias_of__foo__ + # def bar; end + # new_alias_name('bar') #=> __alias_of__bar__2 + def new_alias_name(name, prefix = "__alias_of__", postfix = "__") + base_name = "#{prefix}#{name}#{postfix}" + all_methods = instance_methods(true) + private_instance_methods(true) + same_methods = all_methods.grep(/^#{Regexp.quote(base_name)}[0-9]*$/) + return base_name if same_methods.empty? + no = same_methods.size + while !same_methods.include?(alias_name = base_name + no) + no += 1 + end + alias_name + end + end +end + diff --git a/ruby/lib/ruby/2.1.0/irb/frame.rb b/ruby/lib/ruby/2.1.0/irb/frame.rb new file mode 100644 index 0000000..a7516ab --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/frame.rb @@ -0,0 +1,80 @@ +# +# frame.rb - +# $Release Version: 0.9$ +# $Revision: 38515 $ +# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd) +# +# -- +# +# +# + +require "e2mmap" + +module IRB + class Frame + extend Exception2MessageMapper + def_exception :FrameOverflow, "frame overflow" + def_exception :FrameUnderflow, "frame underflow" + + # Default number of stack frames + INIT_STACK_TIMES = 3 + # Default number of frames offset + CALL_STACK_OFFSET = 3 + + # Creates a new stack frame + def initialize + @frames = [TOPLEVEL_BINDING] * INIT_STACK_TIMES + end + + # Used by Kernel#set_trace_func to register each event in the call stack + def trace_func(event, file, line, id, binding) + case event + when 'call', 'class' + @frames.push binding + when 'return', 'end' + @frames.pop + end + end + + # Returns the +n+ number of frames on the call stack from the last frame + # initialized. + # + # Raises FrameUnderflow if there are no frames in the given stack range. + def top(n = 0) + bind = @frames[-(n + CALL_STACK_OFFSET)] + Fail FrameUnderflow unless bind + bind + end + + # Returns the +n+ number of frames on the call stack from the first frame + # initialized. + # + # Raises FrameOverflow if there are no frames in the given stack range. + def bottom(n = 0) + bind = @frames[n] + Fail FrameOverflow unless bind + bind + end + + # Convenience method for Frame#bottom + def Frame.bottom(n = 0) + @backtrace.bottom(n) + end + + # Convenience method for Frame#top + def Frame.top(n = 0) + @backtrace.top(n) + end + + # Returns the binding context of the caller from the last frame initialized + def Frame.sender + eval "self", @backtrace.top + end + + @backtrace = Frame.new + set_trace_func proc{|event, file, line, id, binding, klass| + @backtrace.trace_func(event, file, line, id, binding) + } + end +end diff --git a/ruby/lib/ruby/2.1.0/irb/help.rb b/ruby/lib/ruby/2.1.0/irb/help.rb new file mode 100644 index 0000000..9b1a40f --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/help.rb @@ -0,0 +1,36 @@ +# +# irb/help.rb - print usage module +# $Release Version: 0.9.6$ +# $Revision: 38515 $ +# by Keiju ISHITSUKA(keiju@ishitsuka.com) +# +# -- +# +# +# + +require 'irb/magic-file' + +module IRB + # Outputs the irb help message, see IRB@Command+line+options. + def IRB.print_usage + lc = IRB.conf[:LC_MESSAGES] + path = lc.find("irb/help-message") + space_line = false + IRB::MagicFile.open(path){|f| + f.each_line do |l| + if /^\s*$/ =~ l + lc.puts l unless space_line + space_line = true + next + end + space_line = false + + l.sub!(/#.*$/, "") + next if /^\s*$/ =~ l + lc.puts l + end + } + end +end + diff --git a/ruby/lib/ruby/2.1.0/irb/init.rb b/ruby/lib/ruby/2.1.0/irb/init.rb new file mode 100644 index 0000000..ca875b0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/init.rb @@ -0,0 +1,310 @@ +# +# irb/init.rb - irb initialize module +# $Release Version: 0.9.6$ +# $Revision: 38620 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +module IRB # :nodoc: + + # initialize config + def IRB.setup(ap_path) + IRB.init_config(ap_path) + IRB.init_error + IRB.parse_opts + IRB.run_config + IRB.load_modules + + unless @CONF[:PROMPT][@CONF[:PROMPT_MODE]] + IRB.fail(UndefinedPromptMode, @CONF[:PROMPT_MODE]) + end + end + + # @CONF default setting + def IRB.init_config(ap_path) + # class instance variables + @TRACER_INITIALIZED = false + + # default configurations + unless ap_path and @CONF[:AP_NAME] + ap_path = File.join(File.dirname(File.dirname(__FILE__)), "irb.rb") + end + @CONF[:AP_NAME] = File::basename(ap_path, ".rb") + + @CONF[:IRB_NAME] = "irb" + @CONF[:IRB_LIB_PATH] = File.dirname(__FILE__) + + @CONF[:RC] = true + @CONF[:LOAD_MODULES] = [] + @CONF[:IRB_RC] = nil + + @CONF[:MATH_MODE] = false + @CONF[:USE_READLINE] = false unless defined?(ReadlineInputMethod) + @CONF[:INSPECT_MODE] = true + @CONF[:USE_TRACER] = false + @CONF[:USE_LOADER] = false + @CONF[:IGNORE_SIGINT] = true + @CONF[:IGNORE_EOF] = false + @CONF[:ECHO] = nil + @CONF[:VERBOSE] = nil + + @CONF[:EVAL_HISTORY] = nil + @CONF[:SAVE_HISTORY] = nil + + @CONF[:BACK_TRACE_LIMIT] = 16 + + @CONF[:PROMPT] = { + :NULL => { + :PROMPT_I => nil, + :PROMPT_N => nil, + :PROMPT_S => nil, + :PROMPT_C => nil, + :RETURN => "%s\n" + }, + :DEFAULT => { + :PROMPT_I => "%N(%m):%03n:%i> ", + :PROMPT_N => "%N(%m):%03n:%i> ", + :PROMPT_S => "%N(%m):%03n:%i%l ", + :PROMPT_C => "%N(%m):%03n:%i* ", + :RETURN => "=> %s\n" + }, + :CLASSIC => { + :PROMPT_I => "%N(%m):%03n:%i> ", + :PROMPT_N => "%N(%m):%03n:%i> ", + :PROMPT_S => "%N(%m):%03n:%i%l ", + :PROMPT_C => "%N(%m):%03n:%i* ", + :RETURN => "%s\n" + }, + :SIMPLE => { + :PROMPT_I => ">> ", + :PROMPT_N => ">> ", + :PROMPT_S => nil, + :PROMPT_C => "?> ", + :RETURN => "=> %s\n" + }, + :INF_RUBY => { + :PROMPT_I => "%N(%m):%03n:%i> ", +# :PROMPT_N => "%N(%m):%03n:%i> ", + :PROMPT_N => nil, + :PROMPT_S => nil, + :PROMPT_C => nil, + :RETURN => "%s\n", + :AUTO_INDENT => true + }, + :XMP => { + :PROMPT_I => nil, + :PROMPT_N => nil, + :PROMPT_S => nil, + :PROMPT_C => nil, + :RETURN => " ==>%s\n" + } + } + + @CONF[:PROMPT_MODE] = (STDIN.tty? ? :DEFAULT : :NULL) + @CONF[:AUTO_INDENT] = false + + @CONF[:CONTEXT_MODE] = 3 # use binding in function on TOPLEVEL_BINDING + @CONF[:SINGLE_IRB] = false + +# @CONF[:LC_MESSAGES] = "en" + @CONF[:LC_MESSAGES] = Locale.new + + @CONF[:AT_EXIT] = [] + + @CONF[:DEBUG_LEVEL] = 0 + end + + def IRB.init_error + @CONF[:LC_MESSAGES].load("irb/error.rb") + end + + FEATURE_IOPT_CHANGE_VERSION = "1.9.0" + + # option analyzing + def IRB.parse_opts + load_path = [] + while opt = ARGV.shift + case opt + when "-f" + @CONF[:RC] = false + when "-m" + @CONF[:MATH_MODE] = true + when "-d" + $DEBUG = true + $VERBOSE = true + when "-w" + $VERBOSE = true + when /^-W(.+)?/ + opt = $1 || ARGV.shift + case opt + when "0" + $VERBOSE = nil + when "1" + $VERBOSE = false + else + $VERBOSE = true + end + when /^-r(.+)?/ + opt = $1 || ARGV.shift + @CONF[:LOAD_MODULES].push opt if opt + when /^-I(.+)?/ + opt = $1 || ARGV.shift + load_path.concat(opt.split(File::PATH_SEPARATOR)) if opt + when '-U' + set_encoding("UTF-8", "UTF-8") + when /^-E(.+)?/, /^--encoding(?:=(.+))?/ + opt = $1 || ARGV.shift + set_encoding(*opt.split(':', 2)) + when "--inspect" + if /^-/ !~ ARGV.first + @CONF[:INSPECT_MODE] = ARGV.shift + else + @CONF[:INSPECT_MODE] = true + end + when "--noinspect" + @CONF[:INSPECT_MODE] = false + when "--readline" + @CONF[:USE_READLINE] = true + when "--noreadline" + @CONF[:USE_READLINE] = false + when "--echo" + @CONF[:ECHO] = true + when "--noecho" + @CONF[:ECHO] = false + when "--verbose" + @CONF[:VERBOSE] = true + when "--noverbose" + @CONF[:VERBOSE] = false + when /^--prompt-mode(?:=(.+))?/, /^--prompt(?:=(.+))?/ + opt = $1 || ARGV.shift + prompt_mode = opt.upcase.tr("-", "_").intern + @CONF[:PROMPT_MODE] = prompt_mode + when "--noprompt" + @CONF[:PROMPT_MODE] = :NULL + when "--inf-ruby-mode" + @CONF[:PROMPT_MODE] = :INF_RUBY + when "--sample-book-mode", "--simple-prompt" + @CONF[:PROMPT_MODE] = :SIMPLE + when "--tracer" + @CONF[:USE_TRACER] = true + when /^--back-trace-limit(?:=(.+))?/ + @CONF[:BACK_TRACE_LIMIT] = ($1 || ARGV.shift).to_i + when /^--context-mode(?:=(.+))?/ + @CONF[:CONTEXT_MODE] = ($1 || ARGV.shift).to_i + when "--single-irb" + @CONF[:SINGLE_IRB] = true + when /^--irb_debug(?:=(.+))?/ + @CONF[:DEBUG_LEVEL] = ($1 || ARGV.shift).to_i + when "-v", "--version" + print IRB.version, "\n" + exit 0 + when "-h", "--help" + require "irb/help" + IRB.print_usage + exit 0 + when "--" + if opt = ARGV.shift + @CONF[:SCRIPT] = opt + $0 = opt + end + break + when /^-/ + IRB.fail UnrecognizedSwitch, opt + else + @CONF[:SCRIPT] = opt + $0 = opt + break + end + end + if RUBY_VERSION >= FEATURE_IOPT_CHANGE_VERSION + load_path.collect! do |path| + /\A\.\// =~ path ? path : File.expand_path(path) + end + end + $LOAD_PATH.unshift(*load_path) + + end + + # running config + def IRB.run_config + if @CONF[:RC] + begin + load rc_file + rescue LoadError, Errno::ENOENT + rescue # StandardError, ScriptError + print "load error: #{rc_file}\n" + print $!.class, ": ", $!, "\n" + for err in $@[0, $@.size - 2] + print "\t", err, "\n" + end + end + end + end + + IRBRC_EXT = "rc" + def IRB.rc_file(ext = IRBRC_EXT) + if !@CONF[:RC_NAME_GENERATOR] + rc_file_generators do |rcgen| + @CONF[:RC_NAME_GENERATOR] ||= rcgen + if File.exist?(rcgen.call(IRBRC_EXT)) + @CONF[:RC_NAME_GENERATOR] = rcgen + break + end + end + end + case rc_file = @CONF[:RC_NAME_GENERATOR].call(ext) + when String + return rc_file + else + IRB.fail IllegalRCNameGenerator + end + end + + # enumerate possible rc-file base name generators + def IRB.rc_file_generators + if irbrc = ENV["IRBRC"] + yield proc{|rc| rc == "rc" ? irbrc : irbrc+rc} + end + if home = ENV["HOME"] + yield proc{|rc| home+"/.irb#{rc}"} + end + home = Dir.pwd + yield proc{|rc| home+"/.irb#{rc}"} + yield proc{|rc| home+"/irb#{rc.sub(/\A_?/, '.')}"} + yield proc{|rc| home+"/_irb#{rc}"} + yield proc{|rc| home+"/$irb#{rc}"} + end + + # loading modules + def IRB.load_modules + for m in @CONF[:LOAD_MODULES] + begin + require m + rescue LoadError => err + warn err.backtrace[0] << ":#{err.class}: #{err}" + end + end + end + + + DefaultEncodings = Struct.new(:external, :internal) + class << IRB + private + def set_encoding(extern, intern = nil) + verbose, $VERBOSE = $VERBOSE, nil + Encoding.default_external = extern unless extern.nil? || extern.empty? + Encoding.default_internal = intern unless intern.nil? || intern.empty? + @CONF[:ENCODINGS] = IRB::DefaultEncodings.new(extern, intern) + [$stdin, $stdout, $stderr].each do |io| + io.set_encoding(extern, intern) + end + @CONF[:LC_MESSAGES].instance_variable_set(:@encoding, extern) + ensure + $VERBOSE = verbose + end + end +end diff --git a/ruby/lib/ruby/2.1.0/irb/input-method.rb b/ruby/lib/ruby/2.1.0/irb/input-method.rb new file mode 100644 index 0000000..24deb27 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/input-method.rb @@ -0,0 +1,193 @@ +# +# irb/input-method.rb - input methods used irb +# $Release Version: 0.9.6$ +# $Revision: 38544 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require 'irb/src_encoding' +require 'irb/magic-file' + +module IRB + STDIN_FILE_NAME = "(line)" # :nodoc: + class InputMethod + @RCS_ID='-$Id: input-method.rb 38544 2012-12-21 17:29:18Z zzak $-' + + # Creates a new input method object + def initialize(file = STDIN_FILE_NAME) + @file_name = file + end + # The file name of this input method, usually given during initialization. + attr_reader :file_name + + # The irb prompt associated with this input method + attr_accessor :prompt + + # Reads the next line from this input method. + # + # See IO#gets for more information. + def gets + IRB.fail NotImplementedError, "gets" + end + public :gets + + # Whether this input method is still readable when there is no more data to + # read. + # + # See IO#eof for more information. + def readable_after_eof? + false + end + end + + class StdioInputMethod < InputMethod + # Creates a new input method object + def initialize + super + @line_no = 0 + @line = [] + @stdin = IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-") + @stdout = IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-") + end + + # Reads the next line from this input method. + # + # See IO#gets for more information. + def gets + print @prompt + line = @stdin.gets + @line[@line_no += 1] = line + end + + # Whether the end of this input method has been reached, returns +true+ if + # there is no more data to read. + # + # See IO#eof? for more information. + def eof? + @stdin.eof? + end + + # Whether this input method is still readable when there is no more data to + # read. + # + # See IO#eof for more information. + def readable_after_eof? + true + end + + # Returns the current line number for #io. + # + # #line counts the number of times #gets is called. + # + # See IO#lineno for more information. + def line(line_no) + @line[line_no] + end + + # The external encoding for standard input. + def encoding + @stdin.external_encoding + end + end + + # Use a File for IO with irb, see InputMethod + class FileInputMethod < InputMethod + # Creates a new input method object + def initialize(file) + super + @io = IRB::MagicFile.open(file) + end + # The file name of this input method, usually given during initialization. + attr_reader :file_name + + # Whether the end of this input method has been reached, returns +true+ if + # there is no more data to read. + # + # See IO#eof? for more information. + def eof? + @io.eof? + end + + # Reads the next line from this input method. + # + # See IO#gets for more information. + def gets + print @prompt + l = @io.gets +# print @prompt, l + l + end + + # The external encoding for standard input. + def encoding + @io.external_encoding + end + end + + begin + require "readline" + class ReadlineInputMethod < InputMethod + include Readline + # Creates a new input method object using Readline + def initialize + super + + @line_no = 0 + @line = [] + @eof = false + + @stdin = IO.open(STDIN.to_i, :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-") + @stdout = IO.open(STDOUT.to_i, 'w', :external_encoding => IRB.conf[:LC_MESSAGES].encoding, :internal_encoding => "-") + end + + # Reads the next line from this input method. + # + # See IO#gets for more information. + def gets + Readline.input = @stdin + Readline.output = @stdout + if l = readline(@prompt, false) + HISTORY.push(l) if !l.empty? + @line[@line_no += 1] = l + "\n" + else + @eof = true + l + end + end + + # Whether the end of this input method has been reached, returns +true+ + # if there is no more data to read. + # + # See IO#eof? for more information. + def eof? + @eof + end + + # Whether this input method is still readable when there is no more data to + # read. + # + # See IO#eof for more information. + def readable_after_eof? + true + end + + # Returns the current line number for #io. + # + # #line counts the number of times #gets is called. + # + # See IO#lineno for more information. + def line(line_no) + @line[line_no] + end + + # The external encoding for standard input. + def encoding + @stdin.external_encoding + end + end + rescue LoadError + end +end diff --git a/ruby/lib/ruby/2.1.0/irb/inspector.rb b/ruby/lib/ruby/2.1.0/irb/inspector.rb new file mode 100644 index 0000000..cdd90d4 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/inspector.rb @@ -0,0 +1,145 @@ +# +# irb/inspector.rb - inspect methods +# $Release Version: 0.9.6$ +# $Revision: 1.19 $ +# $Date: 2002/06/11 07:51:31 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +module IRB # :nodoc: + + + # Convenience method to create a new Inspector, using the given +inspect+ + # proc, and optional +init+ proc and passes them to Inspector.new + # + # irb(main):001:0> ins = IRB::Inspector(proc{ |v| "omg! #{v}" }) + # irb(main):001:0> IRB.CurrentContext.inspect_mode = ins # => omg! # + # irb(main):001:0> "what?" #=> omg! what? + # + def IRB::Inspector(inspect, init = nil) + Inspector.new(inspect, init) + end + + # An irb inspector + # + # In order to create your own custom inspector there are two things you + # should be aware of: + # + # Inspector uses #inspect_value, or +inspect_proc+, for output of return values. + # + # This also allows for an optional #init+, or +init_proc+, which is called + # when the inspector is activated. + # + # Knowing this, you can create a rudimentary inspector as follows: + # + # irb(main):001:0> ins = IRB::Inspector.new(proc{ |v| "omg! #{v}" }) + # irb(main):001:0> IRB.CurrentContext.inspect_mode = ins # => omg! # + # irb(main):001:0> "what?" #=> omg! what? + # + class Inspector + # Default inspectors available to irb, this includes: + # + # +:pp+:: Using Kernel#pretty_inspect + # +:yaml+:: Using YAML.dump + # +:marshal+:: Using Marshal.dump + INSPECTORS = {} + + # Determines the inspector to use where +inspector+ is one of the keys passed + # during inspector definition. + def self.keys_with_inspector(inspector) + INSPECTORS.select{|k,v| v == inspector}.collect{|k, v| k} + end + + # Example + # + # Inspector.def_inspector(key, init_p=nil){|v| v.inspect} + # Inspector.def_inspector([key1,..], init_p=nil){|v| v.inspect} + # Inspector.def_inspector(key, inspector) + # Inspector.def_inspector([key1,...], inspector) + def self.def_inspector(key, arg=nil, &block) + # if !block_given? + # case arg + # when nil, Proc + # inspector = IRB::Inspector(init_p) + # when Inspector + # inspector = init_p + # else + # IRB.Raise IllegalParameter, init_p + # end + # init_p = nil + # else + # inspector = IRB::Inspector(block, init_p) + # end + + if block_given? + inspector = IRB::Inspector(block, arg) + else + inspector = arg + end + + case key + when Array + for k in key + def_inspector(k, inspector) + end + when Symbol + INSPECTORS[key] = inspector + INSPECTORS[key.to_s] = inspector + when String + INSPECTORS[key] = inspector + INSPECTORS[key.intern] = inspector + else + INSPECTORS[key] = inspector + end + end + + # Creates a new inspector object, using the given +inspect_proc+ when + # output return values in irb. + def initialize(inspect_proc, init_proc = nil) + @init = init_proc + @inspect = inspect_proc + end + + # Proc to call when the inspector is activated, good for requiring + # dependent libraries. + def init + @init.call if @init + end + + # Proc to call when the input is evaluated and output in irb. + def inspect_value(v) + @inspect.call(v) + end + end + + Inspector.def_inspector([false, :to_s, :raw]){|v| v.to_s} + Inspector.def_inspector([true, :p, :inspect]){|v| + begin + v.inspect + rescue NoMethodError + puts "(Object doesn't support #inspect)" + end + } + Inspector.def_inspector([:pp, :pretty_inspect], proc{require "pp"}){|v| v.pretty_inspect.chomp} + Inspector.def_inspector([:yaml, :YAML], proc{require "yaml"}){|v| + begin + YAML.dump(v) + rescue + puts "(can't dump yaml. use inspect)" + v.inspect + end + } + + Inspector.def_inspector([:marshal, :Marshal, :MARSHAL, Marshal]){|v| + Marshal.dump(v) + } +end + + + + + diff --git a/ruby/lib/ruby/2.1.0/irb/lc/error.rb b/ruby/lib/ruby/2.1.0/irb/lc/error.rb new file mode 100644 index 0000000..a0d0dc8 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/lc/error.rb @@ -0,0 +1,31 @@ +# +# irb/lc/error.rb - +# $Release Version: 0.9.6$ +# $Revision: 38600 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "e2mmap" + +# :stopdoc: +module IRB + + # exceptions + extend Exception2MessageMapper + def_exception :UnrecognizedSwitch, "Unrecognized switch: %s" + def_exception :NotImplementedError, "Need to define `%s'" + def_exception :CantReturnToNormalMode, "Can't return to normal mode." + def_exception :IllegalParameter, "Invalid parameter(%s)." + def_exception :IrbAlreadyDead, "Irb is already dead." + def_exception :IrbSwitchedToCurrentThread, "Switched to current thread." + def_exception :NoSuchJob, "No such job(%s)." + def_exception :CantShiftToMultiIrbMode, "Can't shift to multi irb mode." + def_exception :CantChangeBinding, "Can't change binding to (%s)." + def_exception :UndefinedPromptMode, "Undefined prompt mode(%s)." + def_exception :IllegalRCGenerator, 'Define illegal RC_NAME_GENERATOR.' + +end +# :startdoc: diff --git a/ruby/lib/ruby/2.1.0/irb/lc/help-message b/ruby/lib/ruby/2.1.0/irb/lc/help-message new file mode 100644 index 0000000..7ac1b2a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/lc/help-message @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# +# irb/lc/help-message.rb - +# $Release Version: 0.9.6$ +# $Revision: 41028 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +Usage: irb.rb [options] [programfile] [arguments] + -f Suppress read of ~/.irbrc + -m Bc mode (load mathn, fraction or matrix are available) + -d Set $DEBUG to true (same as `ruby -d') + -r load-module Same as `ruby -r' + -I path Specify $LOAD_PATH directory + -U Same as `ruby -U` + -E enc Same as `ruby -E` + -w Same as `ruby -w` + -W[level=2] Same as `ruby -W` + --context-mode n Set n[0-3] to method to create Binding Object, + when new workspace was created + --echo Show result(default) + --noecho Don't show result + --inspect Use `inspect' for output (default except for bc mode) + --noinspect Don't use inspect for output + --readline Use Readline extension module + --noreadline Don't use Readline extension module + --prompt prompt-mode/--prompt-mode prompt-mode + Switch prompt mode. Pre-defined prompt modes are + `default', `simple', `xmp' and `inf-ruby' + --inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs. + Suppresses --readline. + --sample-book-mode/--simple-prompt + Simple prompt mode + --noprompt No prompt mode + --single-irb Share self with sub-irb. + --tracer Display trace for each execution of commands. + --back-trace-limit n + Display backtrace top n and tail n. The default + value is 16. + --irb_debug n Set internal debug level to n (not for popular use) + --verbose Show details + --noverbose Don't show details + -v, --version Print the version of irb + -h, --help Print help + -- Separate options of irb from the list of command-line args + +# vim:fileencoding=utf-8 diff --git a/ruby/lib/ruby/2.1.0/irb/lc/ja/encoding_aliases.rb b/ruby/lib/ruby/2.1.0/irb/lc/ja/encoding_aliases.rb new file mode 100644 index 0000000..5bef32e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/lc/ja/encoding_aliases.rb @@ -0,0 +1,10 @@ +# :stopdoc: +module IRB + class Locale + @@legacy_encoding_alias_map = { + 'ujis' => Encoding::EUC_JP, + 'euc' => Encoding::EUC_JP + }.freeze + end +end +# :startdoc: diff --git a/ruby/lib/ruby/2.1.0/irb/lc/ja/error.rb b/ruby/lib/ruby/2.1.0/irb/lc/ja/error.rb new file mode 100644 index 0000000..6532777 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/lc/ja/error.rb @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# irb/lc/ja/error.rb - +# $Release Version: 0.9.6$ +# $Revision: 38600 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +require "e2mmap" + +# :stopdoc: +module IRB + # exceptions + extend Exception2MessageMapper + def_exception :UnrecognizedSwitch, 'スイッãƒ(%s)ãŒåˆ†ã‚Šã¾ã›ã‚“' + def_exception :NotImplementedError, '`%s\'ã®å®šç¾©ãŒå¿…è¦ã§ã™' + def_exception :CantReturnToNormalMode, 'Normalãƒ¢ãƒ¼ãƒ‰ã«æˆ»ã‚Œã¾ã›ã‚“.' + def_exception :IllegalParameter, 'パラメータ(%s)ãŒé–“é•ã£ã¦ã„ã¾ã™.' + def_exception :IrbAlreadyDead, 'Irbã¯æ—¢ã«æ­»ã‚“ã§ã„ã¾ã™.' + def_exception :IrbSwitchedToCurrentThread, 'カレントスレッドã«åˆ‡ã‚Šæ›¿ã‚りã¾ã—ãŸ.' + def_exception :NoSuchJob, 'ãã®ã‚ˆã†ãªã‚¸ãƒ§ãƒ–(%s)ã¯ã‚りã¾ã›ã‚“.' + def_exception :CantShiftToMultiIrbMode, 'multi-irb modeã«ç§»ã‚Œã¾ã›ã‚“.' + def_exception :CantChangeBinding, 'ãƒã‚¤ãƒ³ãƒ‡ã‚£ãƒ³ã‚°(%s)ã«å¤‰æ›´ã§ãã¾ã›ã‚“.' + def_exception :UndefinedPromptMode, 'プロンプトモード(%s)ã¯å®šç¾©ã•れã¦ã„ã¾ã›ã‚“.' + def_exception :IllegalRCNameGenerator, 'RC_NAME_GENERATORãŒæ­£ã—ã定義ã•れã¦ã„ã¾ã›ã‚“.' +end +# :startdoc: +# vim:fileencoding=utf-8 diff --git a/ruby/lib/ruby/2.1.0/irb/lc/ja/help-message b/ruby/lib/ruby/2.1.0/irb/lc/ja/help-message new file mode 100644 index 0000000..1483892 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/lc/ja/help-message @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# irb/lc/ja/help-message.rb - +# $Release Version: 0.9.6$ +# $Revision: 41071 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +Usage: irb.rb [options] [programfile] [arguments] + -f ~/.irbrc を読ã¿è¾¼ã¾ãªã„. + -m bcモード(分数, 行列ã®è¨ˆç®—ãŒã§ãã‚‹) + -d $DEBUG ã‚’trueã«ã™ã‚‹(ruby -d ã¨åŒã˜) + -r load-module ruby -r ã¨åŒã˜. + -I path $LOAD_PATH ã« path を追加ã™ã‚‹. + -U ruby -U ã¨åŒã˜. + -E enc ruby -E ã¨åŒã˜. + -w ruby -w ã¨åŒã˜. + -W[level=2] ruby -W ã¨åŒã˜. + --context-mode n æ–°ã—ã„ワークスペースを作æˆã—ãŸæ™‚ã«é–¢é€£ã™ã‚‹ Binding + オブジェクトã®ä½œæˆæ–¹æ³•ã‚’ 0 ã‹ã‚‰ 3 ã®ã„ãšã‚Œã‹ã«è¨­å®šã™ã‚‹. + --echo å®Ÿè¡Œçµæžœã‚’表示ã™ã‚‹(デフォルト). + --noecho å®Ÿè¡Œçµæžœã‚’表示ã—ãªã„. + --inspect çµæžœå‡ºåŠ›ã«inspectを用ã„ã‚‹(bcモード以外ã¯ãƒ‡ãƒ•ォルト). + --noinspect çµæžœå‡ºåŠ›ã«inspectを用ã„ãªã„. + --readline readlineライブラリを利用ã™ã‚‹. + --noreadline readlineライブラリを利用ã—ãªã„. + --prompt prompt-mode/--prompt-mode prompt-mode + プロンプトモードを切替ãˆã¾ã™. ç¾åœ¨å®šç¾©ã•れã¦ã„るプ + ロンプトモードã¯, default, simple, xmp, inf-ruby㌠+ 用æ„ã•れã¦ã„ã¾ã™. + --inf-ruby-mode emacsã®inf-ruby-mode用ã®ãƒ—ロンプト表示を行ãªã†. 特 + ã«æŒ‡å®šãŒãªã„é™ã‚Š, readlineライブラリã¯ä½¿ã‚ãªããªã‚‹. + --sample-book-mode/--simple-prompt + éžå¸¸ã«ã‚·ãƒ³ãƒ—ルãªãƒ—ロンプトを用ã„るモードã§ã™. + --noprompt プロンプト表示を行ãªã‚ãªã„. + --single-irb irb 中㧠self を実行ã—ã¦å¾—られるオブジェクトをサ + ブ irb ã¨å…±æœ‰ã™ã‚‹. + --tracer コマンド実行時ã«ãƒˆãƒ¬ãƒ¼ã‚¹ã‚’行ãªã†. + --back-trace-limit n + ãƒãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹è¡¨ç¤ºã‚’ãƒãƒƒã‚¯ãƒˆãƒ¬ãƒ¼ã‚¹ã®é ­ã‹ã‚‰ n, 後゠+ ã‹ã‚‰nã ã‘行ãªã†. デフォルトã¯16 + + --irb_debug n irbã®ãƒ‡ãƒãƒƒã‚°ãƒ¬ãƒ™ãƒ«ã‚’nã«è¨­å®šã™ã‚‹(éžæŽ¨å¥¨). + + --verbose 詳細ãªãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’出力ã™ã‚‹. + --noverbose 詳細ãªãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚’出力ã—ãªã„(デフォルト). + -v, --version irbã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’表示ã™ã‚‹. + -h, --help irb ã®ãƒ˜ãƒ«ãƒ—を表示ã™ã‚‹. + -- 以é™ã®ã‚³ãƒžãƒ³ãƒ‰ãƒ©ã‚¤ãƒ³å¼•数をオプションã¨ã—ã¦æ‰±ã‚ãªã„. + +# vim:fileencoding=utf-8 diff --git a/ruby/lib/ruby/2.1.0/irb/locale.rb b/ruby/lib/ruby/2.1.0/irb/locale.rb new file mode 100644 index 0000000..a92043b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/locale.rb @@ -0,0 +1,182 @@ +# +# irb/locale.rb - internationalization module +# $Release Version: 0.9.6$ +# $Revision: 38358 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +module IRB # :nodoc: + class Locale + @RCS_ID='-$Id: locale.rb 38358 2012-12-13 05:22:30Z zzak $-' + + LOCALE_NAME_RE = %r[ + (?[[:alpha:]]{2,3}) + (?:_ (?[[:alpha:]]{2,3}) )? + (?:\. (?[^@]+) )? + (?:@ (?.*) )? + ]x + LOCALE_DIR = "/lc/" + + @@legacy_encoding_alias_map = {}.freeze + + def initialize(locale = nil) + @lang = @territory = @encoding_name = @modifier = nil + @locale = locale || ENV["IRB_LANG"] || ENV["LC_MESSAGES"] || ENV["LC_ALL"] || ENV["LANG"] || "C" + if m = LOCALE_NAME_RE.match(@locale) + @lang, @territory, @encoding_name, @modifier = m[:language], m[:territory], m[:codeset], m[:modifier] + + if @encoding_name + begin load 'irb/encoding_aliases.rb'; rescue LoadError; end + if @encoding = @@legacy_encoding_alias_map[@encoding_name] + warn "%s is obsolete. use %s" % ["#{@lang}_#{@territory}.#{@encoding_name}", "#{@lang}_#{@territory}.#{@encoding.name}"] + end + @encoding = Encoding.find(@encoding_name) rescue nil + end + end + @encoding ||= (Encoding.find('locale') rescue Encoding::ASCII_8BIT) + end + + attr_reader :lang, :territory, :encoding, :modifieer + + def String(mes) + mes = super(mes) + if @encoding + mes.encode(@encoding, undef: :replace) + else + mes + end + end + + def format(*opts) + String(super(*opts)) + end + + def gets(*rs) + String(super(*rs)) + end + + def readline(*rs) + String(super(*rs)) + end + + def print(*opts) + ary = opts.collect{|opt| String(opt)} + super(*ary) + end + + def printf(*opts) + s = format(*opts) + print s + end + + def puts(*opts) + ary = opts.collect{|opt| String(opt)} + super(*ary) + end + + def require(file, priv = nil) + rex = Regexp.new("lc/#{Regexp.quote(file)}\.(so|o|sl|rb)?") + return false if $".find{|f| f =~ rex} + + case file + when /\.rb$/ + begin + load(file, priv) + $".push file + return true + rescue LoadError + end + when /\.(so|o|sl)$/ + return super + end + + begin + load(f = file + ".rb") + $".push f #" + return true + rescue LoadError + return ruby_require(file) + end + end + + alias toplevel_load load + + def load(file, priv=nil) + found = find(file) + if found + return real_load(found, priv) + else + raise LoadError, "No such file to load -- #{file}" + end + end + + def find(file , paths = $:) + dir = File.dirname(file) + dir = "" if dir == "." + base = File.basename(file) + + if dir.start_with?('/') + return each_localized_path(dir, base).find{|full_path| File.readable? full_path} + else + return search_file(paths, dir, base) + end + end + + private + def real_load(path, priv) + src = MagicFile.open(path){|f| f.read} + if priv + eval("self", TOPLEVEL_BINDING).extend(Module.new {eval(src, nil, path)}) + else + eval(src, TOPLEVEL_BINDING, path) + end + end + + # @param paths load paths in which IRB find a localized file. + # @param dir directory + # @param file basename to be localized + # + # typically, for the parameters and a in paths, it searches + # /// + def search_file(lib_paths, dir, file) + each_localized_path(dir, file) do |lc_path| + lib_paths.each do |libpath| + full_path = File.join(libpath, lc_path) + return full_path if File.readable?(full_path) + end + redo if defined?(Gem) and Gem.try_activate(lc_path) + end + nil + end + + def each_localized_path(dir, file) + return enum_for(:each_localized_path) unless block_given? + each_sublocale do |lc| + yield lc.nil? ? File.join(dir, LOCALE_DIR, file) : File.join(dir, LOCALE_DIR, lc, file) + end + end + + def each_sublocale + if @lang + if @territory + if @encoding_name + yield "#{@lang}_#{@territory}.#{@encoding_name}@#{@modifier}" if @modifier + yield "#{@lang}_#{@territory}.#{@encoding_name}" + end + yield "#{@lang}_#{@territory}@#{@modifier}" if @modifier + yield "#{@lang}_#{@territory}" + end + if @encoding_name + yield "#{@lang}.#{@encoding_name}@#{@modifier}" if @modifier + yield "#{@lang}.#{@encoding_name}" + end + yield "#{@lang}@#{@modifier}" if @modifier + yield "#{@lang}" + end + yield nil + end + end +end diff --git a/ruby/lib/ruby/2.1.0/irb/magic-file.rb b/ruby/lib/ruby/2.1.0/irb/magic-file.rb new file mode 100644 index 0000000..339ed60 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/magic-file.rb @@ -0,0 +1,37 @@ +module IRB + class << (MagicFile = Object.new) + # see parser_magic_comment in parse.y + ENCODING_SPEC_RE = %r"coding\s*[=:]\s*([[:alnum:]\-_]+)" + + def open(path) + io = File.open(path, 'rb') + line = io.gets + line = io.gets if line[0,2] == "#!" + encoding = detect_encoding(line) + internal_encoding = encoding + encoding ||= default_src_encoding + io.rewind + io.set_encoding(encoding, internal_encoding) + + if block_given? + begin + return (yield io) + ensure + io.close + end + else + return io + end + end + + private + def detect_encoding(line) + return unless line[0] == ?# + line = line[1..-1] + line = $1 if line[/-\*-\s*(.*?)\s*-*-$/] + return nil unless ENCODING_SPEC_RE =~ line + encoding = $1 + return encoding.sub(/-(?:mac|dos|unix)/i, '') + end + end +end diff --git a/ruby/lib/ruby/2.1.0/irb/notifier.rb b/ruby/lib/ruby/2.1.0/irb/notifier.rb new file mode 100644 index 0000000..ba3e9e2 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/notifier.rb @@ -0,0 +1,231 @@ +# +# notifier.rb - output methods used by irb +# $Release Version: 0.9.6$ +# $Revision: 43881 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +require "e2mmap" +require "irb/output-method" + +module IRB + # An output formatter used internally by the lexer. + module Notifier + extend Exception2MessageMapper + def_exception :ErrUndefinedNotifier, + "undefined notifier level: %d is specified" + def_exception :ErrUnrecognizedLevel, + "unrecognized notifier level: %s is specified" + + # Define a new Notifier output source, returning a new CompositeNotifier + # with the given +prefix+ and +output_method+. + # + # The optional +prefix+ will be appended to all objects being inspected + # during output, using the given +output_method+ as the output source. If + # no +output_method+ is given, StdioOutputMethod will be used, and all + # expressions will be sent directly to STDOUT without any additional + # formatting. + def def_notifier(prefix = "", output_method = StdioOutputMethod.new) + CompositeNotifier.new(prefix, output_method) + end + module_function :def_notifier + + # An abstract class, or superclass, for CompositeNotifier and + # LeveledNotifier to inherit. It provides several wrapper methods for the + # OutputMethod object used by the Notifier. + class AbstractNotifier + # Creates a new Notifier object + def initialize(prefix, base_notifier) + @prefix = prefix + @base_notifier = base_notifier + end + + # The +prefix+ for this Notifier, which is appended to all objects being + # inspected during output. + attr_reader :prefix + + # A wrapper method used to determine whether notifications are enabled. + # + # Defaults to +true+. + def notify? + true + end + + # See OutputMethod#print for more detail. + def print(*opts) + @base_notifier.print prefix, *opts if notify? + end + + # See OutputMethod#printn for more detail. + def printn(*opts) + @base_notifier.printn prefix, *opts if notify? + end + + # See OutputMethod#printf for more detail. + def printf(format, *opts) + @base_notifier.printf(prefix + format, *opts) if notify? + end + + # See OutputMethod#puts for more detail. + def puts(*objs) + if notify? + @base_notifier.puts(*objs.collect{|obj| prefix + obj.to_s}) + end + end + + # Same as #ppx, except it uses the #prefix given during object + # initialization. + # See OutputMethod#ppx for more detail. + def pp(*objs) + if notify? + @base_notifier.ppx @prefix, *objs + end + end + + # Same as #pp, except it concatenates the given +prefix+ with the #prefix + # given during object initialization. + # + # See OutputMethod#ppx for more detail. + def ppx(prefix, *objs) + if notify? + @base_notifier.ppx @prefix+prefix, *objs + end + end + + # Execute the given block if notifications are enabled. + def exec_if + yield(@base_notifier) if notify? + end + end + + # A class that can be used to create a group of notifier objects with the + # intent of representing a leveled notification system for irb. + # + # This class will allow you to generate other notifiers, and assign them + # the appropriate level for output. + # + # The Notifier class provides a class-method Notifier.def_notifier to + # create a new composite notifier. Using the first composite notifier + # object you create, sibling notifiers can be initialized with + # #def_notifier. + class CompositeNotifier(other) + @level <=> other.level + end + + # Whether to output messages to the output method, depending on the level + # of this notifier object. + def notify? + @base_notifier.level >= self + end + end + + # NoMsgNotifier is a LeveledNotifier that's used as the default notifier + # when creating a new CompositeNotifier. + # + # This notifier is used as the +zero+ index, or level +0+, for + # CompositeNotifier#notifiers, and will not output messages of any sort. + class NoMsgNotifier [#0- +] + # (\*|\*[1-9][0-9]*\$|[1-9][0-9]*) + # .(\*|\*[1-9][0-9]*\$|[1-9][0-9]*|)? + # #(hh|h|l|ll|L|q|j|z|t) + # [diouxXeEfgGcsb%] + def parse_printf_format(format, opts) + return format, opts if $1.size % 2 == 1 + end + + # Calls #print on each element in the given +objs+, followed by a newline + # character. + def puts(*objs) + for obj in objs + print(*obj) + print "\n" + end + end + + # Prints the given +objs+ calling Object#inspect on each. + # + # See #puts for more detail. + def pp(*objs) + puts(*objs.collect{|obj| obj.inspect}) + end + + # Prints the given +objs+ calling Object#inspect on each and appending the + # given +prefix+. + # + # See #puts for more detail. + def ppx(prefix, *objs) + puts(*objs.collect{|obj| prefix+obj.inspect}) + end + + end + + # A standard output printer + class StdioOutputMethod 0 + end + end + @debug_level = 0 + + def initialize + lex_init + set_input(STDIN) + + @seek = 0 + @exp_line_no = @line_no = 1 + @base_char_no = 0 + @char_no = 0 + @rests = [] + @readed = [] + @here_readed = [] + + @indent = 0 + @indent_stack = [] + @lex_state = EXPR_BEG + @space_seen = false + @here_header = false + @post_symbeg = false + + @continue = false + @line = "" + + @skip_space = false + @readed_auto_clean_up = false + @exception_on_syntax_error = true + + @prompt = nil + end + + attr_accessor :skip_space + attr_accessor :readed_auto_clean_up + attr_accessor :exception_on_syntax_error + + attr_reader :seek + attr_reader :char_no + attr_reader :line_no + attr_reader :indent + + # io functions + def set_input(io, p = nil, &block) + @io = io + if p.respond_to?(:call) + @input = p + elsif block_given? + @input = block + else + @input = Proc.new{@io.gets} + end + end + + def get_readed + if idx = @readed.rindex("\n") + @base_char_no = @readed.size - (idx + 1) + else + @base_char_no += @readed.size + end + + readed = @readed.join("") + @readed = [] + readed + end + + def getc + while @rests.empty? +# return nil unless buf_input + @rests.push nil unless buf_input + end + c = @rests.shift + if @here_header + @here_readed.push c + else + @readed.push c + end + @seek += 1 + if c == "\n" + @line_no += 1 + @char_no = 0 + else + @char_no += 1 + end + c + end + + def gets + l = "" + while c = getc + l.concat(c) + break if c == "\n" + end + return nil if l == "" and c.nil? + l + end + + def eof? + @io.eof? + end + + def getc_of_rests + if @rests.empty? + nil + else + getc + end + end + + def ungetc(c = nil) + if @here_readed.empty? + c2 = @readed.pop + else + c2 = @here_readed.pop + end + c = c2 unless c + @rests.unshift c #c = + @seek -= 1 + if c == "\n" + @line_no -= 1 + if idx = @readed.rindex("\n") + @char_no = idx + 1 + else + @char_no = @base_char_no + @readed.size + end + else + @char_no -= 1 + end + end + + def peek_equal?(str) + chrs = str.split(//) + until @rests.size >= chrs.size + return false unless buf_input + end + @rests[0, chrs.size] == chrs + end + + def peek_match?(regexp) + while @rests.empty? + return false unless buf_input + end + regexp =~ @rests.join("") + end + + def peek(i = 0) + while @rests.size <= i + return nil unless buf_input + end + @rests[i] + end + + def buf_input + prompt + line = @input.call + return nil unless line + @rests.concat line.chars.to_a + true + end + private :buf_input + + def set_prompt(p = nil, &block) + p = block if block_given? + if p.respond_to?(:call) + @prompt = p + else + @prompt = Proc.new{print p} + end + end + + def prompt + if @prompt + @prompt.call(@ltype, @indent, @continue, @line_no) + end + end + + def initialize_input + @ltype = nil + @quoted = nil + @indent = 0 + @indent_stack = [] + @lex_state = EXPR_BEG + @space_seen = false + @here_header = false + + @continue = false + @post_symbeg = false + + prompt + + @line = "" + @exp_line_no = @line_no + end + + def each_top_level_statement + initialize_input + catch(:TERM_INPUT) do + loop do + begin + @continue = false + prompt + unless l = lex + throw :TERM_INPUT if @line == '' + else + @line.concat l + if @ltype or @continue or @indent > 0 + next + end + end + if @line != "\n" + @line.force_encoding(@io.encoding) + yield @line, @exp_line_no + end + break unless l + @line = '' + @exp_line_no = @line_no + + @indent = 0 + @indent_stack = [] + prompt + rescue TerminateLineInput + initialize_input + prompt + get_readed + end + end + end + end + + def lex + until (((tk = token).kind_of?(TkNL) || tk.kind_of?(TkEND_OF_SCRIPT)) && + !@continue or + tk.nil?) + #p tk + #p @lex_state + #p self + end + line = get_readed + # print self.inspect + if line == "" and tk.kind_of?(TkEND_OF_SCRIPT) || tk.nil? + nil + else + line + end + end + + def token + # require "tracer" + # Tracer.on + @prev_seek = @seek + @prev_line_no = @line_no + @prev_char_no = @char_no + begin + begin + tk = @OP.match(self) + @space_seen = tk.kind_of?(TkSPACE) + @lex_state = EXPR_END if @post_symbeg && tk.kind_of?(TkOp) + @post_symbeg = tk.kind_of?(TkSYMBEG) + rescue SyntaxError + raise if @exception_on_syntax_error + tk = TkError.new(@seek, @line_no, @char_no) + end + end while @skip_space and tk.kind_of?(TkSPACE) + if @readed_auto_clean_up + get_readed + end + # Tracer.off + tk + end + + ENINDENT_CLAUSE = [ + "case", "class", "def", "do", "for", "if", + "module", "unless", "until", "while", "begin" #, "when" + ] + DEINDENT_CLAUSE = ["end" #, "when" + ] + + PERCENT_LTYPE = { + "q" => "\'", + "Q" => "\"", + "x" => "\`", + "r" => "/", + "w" => "]", + "W" => "]", + "i" => "]", + "I" => "]", + "s" => ":" + } + + PERCENT_PAREN = { + "{" => "}", + "[" => "]", + "<" => ">", + "(" => ")" + } + + Ltype2Token = { + "\'" => TkSTRING, + "\"" => TkSTRING, + "\`" => TkXSTRING, + "/" => TkREGEXP, + "]" => TkDSTRING, + ":" => TkSYMBOL + } + DLtype2Token = { + "\"" => TkDSTRING, + "\`" => TkDXSTRING, + "/" => TkDREGEXP, + } + + def lex_init() + @OP = IRB::SLex.new + @OP.def_rules("\0", "\004", "\032") do |op, io| + Token(TkEND_OF_SCRIPT) + end + + @OP.def_rules(" ", "\t", "\f", "\r", "\13") do |op, io| + @space_seen = true + while getc =~ /[ \t\f\r\13]/; end + ungetc + Token(TkSPACE) + end + + @OP.def_rule("#") do |op, io| + identify_comment + end + + @OP.def_rule("=begin", + proc{|op, io| @prev_char_no == 0 && peek(0) =~ /\s/}) do + |op, io| + @ltype = "=" + until getc == "\n"; end + until peek_equal?("=end") && peek(4) =~ /\s/ + until getc == "\n"; end + end + gets + @ltype = nil + Token(TkRD_COMMENT) + end + + @OP.def_rule("\n") do |op, io| + print "\\n\n" if RubyLex.debug? + case @lex_state + when EXPR_BEG, EXPR_FNAME, EXPR_DOT + @continue = true + else + @continue = false + @lex_state = EXPR_BEG + until (@indent_stack.empty? || + [TkLPAREN, TkLBRACK, TkLBRACE, + TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last)) + @indent_stack.pop + end + end + @here_header = false + @here_readed = [] + Token(TkNL) + end + + @OP.def_rules("*", "**", + "=", "==", "===", + "=~", "<=>", + "<", "<=", + ">", ">=", ">>", + "!", "!=", "!~") do + |op, io| + case @lex_state + when EXPR_FNAME, EXPR_DOT + @lex_state = EXPR_ARG + else + @lex_state = EXPR_BEG + end + Token(op) + end + + @OP.def_rules("<<") do + |op, io| + tk = nil + if @lex_state != EXPR_END && @lex_state != EXPR_CLASS && + (@lex_state != EXPR_ARG || @space_seen) + c = peek(0) + if /\S/ =~ c && (/["'`]/ =~ c || /\w/ =~ c || c == "-") + tk = identify_here_document + end + end + unless tk + tk = Token(op) + case @lex_state + when EXPR_FNAME, EXPR_DOT + @lex_state = EXPR_ARG + else + @lex_state = EXPR_BEG + end + end + tk + end + + @OP.def_rules("'", '"') do + |op, io| + identify_string(op) + end + + @OP.def_rules("`") do + |op, io| + if @lex_state == EXPR_FNAME + @lex_state = EXPR_END + Token(op) + else + identify_string(op) + end + end + + @OP.def_rules('?') do + |op, io| + if @lex_state == EXPR_END + @lex_state = EXPR_BEG + Token(TkQUESTION) + else + ch = getc + if @lex_state == EXPR_ARG && ch =~ /\s/ + ungetc + @lex_state = EXPR_BEG; + Token(TkQUESTION) + else + if (ch == '\\') + read_escape + end + @lex_state = EXPR_END + Token(TkINTEGER) + end + end + end + + @OP.def_rules("&", "&&", "|", "||") do + |op, io| + @lex_state = EXPR_BEG + Token(op) + end + + @OP.def_rules("+=", "-=", "*=", "**=", + "&=", "|=", "^=", "<<=", ">>=", "||=", "&&=") do + |op, io| + @lex_state = EXPR_BEG + op =~ /^(.*)=$/ + Token(TkOPASGN, $1) + end + + @OP.def_rule("+@", proc{|op, io| @lex_state == EXPR_FNAME}) do + |op, io| + @lex_state = EXPR_ARG + Token(op) + end + + @OP.def_rule("-@", proc{|op, io| @lex_state == EXPR_FNAME}) do + |op, io| + @lex_state = EXPR_ARG + Token(op) + end + + @OP.def_rules("+", "-") do + |op, io| + catch(:RET) do + if @lex_state == EXPR_ARG + if @space_seen and peek(0) =~ /[0-9]/ + throw :RET, identify_number + else + @lex_state = EXPR_BEG + end + elsif @lex_state != EXPR_END and peek(0) =~ /[0-9]/ + throw :RET, identify_number + else + @lex_state = EXPR_BEG + end + Token(op) + end + end + + @OP.def_rule(".") do + |op, io| + @lex_state = EXPR_BEG + if peek(0) =~ /[0-9]/ + ungetc + identify_number + else + # for "obj.if" etc. + @lex_state = EXPR_DOT + Token(TkDOT) + end + end + + @OP.def_rules("..", "...") do + |op, io| + @lex_state = EXPR_BEG + Token(op) + end + + lex_int2 + end + + def lex_int2 + @OP.def_rules("]", "}", ")") do + |op, io| + @lex_state = EXPR_END + @indent -= 1 + @indent_stack.pop + Token(op) + end + + @OP.def_rule(":") do + |op, io| + if @lex_state == EXPR_END || peek(0) =~ /\s/ + @lex_state = EXPR_BEG + Token(TkCOLON) + else + @lex_state = EXPR_FNAME + Token(TkSYMBEG) + end + end + + @OP.def_rule("::") do + |op, io| +# p @lex_state.id2name, @space_seen + if @lex_state == EXPR_BEG or @lex_state == EXPR_ARG && @space_seen + @lex_state = EXPR_BEG + Token(TkCOLON3) + else + @lex_state = EXPR_DOT + Token(TkCOLON2) + end + end + + @OP.def_rule("/") do + |op, io| + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + identify_string(op) + elsif peek(0) == '=' + getc + @lex_state = EXPR_BEG + Token(TkOPASGN, "/") #/) + elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/ + identify_string(op) + else + @lex_state = EXPR_BEG + Token("/") #/) + end + end + + @OP.def_rules("^") do + |op, io| + @lex_state = EXPR_BEG + Token("^") + end + + # @OP.def_rules("^=") do + # @lex_state = EXPR_BEG + # Token(OP_ASGN, :^) + # end + + @OP.def_rules(",") do + |op, io| + @lex_state = EXPR_BEG + Token(op) + end + + @OP.def_rules(";") do + |op, io| + @lex_state = EXPR_BEG + until (@indent_stack.empty? || + [TkLPAREN, TkLBRACK, TkLBRACE, + TkfLPAREN, TkfLBRACK, TkfLBRACE].include?(@indent_stack.last)) + @indent_stack.pop + end + Token(op) + end + + @OP.def_rule("~") do + |op, io| + @lex_state = EXPR_BEG + Token("~") + end + + @OP.def_rule("~@", proc{|op, io| @lex_state == EXPR_FNAME}) do + |op, io| + @lex_state = EXPR_BEG + Token("~") + end + + @OP.def_rule("(") do + |op, io| + @indent += 1 + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + @lex_state = EXPR_BEG + tk_c = TkfLPAREN + else + @lex_state = EXPR_BEG + tk_c = TkLPAREN + end + @indent_stack.push tk_c + Token(tk_c) + end + + @OP.def_rule("[]", proc{|op, io| @lex_state == EXPR_FNAME}) do + |op, io| + @lex_state = EXPR_ARG + Token("[]") + end + + @OP.def_rule("[]=", proc{|op, io| @lex_state == EXPR_FNAME}) do + |op, io| + @lex_state = EXPR_ARG + Token("[]=") + end + + @OP.def_rule("[") do + |op, io| + @indent += 1 + if @lex_state == EXPR_FNAME + tk_c = TkfLBRACK + else + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + tk_c = TkLBRACK + elsif @lex_state == EXPR_ARG && @space_seen + tk_c = TkLBRACK + else + tk_c = TkfLBRACK + end + @lex_state = EXPR_BEG + end + @indent_stack.push tk_c + Token(tk_c) + end + + @OP.def_rule("{") do + |op, io| + @indent += 1 + if @lex_state != EXPR_END && @lex_state != EXPR_ARG + tk_c = TkLBRACE + else + tk_c = TkfLBRACE + end + @lex_state = EXPR_BEG + @indent_stack.push tk_c + Token(tk_c) + end + + @OP.def_rule('\\') do + |op, io| + if getc == "\n" + @space_seen = true + @continue = true + Token(TkSPACE) + else + read_escape + Token("\\") + end + end + + @OP.def_rule('%') do + |op, io| + if @lex_state == EXPR_BEG || @lex_state == EXPR_MID + identify_quotation + elsif peek(0) == '=' + getc + Token(TkOPASGN, :%) + elsif @lex_state == EXPR_ARG and @space_seen and peek(0) !~ /\s/ + identify_quotation + else + @lex_state = EXPR_BEG + Token("%") #)) + end + end + + @OP.def_rule('$') do + |op, io| + identify_gvar + end + + @OP.def_rule('@') do + |op, io| + if peek(0) =~ /[\w@]/ + ungetc + identify_identifier + else + Token("@") + end + end + + # @OP.def_rule("def", proc{|op, io| /\s/ =~ io.peek(0)}) do + # |op, io| + # @indent += 1 + # @lex_state = EXPR_FNAME + # # @lex_state = EXPR_END + # # until @rests[0] == "\n" or @rests[0] == ";" + # # rests.shift + # # end + # end + + @OP.def_rule("") do + |op, io| + printf "MATCH: start %s: %s\n", op, io.inspect if RubyLex.debug? + if peek(0) =~ /[0-9]/ + t = identify_number + elsif peek(0) =~ /[^\x00-\/:-@\[-^`{-\x7F]/ + t = identify_identifier + end + printf "MATCH: end %s: %s\n", op, io.inspect if RubyLex.debug? + t + end + + p @OP if RubyLex.debug? + end + + def identify_gvar + @lex_state = EXPR_END + + case ch = getc + when /[~_*$?!@\/\\;,=:<>".]/ #" + Token(TkGVAR, "$" + ch) + when "-" + Token(TkGVAR, "$-" + getc) + when "&", "`", "'", "+" + Token(TkBACK_REF, "$"+ch) + when /[1-9]/ + while getc =~ /[0-9]/; end + ungetc + Token(TkNTH_REF) + when /\w/ + ungetc + ungetc + identify_identifier + else + ungetc + Token("$") + end + end + + def identify_identifier + token = "" + if peek(0) =~ /[$@]/ + token.concat(c = getc) + if c == "@" and peek(0) == "@" + token.concat getc + end + end + + while (ch = getc) =~ /[^\x00-\/:-@\[-^`{-\x7F]/ + print ":", ch, ":" if RubyLex.debug? + token.concat ch + end + ungetc + + if (ch == "!" || ch == "?") && token[0,1] =~ /\w/ && peek(0) != "=" + token.concat getc + end + + # almost fix token + + case token + when /^\$/ + return Token(TkGVAR, token) + when /^\@\@/ + @lex_state = EXPR_END + # p Token(TkCVAR, token) + return Token(TkCVAR, token) + when /^\@/ + @lex_state = EXPR_END + return Token(TkIVAR, token) + end + + if @lex_state != EXPR_DOT + print token, "\n" if RubyLex.debug? + + token_c, *trans = TkReading2Token[token] + if token_c + # reserved word? + + if (@lex_state != EXPR_BEG && + @lex_state != EXPR_FNAME && + trans[1]) + # modifiers + token_c = TkSymbol2Token[trans[1]] + @lex_state = trans[0] + else + if @lex_state != EXPR_FNAME + if ENINDENT_CLAUSE.include?(token) + # check for ``class = val'' etc. + valid = true + case token + when "class" + valid = false unless peek_match?(/^\s*(<<|\w|::)/) + when "def" + valid = false if peek_match?(/^\s*(([+\-\/*&\|^]|<<|>>|\|\||\&\&)=|\&\&|\|\|)/) + when "do" + valid = false if peek_match?(/^\s*([+\-\/*]?=|\*|<|>|\&)/) + when *ENINDENT_CLAUSE + valid = false if peek_match?(/^\s*([+\-\/*]?=|\*|<|>|\&|\|)/) + else + # no nothing + end + if valid + if token == "do" + if ![TkFOR, TkWHILE, TkUNTIL].include?(@indent_stack.last) + @indent += 1 + @indent_stack.push token_c + end + else + @indent += 1 + @indent_stack.push token_c + end +# p @indent_stack + end + + elsif DEINDENT_CLAUSE.include?(token) + @indent -= 1 + @indent_stack.pop + end + @lex_state = trans[0] + else + @lex_state = EXPR_END + end + end + return Token(token_c, token) + end + end + + if @lex_state == EXPR_FNAME + @lex_state = EXPR_END + if peek(0) == '=' + token.concat getc + end + elsif @lex_state == EXPR_BEG || @lex_state == EXPR_DOT + @lex_state = EXPR_ARG + else + @lex_state = EXPR_END + end + + if token[0, 1] =~ /[A-Z]/ + return Token(TkCONSTANT, token) + elsif token[token.size - 1, 1] =~ /[!?]/ + return Token(TkFID, token) + else + return Token(TkIDENTIFIER, token) + end + end + + def identify_here_document + ch = getc +# if lt = PERCENT_LTYPE[ch] + if ch == "-" + ch = getc + indent = true + end + if /['"`]/ =~ ch + lt = ch + quoted = "" + while (c = getc) && c != lt + quoted.concat c + end + else + lt = '"' + quoted = ch.dup + while (c = getc) && c =~ /\w/ + quoted.concat c + end + ungetc + end + + ltback, @ltype = @ltype, lt + reserve = [] + while ch = getc + reserve.push ch + if ch == "\\" + reserve.push ch = getc + elsif ch == "\n" + break + end + end + + @here_header = false +# while l = gets +# l = l.sub(/(:?\r)?\n\z/, '') +# if (indent ? l.strip : l) == quoted +# break +# end +# end + + line = "" + while ch = getc + if ch == "\n" + if line == quoted + break + end + line = "" + else + line.concat ch unless indent && line == "" && /\s/ =~ ch + if @ltype != "'" && ch == "#" && peek(0) == "{" + identify_string_dvar + end + end + end + + @here_header = true + @here_readed.concat reserve + while ch = reserve.pop + ungetc ch + end + + @ltype = ltback + @lex_state = EXPR_END + Token(Ltype2Token[lt]) + end + + def identify_quotation + ch = getc + if lt = PERCENT_LTYPE[ch] + ch = getc + elsif ch =~ /\W/ + lt = "\"" + else + RubyLex.fail SyntaxError, "unknown type of %string" + end +# if ch !~ /\W/ +# ungetc +# next +# end + #@ltype = lt + @quoted = ch unless @quoted = PERCENT_PAREN[ch] + identify_string(lt, @quoted) + end + + def identify_number + @lex_state = EXPR_END + + if peek(0) == "0" && peek(1) !~ /[.eE]/ + getc + case peek(0) + when /[xX]/ + ch = getc + match = /[0-9a-fA-F_]/ + when /[bB]/ + ch = getc + match = /[01_]/ + when /[oO]/ + ch = getc + match = /[0-7_]/ + when /[dD]/ + ch = getc + match = /[0-9_]/ + when /[0-7]/ + match = /[0-7_]/ + when /[89]/ + RubyLex.fail SyntaxError, "Invalid octal digit" + else + return Token(TkINTEGER) + end + + len0 = true + non_digit = false + while ch = getc + if match =~ ch + if ch == "_" + if non_digit + RubyLex.fail SyntaxError, "trailing `#{ch}' in number" + else + non_digit = ch + end + else + non_digit = false + len0 = false + end + else + ungetc + if len0 + RubyLex.fail SyntaxError, "numeric literal without digits" + end + if non_digit + RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number" + end + break + end + end + return Token(TkINTEGER) + end + + type = TkINTEGER + allow_point = true + allow_e = true + non_digit = false + while ch = getc + case ch + when /[0-9]/ + non_digit = false + when "_" + non_digit = ch + when allow_point && "." + if non_digit + RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number" + end + type = TkFLOAT + if peek(0) !~ /[0-9]/ + type = TkINTEGER + ungetc + break + end + allow_point = false + when allow_e && "e", allow_e && "E" + if non_digit + RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number" + end + type = TkFLOAT + if peek(0) =~ /[+-]/ + getc + end + allow_e = false + allow_point = false + non_digit = ch + else + if non_digit + RubyLex.fail SyntaxError, "trailing `#{non_digit}' in number" + end + ungetc + break + end + end + Token(type) + end + + def identify_string(ltype, quoted = ltype) + @ltype = ltype + @quoted = quoted + subtype = nil + begin + nest = 0 + while ch = getc + if @quoted == ch and nest == 0 + break + elsif @ltype != "'" && ch == "#" && peek(0) == "{" + identify_string_dvar + elsif @ltype != "'" && @ltype != "]" && @ltype != ":" and ch == "#" + subtype = true + elsif ch == '\\' and @ltype == "'" #' + case ch = getc + when "\\", "\n", "'" + else + ungetc + end + elsif ch == '\\' #' + read_escape + end + if PERCENT_PAREN.values.include?(@quoted) + if PERCENT_PAREN[ch] == @quoted + nest += 1 + elsif ch == @quoted + nest -= 1 + end + end + end + if @ltype == "/" + while /[imxoesun]/ =~ peek(0) + getc + end + end + if subtype + Token(DLtype2Token[ltype]) + else + Token(Ltype2Token[ltype]) + end + ensure + @ltype = nil + @quoted = nil + @lex_state = EXPR_END + end + end + + def identify_string_dvar + begin + getc + + reserve_continue = @continue + reserve_ltype = @ltype + reserve_indent = @indent + reserve_indent_stack = @indent_stack + reserve_state = @lex_state + reserve_quoted = @quoted + + @ltype = nil + @quoted = nil + @indent = 0 + @indent_stack = [] + @lex_state = EXPR_BEG + + loop do + @continue = false + prompt + tk = token + if @ltype or @continue or @indent > 0 + next + end + break if tk.kind_of?(TkRBRACE) + end + ensure + @continue = reserve_continue + @ltype = reserve_ltype + @indent = reserve_indent + @indent_stack = reserve_indent_stack + @lex_state = reserve_state + @quoted = reserve_quoted + end + end + + def identify_comment + @ltype = "#" + + while ch = getc +# if ch == "\\" #" +# read_escape +# end + if ch == "\n" + @ltype = nil + ungetc + break + end + end + return Token(TkCOMMENT) + end + + def read_escape + case ch = getc + when "\n", "\r", "\f" + when "\\", "n", "t", "r", "f", "v", "a", "e", "b", "s" #" + when /[0-7]/ + ungetc ch + 3.times do + case ch = getc + when /[0-7]/ + when nil + break + else + ungetc + break + end + end + + when "x" + 2.times do + case ch = getc + when /[0-9a-fA-F]/ + when nil + break + else + ungetc + break + end + end + + when "M" + if (ch = getc) != '-' + ungetc + else + if (ch = getc) == "\\" #" + read_escape + end + end + + when "C", "c" #, "^" + if ch == "C" and (ch = getc) != "-" + ungetc + elsif (ch = getc) == "\\" #" + read_escape + end + else + # other characters + end + end +end +# :startdoc: diff --git a/ruby/lib/ruby/2.1.0/irb/ruby-token.rb b/ruby/lib/ruby/2.1.0/irb/ruby-token.rb new file mode 100644 index 0000000..2b63629 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/ruby-token.rb @@ -0,0 +1,267 @@ +# +# irb/ruby-token.rb - ruby tokens +# $Release Version: 0.9.6$ +# $Revision: 38358 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +# :stopdoc: +module RubyToken + EXPR_BEG = :EXPR_BEG + EXPR_MID = :EXPR_MID + EXPR_END = :EXPR_END + EXPR_ARG = :EXPR_ARG + EXPR_FNAME = :EXPR_FNAME + EXPR_DOT = :EXPR_DOT + EXPR_CLASS = :EXPR_CLASS + + class Token + def initialize(seek, line_no, char_no) + @seek = seek + @line_no = line_no + @char_no = char_no + end + attr :seek, :line_no, :char_no + end + + class TkNode < Token + def initialize(seek, line_no, char_no) + super + end + attr :node + end + + class TkId < Token + def initialize(seek, line_no, char_no, name) + super(seek, line_no, char_no) + @name = name + end + attr :name + end + + class TkVal < Token + def initialize(seek, line_no, char_no, value = nil) + super(seek, line_no, char_no) + @value = value + end + attr :value + end + + class TkOp < Token + attr_accessor :name + end + + class TkOPASGN < TkOp + def initialize(seek, line_no, char_no, op) + super(seek, line_no, char_no) + op = TkReading2Token[op][0] unless op.kind_of?(Symbol) + @op = op + end + attr :op + end + + class TkUnknownChar < Token + def initialize(seek, line_no, char_no, id) + super(seek, line_no, char_no) + @name = name + end + attr :name + end + + class TkError < Token + end + + def Token(token, value = nil) + case token + when String + if (tk = TkReading2Token[token]).nil? + IRB.fail TkReading2TokenNoKey, token + end + tk = Token(tk[0], value) + if tk.kind_of?(TkOp) + tk.name = token + end + return tk + when Symbol + if (tk = TkSymbol2Token[token]).nil? + IRB.fail TkSymbol2TokenNoKey, token + end + return Token(tk[0], value) + else + if (token.ancestors & [TkId, TkVal, TkOPASGN, TkUnknownChar]).empty? + token.new(@prev_seek, @prev_line_no, @prev_char_no) + else + token.new(@prev_seek, @prev_line_no, @prev_char_no, value) + end + end + end + + TokenDefinitions = [ + [:TkCLASS, TkId, "class", EXPR_CLASS], + [:TkMODULE, TkId, "module", EXPR_BEG], + [:TkDEF, TkId, "def", EXPR_FNAME], + [:TkUNDEF, TkId, "undef", EXPR_FNAME], + [:TkBEGIN, TkId, "begin", EXPR_BEG], + [:TkRESCUE, TkId, "rescue", EXPR_MID], + [:TkENSURE, TkId, "ensure", EXPR_BEG], + [:TkEND, TkId, "end", EXPR_END], + [:TkIF, TkId, "if", EXPR_BEG, :TkIF_MOD], + [:TkUNLESS, TkId, "unless", EXPR_BEG, :TkUNLESS_MOD], + [:TkTHEN, TkId, "then", EXPR_BEG], + [:TkELSIF, TkId, "elsif", EXPR_BEG], + [:TkELSE, TkId, "else", EXPR_BEG], + [:TkCASE, TkId, "case", EXPR_BEG], + [:TkWHEN, TkId, "when", EXPR_BEG], + [:TkWHILE, TkId, "while", EXPR_BEG, :TkWHILE_MOD], + [:TkUNTIL, TkId, "until", EXPR_BEG, :TkUNTIL_MOD], + [:TkFOR, TkId, "for", EXPR_BEG], + [:TkBREAK, TkId, "break", EXPR_END], + [:TkNEXT, TkId, "next", EXPR_END], + [:TkREDO, TkId, "redo", EXPR_END], + [:TkRETRY, TkId, "retry", EXPR_END], + [:TkIN, TkId, "in", EXPR_BEG], + [:TkDO, TkId, "do", EXPR_BEG], + [:TkRETURN, TkId, "return", EXPR_MID], + [:TkYIELD, TkId, "yield", EXPR_END], + [:TkSUPER, TkId, "super", EXPR_END], + [:TkSELF, TkId, "self", EXPR_END], + [:TkNIL, TkId, "nil", EXPR_END], + [:TkTRUE, TkId, "true", EXPR_END], + [:TkFALSE, TkId, "false", EXPR_END], + [:TkAND, TkId, "and", EXPR_BEG], + [:TkOR, TkId, "or", EXPR_BEG], + [:TkNOT, TkId, "not", EXPR_BEG], + [:TkIF_MOD, TkId], + [:TkUNLESS_MOD, TkId], + [:TkWHILE_MOD, TkId], + [:TkUNTIL_MOD, TkId], + [:TkALIAS, TkId, "alias", EXPR_FNAME], + [:TkDEFINED, TkId, "defined?", EXPR_END], + [:TklBEGIN, TkId, "BEGIN", EXPR_END], + [:TklEND, TkId, "END", EXPR_END], + [:Tk__LINE__, TkId, "__LINE__", EXPR_END], + [:Tk__FILE__, TkId, "__FILE__", EXPR_END], + + [:TkIDENTIFIER, TkId], + [:TkFID, TkId], + [:TkGVAR, TkId], + [:TkCVAR, TkId], + [:TkIVAR, TkId], + [:TkCONSTANT, TkId], + + [:TkINTEGER, TkVal], + [:TkFLOAT, TkVal], + [:TkSTRING, TkVal], + [:TkXSTRING, TkVal], + [:TkREGEXP, TkVal], + [:TkSYMBOL, TkVal], + + [:TkDSTRING, TkNode], + [:TkDXSTRING, TkNode], + [:TkDREGEXP, TkNode], + [:TkNTH_REF, TkNode], + [:TkBACK_REF, TkNode], + + [:TkUPLUS, TkOp, "+@"], + [:TkUMINUS, TkOp, "-@"], + [:TkPOW, TkOp, "**"], + [:TkCMP, TkOp, "<=>"], + [:TkEQ, TkOp, "=="], + [:TkEQQ, TkOp, "==="], + [:TkNEQ, TkOp, "!="], + [:TkGEQ, TkOp, ">="], + [:TkLEQ, TkOp, "<="], + [:TkANDOP, TkOp, "&&"], + [:TkOROP, TkOp, "||"], + [:TkMATCH, TkOp, "=~"], + [:TkNMATCH, TkOp, "!~"], + [:TkDOT2, TkOp, ".."], + [:TkDOT3, TkOp, "..."], + [:TkAREF, TkOp, "[]"], + [:TkASET, TkOp, "[]="], + [:TkLSHFT, TkOp, "<<"], + [:TkRSHFT, TkOp, ">>"], + [:TkCOLON2, TkOp], + [:TkCOLON3, TkOp], +# [:OPASGN, TkOp], # +=, -= etc. # + [:TkASSOC, TkOp, "=>"], + [:TkQUESTION, TkOp, "?"], #? + [:TkCOLON, TkOp, ":"], #: + + [:TkfLPAREN], # func( # + [:TkfLBRACK], # func[ # + [:TkfLBRACE], # func{ # + [:TkSTAR], # *arg + [:TkAMPER], # &arg # + [:TkSYMBEG], # :SYMBOL + + [:TkGT, TkOp, ">"], + [:TkLT, TkOp, "<"], + [:TkPLUS, TkOp, "+"], + [:TkMINUS, TkOp, "-"], + [:TkMULT, TkOp, "*"], + [:TkDIV, TkOp, "/"], + [:TkMOD, TkOp, "%"], + [:TkBITOR, TkOp, "|"], + [:TkBITXOR, TkOp, "^"], + [:TkBITAND, TkOp, "&"], + [:TkBITNOT, TkOp, "~"], + [:TkNOTOP, TkOp, "!"], + + [:TkBACKQUOTE, TkOp, "`"], + + [:TkASSIGN, Token, "="], + [:TkDOT, Token, "."], + [:TkLPAREN, Token, "("], #(exp) + [:TkLBRACK, Token, "["], #[arry] + [:TkLBRACE, Token, "{"], #{hash} + [:TkRPAREN, Token, ")"], + [:TkRBRACK, Token, "]"], + [:TkRBRACE, Token, "}"], + [:TkCOMMA, Token, ","], + [:TkSEMICOLON, Token, ";"], + + [:TkCOMMENT], + [:TkRD_COMMENT], + [:TkSPACE], + [:TkNL], + [:TkEND_OF_SCRIPT], + + [:TkBACKSLASH, TkUnknownChar, "\\"], + [:TkAT, TkUnknownChar, "@"], + [:TkDOLLAR, TkUnknownChar, "$"], + ] + + # {reading => token_class} + # {reading => [token_class, *opt]} + TkReading2Token = {} + TkSymbol2Token = {} + + def RubyToken.def_token(token_n, super_token = Token, reading = nil, *opts) + token_n = token_n.id2name if token_n.kind_of?(Symbol) + if RubyToken.const_defined?(token_n) + IRB.fail AlreadyDefinedToken, token_n + end + token_c = eval("class #{token_n} < #{super_token}; end; #{token_n}") + + if reading + if TkReading2Token[reading] + IRB.fail TkReading2TokenDuplicateError, token_n, reading + end + if opts.empty? + TkReading2Token[reading] = [token_c] + else + TkReading2Token[reading] = [token_c].concat(opts) + end + end + TkSymbol2Token[token_n.intern] = token_c + end + + for defs in TokenDefinitions + def_token(*defs) + end +end +# :startdoc: diff --git a/ruby/lib/ruby/2.1.0/irb/slex.rb b/ruby/lib/ruby/2.1.0/irb/slex.rb new file mode 100644 index 0000000..49cca6c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/slex.rb @@ -0,0 +1,284 @@ +# +# irb/slex.rb - simple lex analyzer +# $Release Version: 0.9.6$ +# $Revision: 38545 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +require "e2mmap" +require "irb/notifier" + +# :stopdoc: +module IRB + class SLex + @RCS_ID='-$Id: slex.rb 38545 2012-12-21 17:36:14Z zzak $-' + + extend Exception2MessageMapper + def_exception :ErrNodeNothing, "node nothing" + def_exception :ErrNodeAlreadyExists, "node already exists" + + DOUT = Notifier::def_notifier("SLex::") + D_WARN = DOUT::def_notifier(1, "Warn: ") + D_DEBUG = DOUT::def_notifier(2, "Debug: ") + D_DETAIL = DOUT::def_notifier(4, "Detail: ") + + DOUT.level = Notifier::D_NOMSG + + def initialize + @head = Node.new("") + end + + def def_rule(token, preproc = nil, postproc = nil, &block) + D_DETAIL.pp token + + postproc = block if block_given? + create(token, preproc, postproc) + end + + def def_rules(*tokens, &block) + if block_given? + p = block + end + for token in tokens + def_rule(token, nil, p) + end + end + + def preproc(token, proc) + node = search(token) + node.preproc=proc + end + + #$BMW%A%'%C%/(B? + def postproc(token) + node = search(token, proc) + node.postproc=proc + end + + def search(token) + @head.search(token.split(//)) + end + + def create(token, preproc = nil, postproc = nil) + @head.create_subnode(token.split(//), preproc, postproc) + end + + def match(token) + case token + when Array + when String + return match(token.split(//)) + else + return @head.match_io(token) + end + ret = @head.match(token) + D_DETAIL.exec_if{D_DETAIL.printf "match end: %s:%s\n", ret, token.inspect} + ret + end + + def inspect + format("", @head.inspect) + end + + #---------------------------------------------------------------------- + # + # class Node - + # + #---------------------------------------------------------------------- + class Node + # if postproc is nil, this node is an abstract node. + # if postproc is non-nil, this node is a real node. + def initialize(preproc = nil, postproc = nil) + @Tree = {} + @preproc = preproc + @postproc = postproc + end + + attr_accessor :preproc + attr_accessor :postproc + + def search(chrs, opt = nil) + return self if chrs.empty? + ch = chrs.shift + if node = @Tree[ch] + node.search(chrs, opt) + else + if opt + chrs.unshift ch + self.create_subnode(chrs) + else + SLex.fail ErrNodeNothing + end + end + end + + def create_subnode(chrs, preproc = nil, postproc = nil) + if chrs.empty? + if @postproc + D_DETAIL.pp node + SLex.fail ErrNodeAlreadyExists + else + D_DEBUG.puts "change abstract node to real node." + @preproc = preproc + @postproc = postproc + end + return self + end + + ch = chrs.shift + if node = @Tree[ch] + if chrs.empty? + if node.postproc + DebugLogger.pp node + DebugLogger.pp self + DebugLogger.pp ch + DebugLogger.pp chrs + SLex.fail ErrNodeAlreadyExists + else + D_WARN.puts "change abstract node to real node" + node.preproc = preproc + node.postproc = postproc + end + else + node.create_subnode(chrs, preproc, postproc) + end + else + if chrs.empty? + node = Node.new(preproc, postproc) + else + node = Node.new + node.create_subnode(chrs, preproc, postproc) + end + @Tree[ch] = node + end + node + end + + # + # chrs: String + # character array + # io must have getc()/ungetc(); and ungetc() must be + # able to be called arbitrary number of times. + # + def match(chrs, op = "") + D_DETAIL.print "match>: ", chrs, "op:", op, "\n" + if chrs.empty? + if @preproc.nil? || @preproc.call(op, chrs) + DOUT.printf(D_DETAIL, "op1: %s\n", op) + @postproc.call(op, chrs) + else + nil + end + else + ch = chrs.shift + if node = @Tree[ch] + if ret = node.match(chrs, op+ch) + return ret + else + chrs.unshift ch + if @postproc and @preproc.nil? || @preproc.call(op, chrs) + DOUT.printf(D_DETAIL, "op2: %s\n", op.inspect) + ret = @postproc.call(op, chrs) + return ret + else + return nil + end + end + else + chrs.unshift ch + if @postproc and @preproc.nil? || @preproc.call(op, chrs) + DOUT.printf(D_DETAIL, "op3: %s\n", op) + @postproc.call(op, chrs) + return "" + else + return nil + end + end + end + end + + def match_io(io, op = "") + if op == "" + ch = io.getc + if ch == nil + return nil + end + else + ch = io.getc_of_rests + end + if ch.nil? + if @preproc.nil? || @preproc.call(op, io) + D_DETAIL.printf("op1: %s\n", op) + @postproc.call(op, io) + else + nil + end + else + if node = @Tree[ch] + if ret = node.match_io(io, op+ch) + ret + else + io.ungetc ch + if @postproc and @preproc.nil? || @preproc.call(op, io) + DOUT.exec_if{D_DETAIL.printf "op2: %s\n", op.inspect} + @postproc.call(op, io) + else + nil + end + end + else + io.ungetc ch + if @postproc and @preproc.nil? || @preproc.call(op, io) + D_DETAIL.printf("op3: %s\n", op) + @postproc.call(op, io) + else + nil + end + end + end + end + end + end +end +# :startdoc: + +if $0 == __FILE__ + # Tracer.on + case $1 + when "1" + tr = SLex.new + print "0: ", tr.inspect, "\n" + tr.def_rule("=") {print "=\n"} + print "1: ", tr.inspect, "\n" + tr.def_rule("==") {print "==\n"} + print "2: ", tr.inspect, "\n" + + print "case 1:\n" + print tr.match("="), "\n" + print "case 2:\n" + print tr.match("=="), "\n" + print "case 3:\n" + print tr.match("=>"), "\n" + + when "2" + tr = SLex.new + print "0: ", tr.inspect, "\n" + tr.def_rule("=") {print "=\n"} + print "1: ", tr.inspect, "\n" + tr.def_rule("==", proc{false}) {print "==\n"} + print "2: ", tr.inspect, "\n" + + print "case 1:\n" + print tr.match("="), "\n" + print "case 2:\n" + print tr.match("=="), "\n" + print "case 3:\n" + print tr.match("=>"), "\n" + end + exit +end + diff --git a/ruby/lib/ruby/2.1.0/irb/src_encoding.rb b/ruby/lib/ruby/2.1.0/irb/src_encoding.rb new file mode 100644 index 0000000..958cef1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/src_encoding.rb @@ -0,0 +1,4 @@ +# DO NOT WRITE ANY MAGIC COMMENT HERE. +def default_src_encoding + return __ENCODING__ +end diff --git a/ruby/lib/ruby/2.1.0/irb/version.rb b/ruby/lib/ruby/2.1.0/irb/version.rb new file mode 100644 index 0000000..a0e556e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/version.rb @@ -0,0 +1,15 @@ +# +# irb/version.rb - irb version definition file +# $Release Version: 0.9.6$ +# $Revision: 38358 $ +# by Keiju ISHITSUKA(keiju@ishitsuka.com) +# +# -- +# +# +# + +module IRB # :nodoc: + @RELEASE_VERSION = "0.9.6" + @LAST_UPDATE_DATE = "09/06/30" +end diff --git a/ruby/lib/ruby/2.1.0/irb/workspace.rb b/ruby/lib/ruby/2.1.0/irb/workspace.rb new file mode 100644 index 0000000..3cb8d13 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/workspace.rb @@ -0,0 +1,114 @@ +# +# irb/workspace-binding.rb - +# $Release Version: 0.9.6$ +# $Revision: 43933 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# +module IRB # :nodoc: + class WorkSpace + # Creates a new workspace. + # + # set self to main if specified, otherwise + # inherit main from TOPLEVEL_BINDING. + def initialize(*main) + if main[0].kind_of?(Binding) + @binding = main.shift + elsif IRB.conf[:SINGLE_IRB] + @binding = TOPLEVEL_BINDING + else + case IRB.conf[:CONTEXT_MODE] + when 0 # binding in proc on TOPLEVEL_BINDING + @binding = eval("proc{binding}.call", + TOPLEVEL_BINDING, + __FILE__, + __LINE__) + when 1 # binding in loaded file + require "tempfile" + f = Tempfile.open("irb-binding") + f.print <IRB.conf[:__MAIN__] + attr_reader :main + + # Evaluate the given +statements+ within the context of this workspace. + def evaluate(context, statements, file = __FILE__, line = __LINE__) + eval(statements, @binding, file, line) + end + + # error message manipulator + def filter_backtrace(bt) + case IRB.conf[:CONTEXT_MODE] + when 0 + return nil if bt =~ /\(irb_local_binding\)/ + when 1 + if(bt =~ %r!/tmp/irb-binding! or + bt =~ %r!irb/.*\.rb! or + bt =~ /irb\.rb/) + return nil + end + when 2 + return nil if bt =~ /irb\/.*\.rb/ + return nil if bt =~ /irb\.rb/ + when 3 + return nil if bt =~ /irb\/.*\.rb/ + return nil if bt =~ /irb\.rb/ + bt = bt.sub(/:\s*in `irb_binding'/, '') + end + bt + end + + def IRB.delete_caller + end + end +end diff --git a/ruby/lib/ruby/2.1.0/irb/ws-for-case-2.rb b/ruby/lib/ruby/2.1.0/irb/ws-for-case-2.rb new file mode 100644 index 0000000..a70183e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/ws-for-case-2.rb @@ -0,0 +1,14 @@ +# +# irb/ws-for-case-2.rb - +# $Release Version: 0.9.6$ +# $Revision: 29726 $ +# by Keiju ISHITSUKA(keiju@ruby-lang.org) +# +# -- +# +# +# + +while true + IRB::BINDING_QUEUE.push _ = binding +end diff --git a/ruby/lib/ruby/2.1.0/irb/xmp.rb b/ruby/lib/ruby/2.1.0/irb/xmp.rb new file mode 100644 index 0000000..6e3af5f --- /dev/null +++ b/ruby/lib/ruby/2.1.0/irb/xmp.rb @@ -0,0 +1,173 @@ +# +# xmp.rb - irb version of gotoken xmp +# $Release Version: 0.9$ +# $Revision: 38515 $ +# by Keiju ISHITSUKA(Nippon Rational Inc.) +# +# -- +# +# +# + +require "irb" +require "irb/frame" + +# An example printer for irb. +# +# It's much like the standard library PrettyPrint, that shows the value of each +# expression as it runs. +# +# In order to use this library, you must first require it: +# +# require 'irb/xmp' +# +# Now, you can take advantage of the Object#xmp convenience method. +# +# xmp < foo = "bar" +# #==>"bar" +# #=> baz = 42 +# #==>42 +# +# You can also create an XMP object, with an optional binding to print +# expressions in the given binding: +# +# ctx = binding +# x = XMP.new ctx +# x.puts +# #=> today = "a good day" +# #==>"a good day" +# ctx.eval 'today # is what?' +# #=> "a good day" +class XMP + @RCS_ID='-$Id: xmp.rb 38515 2012-12-21 05:45:50Z zzak $-' + + # Creates a new XMP object. + # + # The top-level binding or, optional +bind+ parameter will be used when + # creating the workspace. See WorkSpace.new for more information. + # + # This uses the +:XMP+ prompt mode, see IRB@Customizing+the+IRB+Prompt for + # full detail. + def initialize(bind = nil) + IRB.init_config(nil) + #IRB.parse_opts + #IRB.load_modules + + IRB.conf[:PROMPT_MODE] = :XMP + + bind = IRB::Frame.top(1) unless bind + ws = IRB::WorkSpace.new(bind) + @io = StringInputMethod.new + @irb = IRB::Irb.new(ws, @io) + @irb.context.ignore_sigint = false + +# IRB.conf[:IRB_RC].call(@irb.context) if IRB.conf[:IRB_RC] + IRB.conf[:MAIN_CONTEXT] = @irb.context + end + + # Evaluates the given +exps+, for example: + # + # require 'irb/xmp' + # x = XMP.new + # + # x.puts '{:a => 1, :b => 2, :c => 3}' + # #=> {:a => 1, :b => 2, :c => 3} + # # ==>{:a=>1, :b=>2, :c=>3} + # x.puts 'foo = "bar"' + # # => foo = "bar" + # # ==>"bar" + def puts(exps) + @io.puts exps + + if @irb.context.ignore_sigint + begin + trap_proc_b = trap("SIGINT"){@irb.signal_handle} + catch(:IRB_EXIT) do + @irb.eval_input + end + ensure + trap("SIGINT", trap_proc_b) + end + else + catch(:IRB_EXIT) do + @irb.eval_input + end + end + end + + # A custom InputMethod class used by XMP for evaluating string io. + class StringInputMethod < IRB::InputMethod + # Creates a new StringInputMethod object + def initialize + super + @exps = [] + end + + # Whether there are any expressions left in this printer. + def eof? + @exps.empty? + end + + # Reads the next expression from this printer. + # + # See IO#gets for more information. + def gets + while l = @exps.shift + next if /^\s+$/ =~ l + l.concat "\n" + print @prompt, l + break + end + l + end + + # Concatenates all expressions in this printer, separated by newlines. + # + # An Encoding::CompatibilityError is raised of the given +exps+'s encoding + # doesn't match the previous expression evaluated. + def puts(exps) + if @encoding and exps.encoding != @encoding + enc = Encoding.compatible?(@exps.join("\n"), exps) + if enc.nil? + raise Encoding::CompatibilityError, "Encoding in which the passed expression is encoded is not compatible to the preceding's one" + else + @encoding = enc + end + else + @encoding = exps.encoding + end + @exps.concat exps.split(/\n/) + end + + # Returns the encoding of last expression printed by #puts. + attr_reader :encoding + end +end + +# A convenience method that's only available when the you require the IRB::XMP standard library. +# +# Creates a new XMP object, using the given expressions as the +exps+ +# parameter, and optional binding as +bind+ or uses the top-level binding. Then +# evaluates the given expressions using the +:XMP+ prompt mode. +# +# For example: +# +# require 'irb/xmp' +# ctx = binding +# xmp 'foo = "bar"', ctx +# #=> foo = "bar" +# #==>"bar" +# ctx.eval 'foo' +# #=> "bar" +# +# See XMP.new for more information. +def xmp(exps, bind = nil) + bind = IRB::Frame.top(1) unless bind + xmp = XMP.new(bind) + xmp.puts exps + xmp +end diff --git a/ruby/lib/ruby/2.1.0/json.rb b/ruby/lib/ruby/2.1.0/json.rb new file mode 100644 index 0000000..24aa385 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json.rb @@ -0,0 +1,62 @@ +require 'json/common' + +## +# = JavaScript Object Notation (JSON) +# +# JSON is a lightweight data-interchange format. It is easy for us +# humans to read and write. Plus, equally simple for machines to generate or parse. +# JSON is completely language agnostic, making it the ideal interchange format. +# +# Built on two universally available structures: +# 1. A collection of name/value pairs. Often referred to as an _object_, hash table, record, struct, keyed list, or associative array. +# 2. An ordered list of values. More commonly called an _array_, vector, sequence or list. +# +# To read more about JSON visit: http://json.org +# +# == Parsing JSON +# +# To parse a JSON string received by another application or generated within +# your existing application: +# +# require 'json' +# +# my_hash = JSON.parse('{"hello": "goodbye"}') +# puts my_hash["hello"] => "goodbye" +# +# Notice the extra quotes '' around the hash notation. Ruby expects +# the argument to be a string and can't convert objects like a hash or array. +# +# Ruby converts your string into a hash +# +# == Generating JSON +# +# Creating a JSON string for communication or serialization is +# just as simple. +# +# require 'json' +# +# my_hash = {:hello => "goodbye"} +# puts JSON.generate(my_hash) => "{\"hello\":\"goodbye\"}" +# +# Or an alternative way: +# +# require 'json' +# puts {:hello => "goodbye"}.to_json => "{\"hello\":\"goodbye\"}" +# +# JSON.generate only allows objects or arrays to be converted +# to JSON syntax. to_json, however, accepts many Ruby classes +# even though it acts only as a method for serialization: +# +# require 'json' +# +# 1.to_json => "1" +# +module JSON + require 'json/version' + + begin + require 'json/ext' + rescue LoadError + require 'json/pure' + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/bigdecimal.rb b/ruby/lib/ruby/2.1.0/json/add/bigdecimal.rb new file mode 100644 index 0000000..0ef69f1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/bigdecimal.rb @@ -0,0 +1,28 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +defined?(::BigDecimal) or require 'bigdecimal' + +class BigDecimal + # Import a JSON Marshalled object. + # + # method used for JSON marshalling support. + def self.json_create(object) + BigDecimal._load object['b'] + end + + # Marshal the object to JSON. + # + # method used for JSON marshalling support. + def as_json(*) + { + JSON.create_id => self.class.name, + 'b' => _dump, + } + end + + # return the JSON value + def to_json(*) + as_json.to_json + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/complex.rb b/ruby/lib/ruby/2.1.0/json/add/complex.rb new file mode 100644 index 0000000..d7ebebf --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/complex.rb @@ -0,0 +1,22 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +defined?(::Complex) or require 'complex' + +class Complex + def self.json_create(object) + Complex(object['r'], object['i']) + end + + def as_json(*) + { + JSON.create_id => self.class.name, + 'r' => real, + 'i' => imag, + } + end + + def to_json(*) + as_json.to_json + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/core.rb b/ruby/lib/ruby/2.1.0/json/add/core.rb new file mode 100644 index 0000000..77d9dc0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/core.rb @@ -0,0 +1,11 @@ +# This file requires the implementations of ruby core's custom objects for +# serialisation/deserialisation. + +require 'json/add/date' +require 'json/add/date_time' +require 'json/add/exception' +require 'json/add/range' +require 'json/add/regexp' +require 'json/add/struct' +require 'json/add/symbol' +require 'json/add/time' diff --git a/ruby/lib/ruby/2.1.0/json/add/date.rb b/ruby/lib/ruby/2.1.0/json/add/date.rb new file mode 100644 index 0000000..4288237 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/date.rb @@ -0,0 +1,34 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +require 'date' + +# Date serialization/deserialization +class Date + + # Deserializes JSON string by converting Julian year y, month + # m, day d and Day of Calendar Reform sg to Date. + def self.json_create(object) + civil(*object.values_at('y', 'm', 'd', 'sg')) + end + + alias start sg unless method_defined?(:start) + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 'y' => year, + 'm' => month, + 'd' => day, + 'sg' => start, + } + end + + # Stores class name (Date) with Julian year y, month m, day + # d and Day of Calendar Reform sg as JSON string + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/date_time.rb b/ruby/lib/ruby/2.1.0/json/add/date_time.rb new file mode 100644 index 0000000..5ea42ea --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/date_time.rb @@ -0,0 +1,50 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +require 'date' + +# DateTime serialization/deserialization +class DateTime + + # Deserializes JSON string by converting year y, month m, + # day d, hour H, minute M, second S, + # offset of and Day of Calendar Reform sg to DateTime. + def self.json_create(object) + args = object.values_at('y', 'm', 'd', 'H', 'M', 'S') + of_a, of_b = object['of'].split('/') + if of_b and of_b != '0' + args << Rational(of_a.to_i, of_b.to_i) + else + args << of_a + end + args << object['sg'] + civil(*args) + end + + alias start sg unless method_defined?(:start) + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 'y' => year, + 'm' => month, + 'd' => day, + 'H' => hour, + 'M' => min, + 'S' => sec, + 'of' => offset.to_s, + 'sg' => start, + } + end + + # Stores class name (DateTime) with Julian year y, month m, + # day d, hour H, minute M, second S, + # offset of and Day of Calendar Reform sg as JSON string + def to_json(*args) + as_json.to_json(*args) + end +end + + diff --git a/ruby/lib/ruby/2.1.0/json/add/exception.rb b/ruby/lib/ruby/2.1.0/json/add/exception.rb new file mode 100644 index 0000000..e6ad257 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/exception.rb @@ -0,0 +1,31 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Exception serialization/deserialization +class Exception + + # Deserializes JSON string by constructing new Exception object with message + # m and backtrace b serialized with to_json + def self.json_create(object) + result = new(object['m']) + result.set_backtrace object['b'] + result + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 'm' => message, + 'b' => backtrace, + } + end + + # Stores class name (Exception) with message m and backtrace array + # b as JSON string + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/ostruct.rb b/ruby/lib/ruby/2.1.0/json/add/ostruct.rb new file mode 100644 index 0000000..da81e10 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/ostruct.rb @@ -0,0 +1,31 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +require 'ostruct' + +# OpenStruct serialization/deserialization +class OpenStruct + + # Deserializes JSON string by constructing new Struct object with values + # v serialized by to_json. + def self.json_create(object) + new(object['t'] || object[:t]) + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + klass = self.class.name + klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!" + { + JSON.create_id => klass, + 't' => table, + } + end + + # Stores class name (OpenStruct) with this struct's values v as a + # JSON string. + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/range.rb b/ruby/lib/ruby/2.1.0/json/add/range.rb new file mode 100644 index 0000000..e61e553 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/range.rb @@ -0,0 +1,29 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Range serialization/deserialization +class Range + + # Deserializes JSON string by constructing new Range object with arguments + # a serialized by to_json. + def self.json_create(object) + new(*object['a']) + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 'a' => [ first, last, exclude_end? ] + } + end + + # Stores class name (Range) with JSON array of arguments a which + # include first (integer), last (integer), and + # exclude_end? (boolean) as JSON string. + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/rational.rb b/ruby/lib/ruby/2.1.0/json/add/rational.rb new file mode 100644 index 0000000..867cd92 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/rational.rb @@ -0,0 +1,22 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end +defined?(::Rational) or require 'rational' + +class Rational + def self.json_create(object) + Rational(object['n'], object['d']) + end + + def as_json(*) + { + JSON.create_id => self.class.name, + 'n' => numerator, + 'd' => denominator, + } + end + + def to_json(*) + as_json.to_json + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/regexp.rb b/ruby/lib/ruby/2.1.0/json/add/regexp.rb new file mode 100644 index 0000000..2fcbb6f --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/regexp.rb @@ -0,0 +1,30 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Regexp serialization/deserialization +class Regexp + + # Deserializes JSON string by constructing new Regexp object with source + # s (Regexp or String) and options o serialized by + # to_json + def self.json_create(object) + new(object['s'], object['o']) + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 'o' => options, + 's' => source, + } + end + + # Stores class name (Regexp) with options o and source s + # (Regexp or String) as JSON string + def to_json(*) + as_json.to_json + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/struct.rb b/ruby/lib/ruby/2.1.0/json/add/struct.rb new file mode 100644 index 0000000..6847cde --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/struct.rb @@ -0,0 +1,30 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Struct serialization/deserialization +class Struct + + # Deserializes JSON string by constructing new Struct object with values + # v serialized by to_json. + def self.json_create(object) + new(*object['v']) + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + klass = self.class.name + klass.to_s.empty? and raise JSON::JSONError, "Only named structs are supported!" + { + JSON.create_id => klass, + 'v' => values, + } + end + + # Stores class name (Struct) with Struct values v as a JSON string. + # Only named structs are supported. + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/symbol.rb b/ruby/lib/ruby/2.1.0/json/add/symbol.rb new file mode 100644 index 0000000..03dc9a5 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/symbol.rb @@ -0,0 +1,25 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Symbol serialization/deserialization +class Symbol + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + { + JSON.create_id => self.class.name, + 's' => to_s, + } + end + + # Stores class name (Symbol) with String representation of Symbol as a JSON string. + def to_json(*a) + as_json.to_json(*a) + end + + # Deserializes JSON string by converting the string value stored in the object to a Symbol + def self.json_create(o) + o['s'].to_sym + end +end diff --git a/ruby/lib/ruby/2.1.0/json/add/time.rb b/ruby/lib/ruby/2.1.0/json/add/time.rb new file mode 100644 index 0000000..338209d --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/add/time.rb @@ -0,0 +1,38 @@ +unless defined?(::JSON::JSON_LOADED) and ::JSON::JSON_LOADED + require 'json' +end + +# Time serialization/deserialization +class Time + + # Deserializes JSON string by converting time since epoch to Time + def self.json_create(object) + if usec = object.delete('u') # used to be tv_usec -> tv_nsec + object['n'] = usec * 1000 + end + if instance_methods.include?(:tv_nsec) + at(object['s'], Rational(object['n'], 1000)) + else + at(object['s'], object['n'] / 1000) + end + end + + # Returns a hash, that will be turned into a JSON object and represent this + # object. + def as_json(*) + nanoseconds = [ tv_usec * 1000 ] + respond_to?(:tv_nsec) and nanoseconds << tv_nsec + nanoseconds = nanoseconds.max + { + JSON.create_id => self.class.name, + 's' => tv_sec, + 'n' => nanoseconds, + } + end + + # Stores class name (Time) with number of seconds since epoch and number of + # microseconds for Time as JSON string + def to_json(*args) + as_json.to_json(*args) + end +end diff --git a/ruby/lib/ruby/2.1.0/json/common.rb b/ruby/lib/ruby/2.1.0/json/common.rb new file mode 100644 index 0000000..8fbaa2b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/common.rb @@ -0,0 +1,484 @@ +require 'json/version' +require 'json/generic_object' + +module JSON + class << self + # If _object_ is string-like, parse the string and return the parsed result + # as a Ruby data structure. Otherwise generate a JSON text from the Ruby + # data structure object and return it. + # + # The _opts_ argument is passed through to generate/parse respectively. See + # generate and parse for their documentation. + def [](object, opts = {}) + if object.respond_to? :to_str + JSON.parse(object.to_str, opts) + else + JSON.generate(object, opts) + end + end + + # Returns the JSON parser class that is used by JSON. This is either + # JSON::Ext::Parser or JSON::Pure::Parser. + attr_reader :parser + + # Set the JSON parser class _parser_ to be used by JSON. + def parser=(parser) # :nodoc: + @parser = parser + remove_const :Parser if JSON.const_defined_in?(self, :Parser) + const_set :Parser, parser + end + + # Return the constant located at _path_. The format of _path_ has to be + # either ::A::B::C or A::B::C. In any case, A has to be located at the top + # level (absolute namespace path?). If there doesn't exist a constant at + # the given path, an ArgumentError is raised. + def deep_const_get(path) # :nodoc: + path.to_s.split(/::/).inject(Object) do |p, c| + case + when c.empty? then p + when JSON.const_defined_in?(p, c) then p.const_get(c) + else + begin + p.const_missing(c) + rescue NameError => e + raise ArgumentError, "can't get const #{path}: #{e}" + end + end + end + end + + # Set the module _generator_ to be used by JSON. + def generator=(generator) # :nodoc: + old, $VERBOSE = $VERBOSE, nil + @generator = generator + generator_methods = generator::GeneratorMethods + for const in generator_methods.constants + klass = deep_const_get(const) + modul = generator_methods.const_get(const) + klass.class_eval do + instance_methods(false).each do |m| + m.to_s == 'to_json' and remove_method m + end + include modul + end + end + self.state = generator::State + const_set :State, self.state + const_set :SAFE_STATE_PROTOTYPE, State.new + const_set :FAST_STATE_PROTOTYPE, State.new( + :indent => '', + :space => '', + :object_nl => "", + :array_nl => "", + :max_nesting => false + ) + const_set :PRETTY_STATE_PROTOTYPE, State.new( + :indent => ' ', + :space => ' ', + :object_nl => "\n", + :array_nl => "\n" + ) + ensure + $VERBOSE = old + end + + # Returns the JSON generator module that is used by JSON. This is + # either JSON::Ext::Generator or JSON::Pure::Generator. + attr_reader :generator + + # Returns the JSON generator state class that is used by JSON. This is + # either JSON::Ext::Generator::State or JSON::Pure::Generator::State. + attr_accessor :state + + # This is create identifier, which is used to decide if the _json_create_ + # hook of a class should be called. It defaults to 'json_class'. + attr_accessor :create_id + end + self.create_id = 'json_class' + + NaN = 0.0/0 + + Infinity = 1.0/0 + + MinusInfinity = -Infinity + + # The base exception for JSON errors. + class JSONError < StandardError + def self.wrap(exception) + obj = new("Wrapped(#{exception.class}): #{exception.message.inspect}") + obj.set_backtrace exception.backtrace + obj + end + end + + # This exception is raised if a parser error occurs. + class ParserError < JSONError; end + + # This exception is raised if the nesting of parsed data structures is too + # deep. + class NestingError < ParserError; end + + # :stopdoc: + class CircularDatastructure < NestingError; end + # :startdoc: + + # This exception is raised if a generator or unparser error occurs. + class GeneratorError < JSONError; end + # For backwards compatibility + UnparserError = GeneratorError + + # This exception is raised if the required unicode support is missing on the + # system. Usually this means that the iconv library is not installed. + class MissingUnicodeSupport < JSONError; end + + module_function + + # Parse the JSON document _source_ into a Ruby data structure and return it. + # + # _opts_ can have the following + # keys: + # * *max_nesting*: The maximum depth of nesting allowed in the parsed data + # structures. Disable depth checking with :max_nesting => false. It defaults + # to 100. + # * *allow_nan*: If set to true, allow NaN, Infinity and -Infinity in + # defiance of RFC 4627 to be parsed by the Parser. This option defaults + # to false. + # * *symbolize_names*: If set to true, returns symbols for the names + # (keys) in a JSON object. Otherwise strings are returned. Strings are + # the default. + # * *create_additions*: If set to false, the Parser doesn't create + # additions even if a matching class and create_id was found. This option + # defaults to true. + # * *object_class*: Defaults to Hash + # * *array_class*: Defaults to Array + def parse(source, opts = {}) + Parser.new(source, opts).parse + end + + # Parse the JSON document _source_ into a Ruby data structure and return it. + # The bang version of the parse method defaults to the more dangerous values + # for the _opts_ hash, so be sure only to parse trusted _source_ documents. + # + # _opts_ can have the following keys: + # * *max_nesting*: The maximum depth of nesting allowed in the parsed data + # structures. Enable depth checking with :max_nesting => anInteger. The parse! + # methods defaults to not doing max depth checking: This can be dangerous + # if someone wants to fill up your stack. + # * *allow_nan*: If set to true, allow NaN, Infinity, and -Infinity in + # defiance of RFC 4627 to be parsed by the Parser. This option defaults + # to true. + # * *create_additions*: If set to false, the Parser doesn't create + # additions even if a matching class and create_id was found. This option + # defaults to true. + def parse!(source, opts = {}) + opts = { + :max_nesting => false, + :allow_nan => true + }.update(opts) + Parser.new(source, opts).parse + end + + # Generate a JSON document from the Ruby data structure _obj_ and return + # it. _state_ is * a JSON::State object, + # * or a Hash like object (responding to to_hash), + # * an object convertible into a hash by a to_h method, + # that is used as or to configure a State object. + # + # It defaults to a state object, that creates the shortest possible JSON text + # in one line, checks for circular data structures and doesn't allow NaN, + # Infinity, and -Infinity. + # + # A _state_ hash can have the following keys: + # * *indent*: a string used to indent levels (default: ''), + # * *space*: a string that is put after, a : or , delimiter (default: ''), + # * *space_before*: a string that is put before a : pair delimiter (default: ''), + # * *object_nl*: a string that is put at the end of a JSON object (default: ''), + # * *array_nl*: a string that is put at the end of a JSON array (default: ''), + # * *allow_nan*: true if NaN, Infinity, and -Infinity should be + # generated, otherwise an exception is thrown if these values are + # encountered. This options defaults to false. + # * *max_nesting*: The maximum depth of nesting allowed in the data + # structures from which JSON is to be generated. Disable depth checking + # with :max_nesting => false, it defaults to 100. + # + # See also the fast_generate for the fastest creation method with the least + # amount of sanity checks, and the pretty_generate method for some + # defaults for pretty output. + def generate(obj, opts = nil) + if State === opts + state, opts = opts, nil + else + state = SAFE_STATE_PROTOTYPE.dup + end + if opts + if opts.respond_to? :to_hash + opts = opts.to_hash + elsif opts.respond_to? :to_h + opts = opts.to_h + else + raise TypeError, "can't convert #{opts.class} into Hash" + end + state = state.configure(opts) + end + state.generate(obj) + end + + # :stopdoc: + # I want to deprecate these later, so I'll first be silent about them, and + # later delete them. + alias unparse generate + module_function :unparse + # :startdoc: + + # Generate a JSON document from the Ruby data structure _obj_ and return it. + # This method disables the checks for circles in Ruby objects. + # + # *WARNING*: Be careful not to pass any Ruby data structures with circles as + # _obj_ argument because this will cause JSON to go into an infinite loop. + def fast_generate(obj, opts = nil) + if State === opts + state, opts = opts, nil + else + state = FAST_STATE_PROTOTYPE.dup + end + if opts + if opts.respond_to? :to_hash + opts = opts.to_hash + elsif opts.respond_to? :to_h + opts = opts.to_h + else + raise TypeError, "can't convert #{opts.class} into Hash" + end + state.configure(opts) + end + state.generate(obj) + end + + # :stopdoc: + # I want to deprecate these later, so I'll first be silent about them, and later delete them. + alias fast_unparse fast_generate + module_function :fast_unparse + # :startdoc: + + # Generate a JSON document from the Ruby data structure _obj_ and return it. + # The returned document is a prettier form of the document returned by + # #unparse. + # + # The _opts_ argument can be used to configure the generator. See the + # generate method for a more detailed explanation. + def pretty_generate(obj, opts = nil) + if State === opts + state, opts = opts, nil + else + state = PRETTY_STATE_PROTOTYPE.dup + end + if opts + if opts.respond_to? :to_hash + opts = opts.to_hash + elsif opts.respond_to? :to_h + opts = opts.to_h + else + raise TypeError, "can't convert #{opts.class} into Hash" + end + state.configure(opts) + end + state.generate(obj) + end + + # :stopdoc: + # I want to deprecate these later, so I'll first be silent about them, and later delete them. + alias pretty_unparse pretty_generate + module_function :pretty_unparse + # :startdoc: + + class << self + # The global default options for the JSON.load method: + # :max_nesting: false + # :allow_nan: true + # :quirks_mode: true + attr_accessor :load_default_options + end + self.load_default_options = { + :max_nesting => false, + :allow_nan => true, + :quirks_mode => true, + :create_additions => true, + } + + # Load a ruby data structure from a JSON _source_ and return it. A source can + # either be a string-like object, an IO-like object, or an object responding + # to the read method. If _proc_ was given, it will be called with any nested + # Ruby object as an argument recursively in depth first order. To modify the + # default options pass in the optional _options_ argument as well. + # + # BEWARE: This method is meant to serialise data from trusted user input, + # like from your own database server or clients under your control, it could + # be dangerous to allow untrusted users to pass JSON sources into it. The + # default options for the parser can be changed via the load_default_options + # method. + # + # This method is part of the implementation of the load/dump interface of + # Marshal and YAML. + def load(source, proc = nil, options = {}) + opts = load_default_options.merge options + if source.respond_to? :to_str + source = source.to_str + elsif source.respond_to? :to_io + source = source.to_io.read + elsif source.respond_to?(:read) + source = source.read + end + if opts[:quirks_mode] && (source.nil? || source.empty?) + source = 'null' + end + result = parse(source, opts) + recurse_proc(result, &proc) if proc + result + end + + # Recursively calls passed _Proc_ if the parsed data structure is an _Array_ or _Hash_ + def recurse_proc(result, &proc) + case result + when Array + result.each { |x| recurse_proc x, &proc } + proc.call result + when Hash + result.each { |x, y| recurse_proc x, &proc; recurse_proc y, &proc } + proc.call result + else + proc.call result + end + end + + alias restore load + module_function :restore + + class << self + # The global default options for the JSON.dump method: + # :max_nesting: false + # :allow_nan: true + # :quirks_mode: true + attr_accessor :dump_default_options + end + self.dump_default_options = { + :max_nesting => false, + :allow_nan => true, + :quirks_mode => true, + } + + # Dumps _obj_ as a JSON string, i.e. calls generate on the object and returns + # the result. + # + # If anIO (an IO-like object or an object that responds to the write method) + # was given, the resulting JSON is written to it. + # + # If the number of nested arrays or objects exceeds _limit_, an ArgumentError + # exception is raised. This argument is similar (but not exactly the + # same!) to the _limit_ argument in Marshal.dump. + # + # The default options for the generator can be changed via the + # dump_default_options method. + # + # This method is part of the implementation of the load/dump interface of + # Marshal and YAML. + def dump(obj, anIO = nil, limit = nil) + if anIO and limit.nil? + anIO = anIO.to_io if anIO.respond_to?(:to_io) + unless anIO.respond_to?(:write) + limit = anIO + anIO = nil + end + end + opts = JSON.dump_default_options + limit and opts.update(:max_nesting => limit) + result = generate(obj, opts) + if anIO + anIO.write result + anIO + else + result + end + rescue JSON::NestingError + raise ArgumentError, "exceed depth limit" + end + + # Swap consecutive bytes of _string_ in place. + def self.swap!(string) # :nodoc: + 0.upto(string.size / 2) do |i| + break unless string[2 * i + 1] + string[2 * i], string[2 * i + 1] = string[2 * i + 1], string[2 * i] + end + string + end + + # Shortuct for iconv. + if ::String.method_defined?(:encode) + # Encodes string using Ruby's _String.encode_ + def self.iconv(to, from, string) + string.encode(to, from) + end + else + require 'iconv' + # Encodes string using _iconv_ library + def self.iconv(to, from, string) + Iconv.conv(to, from, string) + end + end + + if ::Object.method(:const_defined?).arity == 1 + def self.const_defined_in?(modul, constant) + modul.const_defined?(constant) + end + else + def self.const_defined_in?(modul, constant) + modul.const_defined?(constant, false) + end + end +end + +module ::Kernel + private + + # Outputs _objs_ to STDOUT as JSON strings in the shortest form, that is in + # one line. + def j(*objs) + objs.each do |obj| + puts JSON::generate(obj, :allow_nan => true, :max_nesting => false) + end + nil + end + + # Outputs _objs_ to STDOUT as JSON strings in a pretty format, with + # indentation and over many lines. + def jj(*objs) + objs.each do |obj| + puts JSON::pretty_generate(obj, :allow_nan => true, :max_nesting => false) + end + nil + end + + # If _object_ is string-like, parse the string and return the parsed result as + # a Ruby data structure. Otherwise, generate a JSON text from the Ruby data + # structure object and return it. + # + # The _opts_ argument is passed through to generate/parse respectively. See + # generate and parse for their documentation. + def JSON(object, *args) + if object.respond_to? :to_str + JSON.parse(object.to_str, args.first) + else + JSON.generate(object, args.first) + end + end +end + +# Extends any Class to include _json_creatable?_ method. +class ::Class + # Returns true if this class can be used to create an instance + # from a serialised JSON string. The class has to implement a class + # method _json_create_ that expects a hash as first parameter. The hash + # should include the required data. + def json_creatable? + respond_to?(:json_create) + end +end diff --git a/ruby/lib/ruby/2.1.0/json/ext.rb b/ruby/lib/ruby/2.1.0/json/ext.rb new file mode 100644 index 0000000..c5f8131 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/ext.rb @@ -0,0 +1,21 @@ +if ENV['SIMPLECOV_COVERAGE'].to_i == 1 + require 'simplecov' + SimpleCov.start do + add_filter "/tests/" + end +end +require 'json/common' + +module JSON + # This module holds all the modules/classes that implement JSON's + # functionality as C extensions. + module Ext + require 'json/ext/parser' + require 'json/ext/generator' + $DEBUG and warn "Using Ext extension for JSON." + JSON.parser = Parser + JSON.generator = Generator + end + + JSON_LOADED = true unless defined?(::JSON::JSON_LOADED) +end diff --git a/ruby/lib/ruby/2.1.0/json/generic_object.rb b/ruby/lib/ruby/2.1.0/json/generic_object.rb new file mode 100644 index 0000000..8b8fd53 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/generic_object.rb @@ -0,0 +1,70 @@ +require 'ostruct' + +module JSON + class GenericObject < OpenStruct + class << self + alias [] new + + def json_creatable? + @json_creatable + end + + attr_writer :json_creatable + + def json_create(data) + data = data.dup + data.delete JSON.create_id + self[data] + end + + def from_hash(object) + case + when object.respond_to?(:to_hash) + result = new + object.to_hash.each do |key, value| + result[key] = from_hash(value) + end + result + when object.respond_to?(:to_ary) + object.to_ary.map { |a| from_hash(a) } + else + object + end + end + + def load(source, proc = nil, opts = {}) + result = ::JSON.load(source, proc, opts.merge(:object_class => self)) + result.nil? ? new : result + end + + def dump(obj, *args) + ::JSON.dump(obj, *args) + end + end + self.json_creatable = false + + def to_hash + table + end + + def [](name) + table[name.to_sym] + end + + def []=(name, value) + __send__ "#{name}=", value + end + + def |(other) + self.class[other.to_hash.merge(to_hash)] + end + + def as_json(*) + { JSON.create_id => self.class.name }.merge to_hash + end + + def to_json(*a) + as_json.to_json(*a) + end + end +end diff --git a/ruby/lib/ruby/2.1.0/json/version.rb b/ruby/lib/ruby/2.1.0/json/version.rb new file mode 100644 index 0000000..47cdcd6 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/json/version.rb @@ -0,0 +1,8 @@ +module JSON + # JSON version + VERSION = '1.8.1' + VERSION_ARRAY = VERSION.split(/\./).map { |x| x.to_i } # :nodoc: + VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: + VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: + VERSION_BUILD = VERSION_ARRAY[2] # :nodoc: +end diff --git a/ruby/lib/ruby/2.1.0/kconv.rb b/ruby/lib/ruby/2.1.0/kconv.rb new file mode 100644 index 0000000..25e04ed --- /dev/null +++ b/ruby/lib/ruby/2.1.0/kconv.rb @@ -0,0 +1,282 @@ +# +# kconv.rb - Kanji Converter. +# +# $Id: kconv.rb 30112 2010-12-07 11:47:39Z naruse $ +# +# ---- +# +# kconv.rb implements the Kconv class for Kanji Converter. Additionally, +# some methods in String classes are added to allow easy conversion. +# + +require 'nkf' + +# +# Kanji Converter for Ruby. +# +module Kconv + # + # Public Constants + # + + #Constant of Encoding + + # Auto-Detect + AUTO = NKF::AUTO + # ISO-2022-JP + JIS = NKF::JIS + # EUC-JP + EUC = NKF::EUC + # Shift_JIS + SJIS = NKF::SJIS + # BINARY + BINARY = NKF::BINARY + # NOCONV + NOCONV = NKF::NOCONV + # ASCII + ASCII = NKF::ASCII + # UTF-8 + UTF8 = NKF::UTF8 + # UTF-16 + UTF16 = NKF::UTF16 + # UTF-32 + UTF32 = NKF::UTF32 + # UNKNOWN + UNKNOWN = NKF::UNKNOWN + + # + # Public Methods + # + + # call-seq: + # Kconv.kconv(str, to_enc, from_enc=nil) + # + # Convert str to to_enc. + # to_enc and from_enc are given as constants of Kconv or Encoding objects. + def kconv(str, to_enc, from_enc=nil) + opt = '' + opt += ' --ic=' + from_enc.to_s if from_enc + opt += ' --oc=' + to_enc.to_s if to_enc + + ::NKF::nkf(opt, str) + end + module_function :kconv + + # + # Encode to + # + + # call-seq: + # Kconv.tojis(str) => string + # + # Convert str to ISO-2022-JP + def tojis(str) + kconv(str, JIS) + end + module_function :tojis + + # call-seq: + # Kconv.toeuc(str) => string + # + # Convert str to EUC-JP + def toeuc(str) + kconv(str, EUC) + end + module_function :toeuc + + # call-seq: + # Kconv.tosjis(str) => string + # + # Convert str to Shift_JIS + def tosjis(str) + kconv(str, SJIS) + end + module_function :tosjis + + # call-seq: + # Kconv.toutf8(str) => string + # + # Convert str to UTF-8 + def toutf8(str) + kconv(str, UTF8) + end + module_function :toutf8 + + # call-seq: + # Kconv.toutf16(str) => string + # + # Convert str to UTF-16 + def toutf16(str) + kconv(str, UTF16) + end + module_function :toutf16 + + # call-seq: + # Kconv.toutf32(str) => string + # + # Convert str to UTF-32 + def toutf32(str) + kconv(str, UTF32) + end + module_function :toutf32 + + # call-seq: + # Kconv.tolocale => string + # + # Convert self to locale encoding + def tolocale(str) + kconv(str, Encoding.locale_charmap) + end + module_function :tolocale + + # + # guess + # + + # call-seq: + # Kconv.guess(str) => encoding + # + # Guess input encoding by NKF.guess + def guess(str) + ::NKF::guess(str) + end + module_function :guess + + # + # isEncoding + # + + # call-seq: + # Kconv.iseuc(str) => true or false + # + # Returns whether input encoding is EUC-JP or not. + # + # *Note* don't expect this return value is MatchData. + def iseuc(str) + str.dup.force_encoding(EUC).valid_encoding? + end + module_function :iseuc + + # call-seq: + # Kconv.issjis(str) => true or false + # + # Returns whether input encoding is Shift_JIS or not. + def issjis(str) + str.dup.force_encoding(SJIS).valid_encoding? + end + module_function :issjis + + # call-seq: + # Kconv.isjis(str) => true or false + # + # Returns whether input encoding is ISO-2022-JP or not. + def isjis(str) + /\A [\t\n\r\x20-\x7E]* + (?: + (?:\x1b \x28 I [\x21-\x7E]* + |\x1b \x28 J [\x21-\x7E]* + |\x1b \x24 @ (?:[\x21-\x7E]{2})* + |\x1b \x24 B (?:[\x21-\x7E]{2})* + |\x1b \x24 \x28 D (?:[\x21-\x7E]{2})* + )* + \x1b \x28 B [\t\n\r\x20-\x7E]* + )* + \z/nox =~ str.dup.force_encoding('BINARY') ? true : false + end + module_function :isjis + + # call-seq: + # Kconv.isutf8(str) => true or false + # + # Returns whether input encoding is UTF-8 or not. + def isutf8(str) + str.dup.force_encoding(UTF8).valid_encoding? + end + module_function :isutf8 +end + +class String + # call-seq: + # String#kconv(to_enc, from_enc) + # + # Convert self to to_enc. + # to_enc and from_enc are given as constants of Kconv or Encoding objects. + def kconv(to_enc, from_enc=nil) + from_enc = self.encoding if !from_enc && self.encoding != Encoding.list[0] + Kconv::kconv(self, to_enc, from_enc) + end + + # + # to Encoding + # + + # call-seq: + # String#tojis => string + # + # Convert self to ISO-2022-JP + def tojis; Kconv.tojis(self) end + + # call-seq: + # String#toeuc => string + # + # Convert self to EUC-JP + def toeuc; Kconv.toeuc(self) end + + # call-seq: + # String#tosjis => string + # + # Convert self to Shift_JIS + def tosjis; Kconv.tosjis(self) end + + # call-seq: + # String#toutf8 => string + # + # Convert self to UTF-8 + def toutf8; Kconv.toutf8(self) end + + # call-seq: + # String#toutf16 => string + # + # Convert self to UTF-16 + def toutf16; Kconv.toutf16(self) end + + # call-seq: + # String#toutf32 => string + # + # Convert self to UTF-32 + def toutf32; Kconv.toutf32(self) end + + # call-seq: + # String#tolocale => string + # + # Convert self to locale encoding + def tolocale; Kconv.tolocale(self) end + + # + # is Encoding + # + + # call-seq: + # String#iseuc => true or false + # + # Returns whether self's encoding is EUC-JP or not. + def iseuc; Kconv.iseuc(self) end + + # call-seq: + # String#issjis => true or false + # + # Returns whether self's encoding is Shift_JIS or not. + def issjis; Kconv.issjis(self) end + + # call-seq: + # String#isjis => true or false + # + # Returns whether self's encoding is ISO-2022-JP or not. + def isjis; Kconv.isjis(self) end + + # call-seq: + # String#isutf8 => true or false + # + # Returns whether self's encoding is UTF-8 or not. + def isutf8; Kconv.isutf8(self) end +end diff --git a/ruby/lib/ruby/2.1.0/logger.rb b/ruby/lib/ruby/2.1.0/logger.rb new file mode 100644 index 0000000..b9595a0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/logger.rb @@ -0,0 +1,844 @@ +# logger.rb - simple logging utility +# Copyright (C) 2000-2003, 2005, 2008, 2011 NAKAMURA, Hiroshi . +# +# Documentation:: NAKAMURA, Hiroshi and Gavin Sinclair +# License:: +# You can redistribute it and/or modify it under the same terms of Ruby's +# license; either the dual license version in 2003, or any later version. +# Revision:: $Id: logger.rb 44203 2013-12-14 05:43:01Z nobu $ +# +# A simple system for logging messages. See Logger for more documentation. + +require 'monitor' + +# == Description +# +# The Logger class provides a simple but sophisticated logging utility that +# you can use to output messages. +# +# The messages have associated levels, such as +INFO+ or +ERROR+ that indicate +# their importance. You can then give the Logger a level, and only messages +# at that level or higher will be printed. +# +# The levels are: +# +# +UNKNOWN+:: An unknown message that should always be logged. +# +FATAL+:: An unhandleable error that results in a program crash. +# +ERROR+:: A handleable error condition. +# +WARN+:: A warning. +# +INFO+:: Generic (useful) information about system operation. +# +DEBUG+:: Low-level information for developers. +# +# For instance, in a production system, you may have your Logger set to +# +INFO+ or even +WARN+. +# When you are developing the system, however, you probably +# want to know about the program's internal state, and would set the Logger to +# +DEBUG+. +# +# *Note*: Logger does not escape or sanitize any messages passed to it. +# Developers should be aware of when potentially malicious data (user-input) +# is passed to Logger, and manually escape the untrusted data: +# +# logger.info("User-input: #{input.dump}") +# logger.info("User-input: %p" % input) +# +# You can use #formatter= for escaping all data. +# +# original_formatter = Logger::Formatter.new +# logger.formatter = proc { |severity, datetime, progname, msg| +# original_formatter.call(severity, datetime, progname, msg.dump) +# } +# logger.info(input) +# +# === Example +# +# This creates a Logger that outputs to the standard output stream, with a +# level of +WARN+: +# +# require 'logger' +# +# logger = Logger.new(STDOUT) +# logger.level = Logger::WARN +# +# logger.debug("Created logger") +# logger.info("Program started") +# logger.warn("Nothing to do!") +# +# path = "a_non_existent_file" +# +# begin +# File.foreach(path) do |line| +# unless line =~ /^(\w+) = (.*)$/ +# logger.error("Line in wrong format: #{line.chomp}") +# end +# end +# rescue => err +# logger.fatal("Caught exception; exiting") +# logger.fatal(err) +# end +# +# Because the Logger's level is set to +WARN+, only the warning, error, and +# fatal messages are recorded. The debug and info messages are silently +# discarded. +# +# === Features +# +# There are several interesting features that Logger provides, like +# auto-rolling of log files, setting the format of log messages, and +# specifying a program name in conjunction with the message. The next section +# shows you how to achieve these things. +# +# +# == HOWTOs +# +# === How to create a logger +# +# The options below give you various choices, in more or less increasing +# complexity. +# +# 1. Create a logger which logs messages to STDERR/STDOUT. +# +# logger = Logger.new(STDERR) +# logger = Logger.new(STDOUT) +# +# 2. Create a logger for the file which has the specified name. +# +# logger = Logger.new('logfile.log') +# +# 3. Create a logger for the specified file. +# +# file = File.open('foo.log', File::WRONLY | File::APPEND) +# # To create new (and to remove old) logfile, add File::CREAT like: +# # file = File.open('foo.log', File::WRONLY | File::APPEND | File::CREAT) +# logger = Logger.new(file) +# +# 4. Create a logger which ages the logfile once it reaches a certain size. +# Leave 10 "old" log files where each file is about 1,024,000 bytes. +# +# logger = Logger.new('foo.log', 10, 1024000) +# +# 5. Create a logger which ages the logfile daily/weekly/monthly. +# +# logger = Logger.new('foo.log', 'daily') +# logger = Logger.new('foo.log', 'weekly') +# logger = Logger.new('foo.log', 'monthly') +# +# === How to log a message +# +# Notice the different methods (+fatal+, +error+, +info+) being used to log +# messages of various levels? Other methods in this family are +warn+ and +# +debug+. +add+ is used below to log a message of an arbitrary (perhaps +# dynamic) level. +# +# 1. Message in a block. +# +# logger.fatal { "Argument 'foo' not given." } +# +# 2. Message as a string. +# +# logger.error "Argument #{@foo} mismatch." +# +# 3. With progname. +# +# logger.info('initialize') { "Initializing..." } +# +# 4. With severity. +# +# logger.add(Logger::FATAL) { 'Fatal error!' } +# +# The block form allows you to create potentially complex log messages, +# but to delay their evaluation until and unless the message is +# logged. For example, if we have the following: +# +# logger.debug { "This is a " + potentially + " expensive operation" } +# +# If the logger's level is +INFO+ or higher, no debug messages will be logged, +# and the entire block will not even be evaluated. Compare to this: +# +# logger.debug("This is a " + potentially + " expensive operation") +# +# Here, the string concatenation is done every time, even if the log +# level is not set to show the debug message. +# +# === How to close a logger +# +# logger.close +# +# === Setting severity threshold +# +# 1. Original interface. +# +# logger.sev_threshold = Logger::WARN +# +# 2. Log4r (somewhat) compatible interface. +# +# logger.level = Logger::INFO +# +# # DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN +# +# == Format +# +# Log messages are rendered in the output stream in a certain format by +# default. The default format and a sample are shown below: +# +# Log format: +# SeverityID, [DateTime #pid] SeverityLabel -- ProgName: message +# +# Log sample: +# I, [1999-03-03T02:34:24.895701 #19074] INFO -- Main: info. +# +# You may change the date and time format via #datetime_format=. +# +# logger.datetime_format = '%Y-%m-%d %H:%M:%S' +# # e.g. "2004-01-03 00:54:26" +# +# Or, you may change the overall format via the #formatter= method. +# +# logger.formatter = proc do |severity, datetime, progname, msg| +# "#{datetime}: #{msg}\n" +# end +# # e.g. "2005-09-22 08:51:08 +0900: hello world" +# +class Logger + VERSION = "1.2.7" + _, name, rev = %w$Id: logger.rb 44203 2013-12-14 05:43:01Z nobu $ + if name + name = name.chomp(",v") + else + name = File.basename(__FILE__) + end + rev ||= "v#{VERSION}" + ProgName = "#{name}/#{rev}" + + class Error < RuntimeError # :nodoc: + end + # not used after 1.2.7. just for compat. + class ShiftingError < Error # :nodoc: + end + + # Logging severity. + module Severity + # Low-level information, mostly for developers. + DEBUG = 0 + # Generic (useful) information about system operation. + INFO = 1 + # A warning. + WARN = 2 + # A handleable error condition. + ERROR = 3 + # An unhandleable error that results in a program crash. + FATAL = 4 + # An unknown message that should always be logged. + UNKNOWN = 5 + end + include Severity + + # Logging severity threshold (e.g. Logger::INFO). + attr_accessor :level + + # Program name to include in log messages. + attr_accessor :progname + + # Set date-time format. + # + # +datetime_format+:: A string suitable for passing to +strftime+. + def datetime_format=(datetime_format) + @default_formatter.datetime_format = datetime_format + end + + # Returns the date format being used. See #datetime_format= + def datetime_format + @default_formatter.datetime_format + end + + # Logging formatter, as a +Proc+ that will take four arguments and + # return the formatted message. The arguments are: + # + # +severity+:: The Severity of the log message. + # +time+:: A Time instance representing when the message was logged. + # +progname+:: The #progname configured, or passed to the logger method. + # +msg+:: The _Object_ the user passed to the log message; not necessarily a + # String. + # + # The block should return an Object that can be written to the logging + # device via +write+. The default formatter is used when no formatter is + # set. + attr_accessor :formatter + + alias sev_threshold level + alias sev_threshold= level= + + # Returns +true+ iff the current severity level allows for the printing of + # +DEBUG+ messages. + def debug?; @level <= DEBUG; end + + # Returns +true+ iff the current severity level allows for the printing of + # +INFO+ messages. + def info?; @level <= INFO; end + + # Returns +true+ iff the current severity level allows for the printing of + # +WARN+ messages. + def warn?; @level <= WARN; end + + # Returns +true+ iff the current severity level allows for the printing of + # +ERROR+ messages. + def error?; @level <= ERROR; end + + # Returns +true+ iff the current severity level allows for the printing of + # +FATAL+ messages. + def fatal?; @level <= FATAL; end + + # + # :call-seq: + # Logger.new(name, shift_age = 7, shift_size = 1048576) + # Logger.new(name, shift_age = 'weekly') + # + # === Args + # + # +logdev+:: + # The log device. This is a filename (String) or IO object (typically + # +STDOUT+, +STDERR+, or an open file). + # +shift_age+:: + # Number of old log files to keep, *or* frequency of rotation (+daily+, + # +weekly+ or +monthly+). + # +shift_size+:: + # Maximum logfile size (only applies when +shift_age+ is a number). + # + # === Description + # + # Create an instance. + # + def initialize(logdev, shift_age = 0, shift_size = 1048576) + @progname = nil + @level = DEBUG + @default_formatter = Formatter.new + @formatter = nil + @logdev = nil + if logdev + @logdev = LogDevice.new(logdev, :shift_age => shift_age, + :shift_size => shift_size) + end + end + + # + # :call-seq: + # Logger#add(severity, message = nil, progname = nil) { ... } + # + # === Args + # + # +severity+:: + # Severity. Constants are defined in Logger namespace: +DEBUG+, +INFO+, + # +WARN+, +ERROR+, +FATAL+, or +UNKNOWN+. + # +message+:: + # The log message. A String or Exception. + # +progname+:: + # Program name string. Can be omitted. Treated as a message if no + # +message+ and +block+ are given. + # +block+:: + # Can be omitted. Called to get a message string if +message+ is nil. + # + # === Return + # + # When the given severity is not high enough (for this particular logger), + # log no message, and return +true+. + # + # === Description + # + # Log a message if the given severity is high enough. This is the generic + # logging method. Users will be more inclined to use #debug, #info, #warn, + # #error, and #fatal. + # + # Message format: +message+ can be any object, but it has to be + # converted to a String in order to log it. Generally, +inspect+ is used + # if the given object is not a String. + # A special case is an +Exception+ object, which will be printed in detail, + # including message, class, and backtrace. See #msg2str for the + # implementation if required. + # + # === Bugs + # + # * Logfile is not locked. + # * Append open does not need to lock file. + # * If the OS supports multi I/O, records possibly may be mixed. + # + def add(severity, message = nil, progname = nil, &block) + severity ||= UNKNOWN + if @logdev.nil? or severity < @level + return true + end + progname ||= @progname + if message.nil? + if block_given? + message = yield + else + message = progname + progname = @progname + end + end + @logdev.write( + format_message(format_severity(severity), Time.now, progname, message)) + true + end + alias log add + + # + # Dump given message to the log device without any formatting. If no log + # device exists, return +nil+. + # + def <<(msg) + unless @logdev.nil? + @logdev.write(msg) + end + end + + # + # Log a +DEBUG+ message. + # + # See #info for more information. + # + def debug(progname = nil, &block) + add(DEBUG, nil, progname, &block) + end + + # + # :call-seq: + # info(message) + # info(progname, &block) + # + # Log an +INFO+ message. + # + # +message+:: The message to log; does not need to be a String. + # +progname+:: In the block form, this is the #progname to use in the + # log message. The default can be set with #progname=. + # +block+:: Evaluates to the message to log. This is not evaluated unless + # the logger's level is sufficient to log the message. This + # allows you to create potentially expensive logging messages that + # are only called when the logger is configured to show them. + # + # === Examples + # + # logger.info("MainApp") { "Received connection from #{ip}" } + # # ... + # logger.info "Waiting for input from user" + # # ... + # logger.info { "User typed #{input}" } + # + # You'll probably stick to the second form above, unless you want to provide a + # program name (which you can do with #progname= as well). + # + # === Return + # + # See #add. + # + def info(progname = nil, &block) + add(INFO, nil, progname, &block) + end + + # + # Log a +WARN+ message. + # + # See #info for more information. + # + def warn(progname = nil, &block) + add(WARN, nil, progname, &block) + end + + # + # Log an +ERROR+ message. + # + # See #info for more information. + # + def error(progname = nil, &block) + add(ERROR, nil, progname, &block) + end + + # + # Log a +FATAL+ message. + # + # See #info for more information. + # + def fatal(progname = nil, &block) + add(FATAL, nil, progname, &block) + end + + # + # Log an +UNKNOWN+ message. This will be printed no matter what the logger's + # level is. + # + # See #info for more information. + # + def unknown(progname = nil, &block) + add(UNKNOWN, nil, progname, &block) + end + + # + # Close the logging device. + # + def close + @logdev.close if @logdev + end + +private + + # Severity label for logging (max 5 chars). + SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY) + + def format_severity(severity) + SEV_LABEL[severity] || 'ANY' + end + + def format_message(severity, datetime, progname, msg) + (@formatter || @default_formatter).call(severity, datetime, progname, msg) + end + + + # Default formatter for log messages. + class Formatter + Format = "%s, [%s#%d] %5s -- %s: %s\n" + + attr_accessor :datetime_format + + def initialize + @datetime_format = nil + end + + def call(severity, time, progname, msg) + Format % [severity[0..0], format_datetime(time), $$, severity, progname, + msg2str(msg)] + end + + private + + def format_datetime(time) + if @datetime_format.nil? + time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec + else + time.strftime(@datetime_format) + end + end + + def msg2str(msg) + case msg + when ::String + msg + when ::Exception + "#{ msg.message } (#{ msg.class })\n" << + (msg.backtrace || []).join("\n") + else + msg.inspect + end + end + end + + + # Device used for logging messages. + class LogDevice + attr_reader :dev + attr_reader :filename + + class LogDeviceMutex + include MonitorMixin + end + + def initialize(log = nil, opt = {}) + @dev = @filename = @shift_age = @shift_size = nil + @mutex = LogDeviceMutex.new + if log.respond_to?(:write) and log.respond_to?(:close) + @dev = log + else + @dev = open_logfile(log) + @dev.sync = true + @filename = log + @shift_age = opt[:shift_age] || 7 + @shift_size = opt[:shift_size] || 1048576 + end + end + + def write(message) + begin + @mutex.synchronize do + if @shift_age and @dev.respond_to?(:stat) + begin + check_shift_log + rescue + warn("log shifting failed. #{$!}") + end + end + begin + @dev.write(message) + rescue + warn("log writing failed. #{$!}") + end + end + rescue Exception => ignored + warn("log writing failed. #{ignored}") + end + end + + def close + begin + @mutex.synchronize do + @dev.close rescue nil + end + rescue Exception + @dev.close rescue nil + end + end + + private + + def open_logfile(filename) + begin + open(filename, (File::WRONLY | File::APPEND)) + rescue Errno::ENOENT + create_logfile(filename) + end + end + + def create_logfile(filename) + begin + logdev = open(filename, (File::WRONLY | File::APPEND | File::CREAT | File::EXCL)) + logdev.flock(File::LOCK_EX) + logdev.sync = true + add_log_header(logdev) + logdev.flock(File::LOCK_UN) + rescue Errno::EEXIST + # file is created by another process + logdev = open_logfile(filename) + logdev.sync = true + end + logdev + end + + def add_log_header(file) + file.write( + "# Logfile created on %s by %s\n" % [Time.now.to_s, Logger::ProgName] + ) if file.size == 0 + end + + SiD = 24 * 60 * 60 + + def check_shift_log + if @shift_age.is_a?(Integer) + # Note: always returns false if '0'. + if @filename && (@shift_age > 0) && (@dev.stat.size > @shift_size) + lock_shift_log { shift_log_age } + end + else + now = Time.now + period_end = previous_period_end(now) + if @dev.stat.mtime <= period_end + lock_shift_log { shift_log_period(period_end) } + end + end + end + + if /mswin|mingw/ =~ RUBY_PLATFORM + def lock_shift_log + yield + end + else + def lock_shift_log + retry_limit = 8 + retry_sleep = 0.1 + begin + File.open(@filename, File::WRONLY | File::APPEND) do |lock| + lock.flock(File::LOCK_EX) # inter-process locking. will be unlocked at closing file + if File.identical?(@filename, lock) and File.identical?(lock, @dev) + yield # log shifting + else + # log shifted by another process (i-node before locking and i-node after locking are different) + @dev.close rescue nil + @dev = open_logfile(@filename) + @dev.sync = true + end + end + rescue Errno::ENOENT + # @filename file would not exist right after #rename and before #create_logfile + if retry_limit <= 0 + warn("log rotation inter-process lock failed. #{$!}") + else + sleep retry_sleep + retry_limit -= 1 + retry_sleep *= 2 + retry + end + end + rescue + warn("log rotation inter-process lock failed. #{$!}") + end + end + + def shift_log_age + (@shift_age-3).downto(0) do |i| + if FileTest.exist?("#{@filename}.#{i}") + File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}") + end + end + @dev.close rescue nil + File.rename("#{@filename}", "#{@filename}.0") + @dev = create_logfile(@filename) + return true + end + + def shift_log_period(period_end) + postfix = period_end.strftime("%Y%m%d") # YYYYMMDD + age_file = "#{@filename}.#{postfix}" + if FileTest.exist?(age_file) + # try to avoid filename crash caused by Timestamp change. + idx = 0 + # .99 can be overridden; avoid too much file search with 'loop do' + while idx < 100 + idx += 1 + age_file = "#{@filename}.#{postfix}.#{idx}" + break unless FileTest.exist?(age_file) + end + end + @dev.close rescue nil + File.rename("#{@filename}", age_file) + @dev = create_logfile(@filename) + return true + end + + def previous_period_end(now) + case @shift_age + when /^daily$/ + eod(now - 1 * SiD) + when /^weekly$/ + eod(now - ((now.wday + 1) * SiD)) + when /^monthly$/ + eod(now - now.mday * SiD) + else + now + end + end + + def eod(t) + Time.mktime(t.year, t.month, t.mday, 23, 59, 59) + end + end + + + # + # == Description + # + # Logger::Application --- Add logging support to your application. + # + # == Usage + # + # 1. Define your application class as a sub-class of this class. + # 2. Override the +run+ method in your class to do many things. + # 3. Instantiate it and invoke #start. + # + # == Example + # + # class FooApp < Logger::Application + # def initialize(foo_app, application_specific, arguments) + # super('FooApp') # Name of the application. + # end + # + # def run + # ... + # log(WARN, 'warning', 'my_method1') + # ... + # @log.error('my_method2') { 'Error!' } + # ... + # end + # end + # + # status = FooApp.new(....).start + # + class Application + include Logger::Severity + + # Name of the application given at initialize. + attr_reader :appname + + # + # :call-seq: + # Logger::Application.new(appname = '') + # + # == Args + # + # +appname+:: Name of the application. + # + # == Description + # + # Create an instance. Log device is +STDERR+ by default. This can be + # changed with #set_log. + # + def initialize(appname = nil) + @appname = appname + @log = Logger.new(STDERR) + @log.progname = @appname + @level = @log.level + end + + # + # Start the application. Return the status code. + # + def start + status = -1 + begin + log(INFO, "Start of #{ @appname }.") + status = run + rescue + log(FATAL, "Detected an exception. Stopping ... #{$!} (#{$!.class})\n" << $@.join("\n")) + ensure + log(INFO, "End of #{ @appname }. (status: #{ status.to_s })") + end + status + end + + # Logger for this application. See the class Logger for an explanation. + def logger + @log + end + + # + # Sets the logger for this application. See the class Logger for an + # explanation. + # + def logger=(logger) + @log = logger + @log.progname = @appname + @log.level = @level + end + + # + # Sets the log device for this application. See Logger.new for + # an explanation of the arguments. + # + def set_log(logdev, shift_age = 0, shift_size = 1024000) + @log = Logger.new(logdev, shift_age, shift_size) + @log.progname = @appname + @log.level = @level + end + + def log=(logdev) + set_log(logdev) + end + + # + # Set the logging threshold, just like Logger#level=. + # + def level=(level) + @level = level + @log.level = @level + end + + # + # See Logger#add. This application's +appname+ is used. + # + def log(severity, message = nil, &block) + @log.add(severity, message, @appname, &block) if @log + end + + private + + def run + # TODO: should be an NotImplementedError + raise RuntimeError.new('Method run must be defined in the derived class.') + end + end +end diff --git a/ruby/lib/ruby/2.1.0/mathn.rb b/ruby/lib/ruby/2.1.0/mathn.rb new file mode 100644 index 0000000..aae4620 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/mathn.rb @@ -0,0 +1,324 @@ +#-- +# $Release Version: 0.5 $ +# $Revision: 1.1.1.1.4.1 $ + +## +# = mathn +# +# mathn is a library for changing the way Ruby does math. If you need +# more precise rounding with multiple division or exponentiation +# operations, then mathn is the right tool. +# +# Without mathn: +# +# 3 / 2 => 1 # Integer +# +# With mathn: +# +# 3 / 2 => 3/2 # Rational +# +# mathn features late rounding and lacks truncation of intermediate results: +# +# Without mathn: +# +# 20 / 9 * 3 * 14 / 7 * 3 / 2 # => 18 +# +# With mathn: +# +# 20 / 9 * 3 * 14 / 7 * 3 / 2 # => 20 +# +# +# When you require 'mathn', the libraries for Prime, CMath, Matrix and Vector +# are also loaded. +# +# == Copyright +# +# Author: Keiju ISHITSUKA (SHL Japan Inc.) +#-- +# class Numeric follows to make this documentation findable in a reasonable +# location + +class Numeric; end + +require "cmath.rb" +require "matrix.rb" +require "prime.rb" + +require "mathn/rational" +require "mathn/complex" + +unless defined?(Math.exp!) + Object.instance_eval{remove_const :Math} + Math = CMath # :nodoc: +end + +## +# When mathn is required, Fixnum's division and exponentiation are enhanced to +# return more precise values from mathematical expressions. +# +# 2/3*3 # => 0 +# require 'mathn' +# 2/3*3 # => 2 + +class Fixnum + remove_method :/ + + ## + # +/+ defines the Rational division for Fixnum. + # + # 1/3 # => (1/3) + + alias / quo + + alias power! ** unless method_defined? :power! + + ## + # Exponentiate by +other+ + + def ** (other) + if self < 0 && other.round != other + Complex(self, 0.0) ** other + else + power!(other) + end + end + +end + +## +# When mathn is required Bignum's division and exponentiation are enhanced to +# return more precise values from mathematical expressions. + +class Bignum + remove_method :/ + + ## + # +/+ defines the Rational division for Bignum. + # + # (2**72) / ((2**70) * 3) # => 4/3 + + alias / quo + + alias power! ** unless method_defined? :power! + + ## + # Exponentiate by +other+ + + def ** (other) + if self < 0 && other.round != other + Complex(self, 0.0) ** other + else + power!(other) + end + end + +end + +## +# When mathn is required Rational is changed to simplify the use of Rational +# operations. +# +# Normal behaviour: +# +# Rational.new!(1,3) ** 2 # => Rational(1, 9) +# (1 / 3) ** 2 # => 0 +# +# require 'mathn' behaviour: +# +# (1 / 3) ** 2 # => 1/9 + +class Rational + remove_method :** + + ## + # Exponentiate by +other+ + # + # (1/3) ** 2 # => 1/9 + + def ** (other) + if other.kind_of?(Rational) + other2 = other + if self < 0 + return Complex(self, 0.0) ** other + elsif other == 0 + return Rational(1,1) + elsif self == 0 + return Rational(0,1) + elsif self == 1 + return Rational(1,1) + end + + npd = numerator.prime_division + dpd = denominator.prime_division + if other < 0 + other = -other + npd, dpd = dpd, npd + end + + for elm in npd + elm[1] = elm[1] * other + if !elm[1].kind_of?(Integer) and elm[1].denominator != 1 + return Float(self) ** other2 + end + elm[1] = elm[1].to_i + end + + for elm in dpd + elm[1] = elm[1] * other + if !elm[1].kind_of?(Integer) and elm[1].denominator != 1 + return Float(self) ** other2 + end + elm[1] = elm[1].to_i + end + + num = Integer.from_prime_division(npd) + den = Integer.from_prime_division(dpd) + + Rational(num,den) + + elsif other.kind_of?(Integer) + if other > 0 + num = numerator ** other + den = denominator ** other + elsif other < 0 + num = denominator ** -other + den = numerator ** -other + elsif other == 0 + num = 1 + den = 1 + end + Rational(num, den) + elsif other.kind_of?(Float) + Float(self) ** other + else + x , y = other.coerce(self) + x ** y + end + end +end + +## +# When mathn is required, the Math module changes as follows: +# +# Standard Math module behaviour: +# Math.sqrt(4/9) # => 0.0 +# Math.sqrt(4.0/9.0) # => 0.666666666666667 +# Math.sqrt(- 4/9) # => Errno::EDOM: Numerical argument out of domain - sqrt +# +# After require 'mathn', this is changed to: +# +# require 'mathn' +# Math.sqrt(4/9) # => 2/3 +# Math.sqrt(4.0/9.0) # => 0.666666666666667 +# Math.sqrt(- 4/9) # => Complex(0, 2/3) + +module Math + remove_method(:sqrt) + + ## + # Computes the square root of +a+. It makes use of Complex and + # Rational to have no rounding errors if possible. + # + # Math.sqrt(4/9) # => 2/3 + # Math.sqrt(- 4/9) # => Complex(0, 2/3) + # Math.sqrt(4.0/9.0) # => 0.666666666666667 + + def sqrt(a) + if a.kind_of?(Complex) + abs = sqrt(a.real*a.real + a.imag*a.imag) +# if not abs.kind_of?(Rational) +# return a**Rational(1,2) +# end + x = sqrt((a.real + abs)/Rational(2)) + y = sqrt((-a.real + abs)/Rational(2)) +# if !(x.kind_of?(Rational) and y.kind_of?(Rational)) +# return a**Rational(1,2) +# end + if a.imag >= 0 + Complex(x, y) + else + Complex(x, -y) + end + elsif a.respond_to?(:nan?) and a.nan? + a + elsif a >= 0 + rsqrt(a) + else + Complex(0,rsqrt(-a)) + end + end + + ## + # Compute square root of a non negative number. This method is + # internally used by +Math.sqrt+. + + def rsqrt(a) + if a.kind_of?(Float) + sqrt!(a) + elsif a.kind_of?(Rational) + rsqrt(a.numerator)/rsqrt(a.denominator) + else + src = a + max = 2 ** 32 + byte_a = [src & 0xffffffff] + # ruby's bug + while (src >= max) and (src >>= 32) + byte_a.unshift src & 0xffffffff + end + + answer = 0 + main = 0 + side = 0 + for elm in byte_a + main = (main << 32) + elm + side <<= 16 + if answer != 0 + if main * 4 < side * side + applo = main.div(side) + else + applo = ((sqrt!(side * side + 4 * main) - side)/2.0).to_i + 1 + end + else + applo = sqrt!(main).to_i + 1 + end + + while (x = (side + applo) * applo) > main + applo -= 1 + end + main -= x + answer = (answer << 16) + applo + side += applo * 2 + end + if main == 0 + answer + else + sqrt!(a) + end + end + end + + class << self + remove_method(:sqrt) + end + module_function :sqrt + module_function :rsqrt +end + +## +# When mathn is required, Float is changed to handle Complex numbers. + +class Float + alias power! ** + + ## + # Exponentiate by +other+ + + def ** (other) + if self < 0 && other.round != other + Complex(self, 0.0) ** other + else + power!(other) + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/matrix.rb b/ruby/lib/ruby/2.1.0/matrix.rb new file mode 100644 index 0000000..b20fd94 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/matrix.rb @@ -0,0 +1,1881 @@ +# encoding: utf-8 +# +# = matrix.rb +# +# An implementation of Matrix and Vector classes. +# +# See classes Matrix and Vector for documentation. +# +# Current Maintainer:: Marc-André Lafortune +# Original Author:: Keiju ISHITSUKA +# Original Documentation:: Gavin Sinclair (sourced from Ruby in a Nutshell (Matsumoto, O'Reilly)) +## + +require "e2mmap.rb" + +module ExceptionForMatrix # :nodoc: + extend Exception2MessageMapper + def_e2message(TypeError, "wrong argument type %s (expected %s)") + def_e2message(ArgumentError, "Wrong # of arguments(%d for %d)") + + def_exception("ErrDimensionMismatch", "\#{self.name} dimension mismatch") + def_exception("ErrNotRegular", "Not Regular Matrix") + def_exception("ErrOperationNotDefined", "Operation(%s) can\\'t be defined: %s op %s") + def_exception("ErrOperationNotImplemented", "Sorry, Operation(%s) not implemented: %s op %s") +end + +# +# The +Matrix+ class represents a mathematical matrix. It provides methods for creating +# matrices, operating on them arithmetically and algebraically, +# and determining their mathematical properties (trace, rank, inverse, determinant). +# +# == Method Catalogue +# +# To create a matrix: +# * Matrix[*rows] +# * Matrix.[](*rows) +# * Matrix.rows(rows, copy = true) +# * Matrix.columns(columns) +# * Matrix.build(row_count, column_count, &block) +# * Matrix.diagonal(*values) +# * Matrix.scalar(n, value) +# * Matrix.identity(n) +# * Matrix.unit(n) +# * Matrix.I(n) +# * Matrix.zero(n) +# * Matrix.row_vector(row) +# * Matrix.column_vector(column) +# +# To access Matrix elements/columns/rows/submatrices/properties: +# * #[](i, j) +# * #row_count (row_size) +# * #column_count (column_size) +# * #row(i) +# * #column(j) +# * #collect +# * #map +# * #each +# * #each_with_index +# * #find_index +# * #minor(*param) +# +# Properties of a matrix: +# * #diagonal? +# * #empty? +# * #hermitian? +# * #lower_triangular? +# * #normal? +# * #orthogonal? +# * #permutation? +# * #real? +# * #regular? +# * #singular? +# * #square? +# * #symmetric? +# * #unitary? +# * #upper_triangular? +# * #zero? +# +# Matrix arithmetic: +# * #*(m) +# * #+(m) +# * #-(m) +# * #/(m) +# * #inverse +# * #inv +# * #** +# +# Matrix functions: +# * #determinant +# * #det +# * #rank +# * #round +# * #trace +# * #tr +# * #transpose +# * #t +# +# Matrix decompositions: +# * #eigen +# * #eigensystem +# * #lup +# * #lup_decomposition +# +# Complex arithmetic: +# * conj +# * conjugate +# * imag +# * imaginary +# * real +# * rect +# * rectangular +# +# Conversion to other data types: +# * #coerce(other) +# * #row_vectors +# * #column_vectors +# * #to_a +# +# String representations: +# * #to_s +# * #inspect +# +class Matrix + include Enumerable + include ExceptionForMatrix + autoload :EigenvalueDecomposition, "matrix/eigenvalue_decomposition" + autoload :LUPDecomposition, "matrix/lup_decomposition" + + # instance creations + private_class_method :new + attr_reader :rows + protected :rows + + # + # Creates a matrix where each argument is a row. + # Matrix[ [25, 93], [-1, 66] ] + # => 25 93 + # -1 66 + # + def Matrix.[](*rows) + rows(rows, false) + end + + # + # Creates a matrix where +rows+ is an array of arrays, each of which is a row + # of the matrix. If the optional argument +copy+ is false, use the given + # arrays as the internal structure of the matrix without copying. + # Matrix.rows([[25, 93], [-1, 66]]) + # => 25 93 + # -1 66 + # + def Matrix.rows(rows, copy = true) + rows = convert_to_array(rows) + rows.map! do |row| + convert_to_array(row, copy) + end + size = (rows[0] || []).size + rows.each do |row| + raise ErrDimensionMismatch, "row size differs (#{row.size} should be #{size})" unless row.size == size + end + new rows, size + end + + # + # Creates a matrix using +columns+ as an array of column vectors. + # Matrix.columns([[25, 93], [-1, 66]]) + # => 25 -1 + # 93 66 + # + def Matrix.columns(columns) + rows(columns, false).transpose + end + + # + # Creates a matrix of size +row_count+ x +column_count+. + # It fills the values by calling the given block, + # passing the current row and column. + # Returns an enumerator if no block is given. + # + # m = Matrix.build(2, 4) {|row, col| col - row } + # => Matrix[[0, 1, 2, 3], [-1, 0, 1, 2]] + # m = Matrix.build(3) { rand } + # => a 3x3 matrix with random elements + # + def Matrix.build(row_count, column_count = row_count) + row_count = CoercionHelper.coerce_to_int(row_count) + column_count = CoercionHelper.coerce_to_int(column_count) + raise ArgumentError if row_count < 0 || column_count < 0 + return to_enum :build, row_count, column_count unless block_given? + rows = Array.new(row_count) do |i| + Array.new(column_count) do |j| + yield i, j + end + end + new rows, column_count + end + + # + # Creates a matrix where the diagonal elements are composed of +values+. + # Matrix.diagonal(9, 5, -3) + # => 9 0 0 + # 0 5 0 + # 0 0 -3 + # + def Matrix.diagonal(*values) + size = values.size + rows = Array.new(size) {|j| + row = Array.new(size, 0) + row[j] = values[j] + row + } + new rows + end + + # + # Creates an +n+ by +n+ diagonal matrix where each diagonal element is + # +value+. + # Matrix.scalar(2, 5) + # => 5 0 + # 0 5 + # + def Matrix.scalar(n, value) + diagonal(*Array.new(n, value)) + end + + # + # Creates an +n+ by +n+ identity matrix. + # Matrix.identity(2) + # => 1 0 + # 0 1 + # + def Matrix.identity(n) + scalar(n, 1) + end + class << Matrix + alias unit identity + alias I identity + end + + # + # Creates a zero matrix. + # Matrix.zero(2) + # => 0 0 + # 0 0 + # + def Matrix.zero(row_count, column_count = row_count) + rows = Array.new(row_count){Array.new(column_count, 0)} + new rows, column_count + end + + # + # Creates a single-row matrix where the values of that row are as given in + # +row+. + # Matrix.row_vector([4,5,6]) + # => 4 5 6 + # + def Matrix.row_vector(row) + row = convert_to_array(row) + new [row] + end + + # + # Creates a single-column matrix where the values of that column are as given + # in +column+. + # Matrix.column_vector([4,5,6]) + # => 4 + # 5 + # 6 + # + def Matrix.column_vector(column) + column = convert_to_array(column) + new [column].transpose, 1 + end + + # + # Creates a empty matrix of +row_count+ x +column_count+. + # At least one of +row_count+ or +column_count+ must be 0. + # + # m = Matrix.empty(2, 0) + # m == Matrix[ [], [] ] + # => true + # n = Matrix.empty(0, 3) + # n == Matrix.columns([ [], [], [] ]) + # => true + # m * n + # => Matrix[[0, 0, 0], [0, 0, 0]] + # + def Matrix.empty(row_count = 0, column_count = 0) + raise ArgumentError, "One size must be 0" if column_count != 0 && row_count != 0 + raise ArgumentError, "Negative size" if column_count < 0 || row_count < 0 + + new([[]]*row_count, column_count) + end + + # + # Matrix.new is private; use Matrix.rows, columns, [], etc... to create. + # + def initialize(rows, column_count = rows[0].size) + # No checking is done at this point. rows must be an Array of Arrays. + # column_count must be the size of the first row, if there is one, + # otherwise it *must* be specified and can be any integer >= 0 + @rows = rows + @column_count = column_count + end + + def new_matrix(rows, column_count = rows[0].size) # :nodoc: + self.class.send(:new, rows, column_count) # bypass privacy of Matrix.new + end + private :new_matrix + + # + # Returns element (+i+,+j+) of the matrix. That is: row +i+, column +j+. + # + def [](i, j) + @rows.fetch(i){return nil}[j] + end + alias element [] + alias component [] + + def []=(i, j, v) + @rows[i][j] = v + end + alias set_element []= + alias set_component []= + private :[]=, :set_element, :set_component + + # + # Returns the number of rows. + # + def row_count + @rows.size + end + + alias_method :row_size, :row_count + # + # Returns the number of columns. + # + attr_reader :column_count + alias_method :column_size, :column_count + + # + # Returns row vector number +i+ of the matrix as a Vector (starting at 0 like + # an array). When a block is given, the elements of that vector are iterated. + # + def row(i, &block) # :yield: e + if block_given? + @rows.fetch(i){return self}.each(&block) + self + else + Vector.elements(@rows.fetch(i){return nil}) + end + end + + # + # Returns column vector number +j+ of the matrix as a Vector (starting at 0 + # like an array). When a block is given, the elements of that vector are + # iterated. + # + def column(j) # :yield: e + if block_given? + return self if j >= column_count || j < -column_count + row_count.times do |i| + yield @rows[i][j] + end + self + else + return nil if j >= column_count || j < -column_count + col = Array.new(row_count) {|i| + @rows[i][j] + } + Vector.elements(col, false) + end + end + + # + # Returns a matrix that is the result of iteration of the given block over all + # elements of the matrix. + # Matrix[ [1,2], [3,4] ].collect { |e| e**2 } + # => 1 4 + # 9 16 + # + def collect(&block) # :yield: e + return to_enum(:collect) unless block_given? + rows = @rows.collect{|row| row.collect(&block)} + new_matrix rows, column_count + end + alias map collect + + # + # Yields all elements of the matrix, starting with those of the first row, + # or returns an Enumerator is no block given. + # Elements can be restricted by passing an argument: + # * :all (default): yields all elements + # * :diagonal: yields only elements on the diagonal + # * :off_diagonal: yields all elements except on the diagonal + # * :lower: yields only elements on or below the diagonal + # * :strict_lower: yields only elements below the diagonal + # * :strict_upper: yields only elements above the diagonal + # * :upper: yields only elements on or above the diagonal + # + # Matrix[ [1,2], [3,4] ].each { |e| puts e } + # # => prints the numbers 1 to 4 + # Matrix[ [1,2], [3,4] ].each(:strict_lower).to_a # => [3] + # + def each(which = :all) # :yield: e + return to_enum :each, which unless block_given? + last = column_count - 1 + case which + when :all + block = Proc.new + @rows.each do |row| + row.each(&block) + end + when :diagonal + @rows.each_with_index do |row, row_index| + yield row.fetch(row_index){return self} + end + when :off_diagonal + @rows.each_with_index do |row, row_index| + column_count.times do |col_index| + yield row[col_index] unless row_index == col_index + end + end + when :lower + @rows.each_with_index do |row, row_index| + 0.upto([row_index, last].min) do |col_index| + yield row[col_index] + end + end + when :strict_lower + @rows.each_with_index do |row, row_index| + [row_index, column_count].min.times do |col_index| + yield row[col_index] + end + end + when :strict_upper + @rows.each_with_index do |row, row_index| + (row_index+1).upto(last) do |col_index| + yield row[col_index] + end + end + when :upper + @rows.each_with_index do |row, row_index| + row_index.upto(last) do |col_index| + yield row[col_index] + end + end + else + raise ArgumentError, "expected #{which.inspect} to be one of :all, :diagonal, :off_diagonal, :lower, :strict_lower, :strict_upper or :upper" + end + self + end + + # + # Same as #each, but the row index and column index in addition to the element + # + # Matrix[ [1,2], [3,4] ].each_with_index do |e, row, col| + # puts "#{e} at #{row}, #{col}" + # end + # # => Prints: + # # 1 at 0, 0 + # # 2 at 0, 1 + # # 3 at 1, 0 + # # 4 at 1, 1 + # + def each_with_index(which = :all) # :yield: e, row, column + return to_enum :each_with_index, which unless block_given? + last = column_count - 1 + case which + when :all + @rows.each_with_index do |row, row_index| + row.each_with_index do |e, col_index| + yield e, row_index, col_index + end + end + when :diagonal + @rows.each_with_index do |row, row_index| + yield row.fetch(row_index){return self}, row_index, row_index + end + when :off_diagonal + @rows.each_with_index do |row, row_index| + column_count.times do |col_index| + yield row[col_index], row_index, col_index unless row_index == col_index + end + end + when :lower + @rows.each_with_index do |row, row_index| + 0.upto([row_index, last].min) do |col_index| + yield row[col_index], row_index, col_index + end + end + when :strict_lower + @rows.each_with_index do |row, row_index| + [row_index, column_count].min.times do |col_index| + yield row[col_index], row_index, col_index + end + end + when :strict_upper + @rows.each_with_index do |row, row_index| + (row_index+1).upto(last) do |col_index| + yield row[col_index], row_index, col_index + end + end + when :upper + @rows.each_with_index do |row, row_index| + row_index.upto(last) do |col_index| + yield row[col_index], row_index, col_index + end + end + else + raise ArgumentError, "expected #{which.inspect} to be one of :all, :diagonal, :off_diagonal, :lower, :strict_lower, :strict_upper or :upper" + end + self + end + + SELECTORS = {all: true, diagonal: true, off_diagonal: true, lower: true, strict_lower: true, strict_upper: true, upper: true}.freeze + # + # :call-seq: + # index(value, selector = :all) -> [row, column] + # index(selector = :all){ block } -> [row, column] + # index(selector = :all) -> an_enumerator + # + # The index method is specialized to return the index as [row, column] + # It also accepts an optional +selector+ argument, see #each for details. + # + # Matrix[ [1,2], [3,4] ].index(&:even?) # => [0, 1] + # Matrix[ [1,1], [1,1] ].index(1, :strict_lower) # => [1, 0] + # + def index(*args) + raise ArgumentError, "wrong number of arguments(#{args.size} for 0-2)" if args.size > 2 + which = (args.size == 2 || SELECTORS.include?(args.last)) ? args.pop : :all + return to_enum :find_index, which, *args unless block_given? || args.size == 1 + if args.size == 1 + value = args.first + each_with_index(which) do |e, row_index, col_index| + return row_index, col_index if e == value + end + else + each_with_index(which) do |e, row_index, col_index| + return row_index, col_index if yield e + end + end + nil + end + alias_method :find_index, :index + # + # Returns a section of the matrix. The parameters are either: + # * start_row, nrows, start_col, ncols; OR + # * row_range, col_range + # + # Matrix.diagonal(9, 5, -3).minor(0..1, 0..2) + # => 9 0 0 + # 0 5 0 + # + # Like Array#[], negative indices count backward from the end of the + # row or column (-1 is the last element). Returns nil if the starting + # row or column is greater than row_count or column_count respectively. + # + def minor(*param) + case param.size + when 2 + row_range, col_range = param + from_row = row_range.first + from_row += row_count if from_row < 0 + to_row = row_range.end + to_row += row_count if to_row < 0 + to_row += 1 unless row_range.exclude_end? + size_row = to_row - from_row + + from_col = col_range.first + from_col += column_count if from_col < 0 + to_col = col_range.end + to_col += column_count if to_col < 0 + to_col += 1 unless col_range.exclude_end? + size_col = to_col - from_col + when 4 + from_row, size_row, from_col, size_col = param + return nil if size_row < 0 || size_col < 0 + from_row += row_count if from_row < 0 + from_col += column_count if from_col < 0 + else + raise ArgumentError, param.inspect + end + + return nil if from_row > row_count || from_col > column_count || from_row < 0 || from_col < 0 + rows = @rows[from_row, size_row].collect{|row| + row[from_col, size_col] + } + new_matrix rows, [column_count - from_col, size_col].min + end + + #-- + # TESTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Returns +true+ is this is a diagonal matrix. + # Raises an error if matrix is not square. + # + def diagonal? + Matrix.Raise ErrDimensionMismatch unless square? + each(:off_diagonal).all?(&:zero?) + end + + # + # Returns +true+ if this is an empty matrix, i.e. if the number of rows + # or the number of columns is 0. + # + def empty? + column_count == 0 || row_count == 0 + end + + # + # Returns +true+ is this is an hermitian matrix. + # Raises an error if matrix is not square. + # + def hermitian? + Matrix.Raise ErrDimensionMismatch unless square? + each_with_index(:upper).all? do |e, row, col| + e == rows[col][row].conj + end + end + + # + # Returns +true+ is this is a lower triangular matrix. + # + def lower_triangular? + each(:strict_upper).all?(&:zero?) + end + + # + # Returns +true+ is this is a normal matrix. + # Raises an error if matrix is not square. + # + def normal? + Matrix.Raise ErrDimensionMismatch unless square? + rows.each_with_index do |row_i, i| + rows.each_with_index do |row_j, j| + s = 0 + rows.each_with_index do |row_k, k| + s += row_i[k] * row_j[k].conj - row_k[i].conj * row_k[j] + end + return false unless s == 0 + end + end + true + end + + # + # Returns +true+ is this is an orthogonal matrix + # Raises an error if matrix is not square. + # + def orthogonal? + Matrix.Raise ErrDimensionMismatch unless square? + rows.each_with_index do |row, i| + column_count.times do |j| + s = 0 + row_count.times do |k| + s += row[k] * rows[k][j] + end + return false unless s == (i == j ? 1 : 0) + end + end + true + end + + # + # Returns +true+ is this is a permutation matrix + # Raises an error if matrix is not square. + # + def permutation? + Matrix.Raise ErrDimensionMismatch unless square? + cols = Array.new(column_count) + rows.each_with_index do |row, i| + found = false + row.each_with_index do |e, j| + if e == 1 + return false if found || cols[j] + found = cols[j] = true + elsif e != 0 + return false + end + end + return false unless found + end + true + end + + # + # Returns +true+ if all entries of the matrix are real. + # + def real? + all?(&:real?) + end + + # + # Returns +true+ if this is a regular (i.e. non-singular) matrix. + # + def regular? + not singular? + end + + # + # Returns +true+ is this is a singular matrix. + # + def singular? + determinant == 0 + end + + # + # Returns +true+ is this is a square matrix. + # + def square? + column_count == row_count + end + + # + # Returns +true+ is this is a symmetric matrix. + # Raises an error if matrix is not square. + # + def symmetric? + Matrix.Raise ErrDimensionMismatch unless square? + each_with_index(:strict_upper) do |e, row, col| + return false if e != rows[col][row] + end + true + end + + # + # Returns +true+ is this is a unitary matrix + # Raises an error if matrix is not square. + # + def unitary? + Matrix.Raise ErrDimensionMismatch unless square? + rows.each_with_index do |row, i| + column_count.times do |j| + s = 0 + row_count.times do |k| + s += row[k].conj * rows[k][j] + end + return false unless s == (i == j ? 1 : 0) + end + end + true + end + + # + # Returns +true+ is this is an upper triangular matrix. + # + def upper_triangular? + each(:strict_lower).all?(&:zero?) + end + + # + # Returns +true+ is this is a matrix with only zero elements + # + def zero? + all?(&:zero?) + end + + #-- + # OBJECT METHODS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Returns +true+ if and only if the two matrices contain equal elements. + # + def ==(other) + return false unless Matrix === other && + column_count == other.column_count # necessary for empty matrices + rows == other.rows + end + + def eql?(other) + return false unless Matrix === other && + column_count == other.column_count # necessary for empty matrices + rows.eql? other.rows + end + + # + # Returns a clone of the matrix, so that the contents of each do not reference + # identical objects. + # There should be no good reason to do this since Matrices are immutable. + # + def clone + new_matrix @rows.map(&:dup), column_count + end + + # + # Returns a hash-code for the matrix. + # + def hash + @rows.hash + end + + #-- + # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Matrix multiplication. + # Matrix[[2,4], [6,8]] * Matrix.identity(2) + # => 2 4 + # 6 8 + # + def *(m) # m is matrix or vector or number + case(m) + when Numeric + rows = @rows.collect {|row| + row.collect {|e| e * m } + } + return new_matrix rows, column_count + when Vector + m = self.class.column_vector(m) + r = self * m + return r.column(0) + when Matrix + Matrix.Raise ErrDimensionMismatch if column_count != m.row_count + + rows = Array.new(row_count) {|i| + Array.new(m.column_count) {|j| + (0 ... column_count).inject(0) do |vij, k| + vij + self[i, k] * m[k, j] + end + } + } + return new_matrix rows, m.column_count + else + return apply_through_coercion(m, __method__) + end + end + + # + # Matrix addition. + # Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]] + # => 6 0 + # -4 12 + # + def +(m) + case m + when Numeric + Matrix.Raise ErrOperationNotDefined, "+", self.class, m.class + when Vector + m = self.class.column_vector(m) + when Matrix + else + return apply_through_coercion(m, __method__) + end + + Matrix.Raise ErrDimensionMismatch unless row_count == m.row_count and column_count == m.column_count + + rows = Array.new(row_count) {|i| + Array.new(column_count) {|j| + self[i, j] + m[i, j] + } + } + new_matrix rows, column_count + end + + # + # Matrix subtraction. + # Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]] + # => -8 2 + # 8 1 + # + def -(m) + case m + when Numeric + Matrix.Raise ErrOperationNotDefined, "-", self.class, m.class + when Vector + m = self.class.column_vector(m) + when Matrix + else + return apply_through_coercion(m, __method__) + end + + Matrix.Raise ErrDimensionMismatch unless row_count == m.row_count and column_count == m.column_count + + rows = Array.new(row_count) {|i| + Array.new(column_count) {|j| + self[i, j] - m[i, j] + } + } + new_matrix rows, column_count + end + + # + # Matrix division (multiplication by the inverse). + # Matrix[[7,6], [3,9]] / Matrix[[2,9], [3,1]] + # => -7 1 + # -3 -6 + # + def /(other) + case other + when Numeric + rows = @rows.collect {|row| + row.collect {|e| e / other } + } + return new_matrix rows, column_count + when Matrix + return self * other.inverse + else + return apply_through_coercion(other, __method__) + end + end + + # + # Returns the inverse of the matrix. + # Matrix[[-1, -1], [0, -1]].inverse + # => -1 1 + # 0 -1 + # + def inverse + Matrix.Raise ErrDimensionMismatch unless square? + self.class.I(row_count).send(:inverse_from, self) + end + alias inv inverse + + def inverse_from(src) # :nodoc: + last = row_count - 1 + a = src.to_a + + 0.upto(last) do |k| + i = k + akk = a[k][k].abs + (k+1).upto(last) do |j| + v = a[j][k].abs + if v > akk + i = j + akk = v + end + end + Matrix.Raise ErrNotRegular if akk == 0 + if i != k + a[i], a[k] = a[k], a[i] + @rows[i], @rows[k] = @rows[k], @rows[i] + end + akk = a[k][k] + + 0.upto(last) do |ii| + next if ii == k + q = a[ii][k].quo(akk) + a[ii][k] = 0 + + (k + 1).upto(last) do |j| + a[ii][j] -= a[k][j] * q + end + 0.upto(last) do |j| + @rows[ii][j] -= @rows[k][j] * q + end + end + + (k+1).upto(last) do |j| + a[k][j] = a[k][j].quo(akk) + end + 0.upto(last) do |j| + @rows[k][j] = @rows[k][j].quo(akk) + end + end + self + end + private :inverse_from + + # + # Matrix exponentiation. + # Equivalent to multiplying the matrix by itself N times. + # Non integer exponents will be handled by diagonalizing the matrix. + # + # Matrix[[7,6], [3,9]] ** 2 + # => 67 96 + # 48 99 + # + def ** (other) + case other + when Integer + x = self + if other <= 0 + x = self.inverse + return self.class.identity(self.column_count) if other == 0 + other = -other + end + z = nil + loop do + z = z ? z * x : x if other[0] == 1 + return z if (other >>= 1).zero? + x *= x + end + when Numeric + v, d, v_inv = eigensystem + v * self.class.diagonal(*d.each(:diagonal).map{|e| e ** other}) * v_inv + else + Matrix.Raise ErrOperationNotDefined, "**", self.class, other.class + end + end + + #-- + # MATRIX FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Returns the determinant of the matrix. + # + # Beware that using Float values can yield erroneous results + # because of their lack of precision. + # Consider using exact types like Rational or BigDecimal instead. + # + # Matrix[[7,6], [3,9]].determinant + # => 45 + # + def determinant + Matrix.Raise ErrDimensionMismatch unless square? + m = @rows + case row_count + # Up to 4x4, give result using Laplacian expansion by minors. + # This will typically be faster, as well as giving good results + # in case of Floats + when 0 + +1 + when 1 + + m[0][0] + when 2 + + m[0][0] * m[1][1] - m[0][1] * m[1][0] + when 3 + m0, m1, m2 = m + + m0[0] * m1[1] * m2[2] - m0[0] * m1[2] * m2[1] \ + - m0[1] * m1[0] * m2[2] + m0[1] * m1[2] * m2[0] \ + + m0[2] * m1[0] * m2[1] - m0[2] * m1[1] * m2[0] + when 4 + m0, m1, m2, m3 = m + + m0[0] * m1[1] * m2[2] * m3[3] - m0[0] * m1[1] * m2[3] * m3[2] \ + - m0[0] * m1[2] * m2[1] * m3[3] + m0[0] * m1[2] * m2[3] * m3[1] \ + + m0[0] * m1[3] * m2[1] * m3[2] - m0[0] * m1[3] * m2[2] * m3[1] \ + - m0[1] * m1[0] * m2[2] * m3[3] + m0[1] * m1[0] * m2[3] * m3[2] \ + + m0[1] * m1[2] * m2[0] * m3[3] - m0[1] * m1[2] * m2[3] * m3[0] \ + - m0[1] * m1[3] * m2[0] * m3[2] + m0[1] * m1[3] * m2[2] * m3[0] \ + + m0[2] * m1[0] * m2[1] * m3[3] - m0[2] * m1[0] * m2[3] * m3[1] \ + - m0[2] * m1[1] * m2[0] * m3[3] + m0[2] * m1[1] * m2[3] * m3[0] \ + + m0[2] * m1[3] * m2[0] * m3[1] - m0[2] * m1[3] * m2[1] * m3[0] \ + - m0[3] * m1[0] * m2[1] * m3[2] + m0[3] * m1[0] * m2[2] * m3[1] \ + + m0[3] * m1[1] * m2[0] * m3[2] - m0[3] * m1[1] * m2[2] * m3[0] \ + - m0[3] * m1[2] * m2[0] * m3[1] + m0[3] * m1[2] * m2[1] * m3[0] + else + # For bigger matrices, use an efficient and general algorithm. + # Currently, we use the Gauss-Bareiss algorithm + determinant_bareiss + end + end + alias_method :det, :determinant + + # + # Private. Use Matrix#determinant + # + # Returns the determinant of the matrix, using + # Bareiss' multistep integer-preserving gaussian elimination. + # It has the same computational cost order O(n^3) as standard Gaussian elimination. + # Intermediate results are fraction free and of lower complexity. + # A matrix of Integers will have thus intermediate results that are also Integers, + # with smaller bignums (if any), while a matrix of Float will usually have + # intermediate results with better precision. + # + def determinant_bareiss + size = row_count + last = size - 1 + a = to_a + no_pivot = Proc.new{ return 0 } + sign = +1 + pivot = 1 + size.times do |k| + previous_pivot = pivot + if (pivot = a[k][k]) == 0 + switch = (k+1 ... size).find(no_pivot) {|row| + a[row][k] != 0 + } + a[switch], a[k] = a[k], a[switch] + pivot = a[k][k] + sign = -sign + end + (k+1).upto(last) do |i| + ai = a[i] + (k+1).upto(last) do |j| + ai[j] = (pivot * ai[j] - ai[k] * a[k][j]) / previous_pivot + end + end + end + sign * pivot + end + private :determinant_bareiss + + # + # deprecated; use Matrix#determinant + # + def determinant_e + warn "#{caller(1)[0]}: warning: Matrix#determinant_e is deprecated; use #determinant" + determinant + end + alias det_e determinant_e + + # + # Returns the rank of the matrix. + # Beware that using Float values can yield erroneous results + # because of their lack of precision. + # Consider using exact types like Rational or BigDecimal instead. + # + # Matrix[[7,6], [3,9]].rank + # => 2 + # + def rank + # We currently use Bareiss' multistep integer-preserving gaussian elimination + # (see comments on determinant) + a = to_a + last_column = column_count - 1 + last_row = row_count - 1 + pivot_row = 0 + previous_pivot = 1 + 0.upto(last_column) do |k| + switch_row = (pivot_row .. last_row).find {|row| + a[row][k] != 0 + } + if switch_row + a[switch_row], a[pivot_row] = a[pivot_row], a[switch_row] unless pivot_row == switch_row + pivot = a[pivot_row][k] + (pivot_row+1).upto(last_row) do |i| + ai = a[i] + (k+1).upto(last_column) do |j| + ai[j] = (pivot * ai[j] - ai[k] * a[pivot_row][j]) / previous_pivot + end + end + pivot_row += 1 + previous_pivot = pivot + end + end + pivot_row + end + + # + # deprecated; use Matrix#rank + # + def rank_e + warn "#{caller(1)[0]}: warning: Matrix#rank_e is deprecated; use #rank" + rank + end + + # Returns a matrix with entries rounded to the given precision + # (see Float#round) + # + def round(ndigits=0) + map{|e| e.round(ndigits)} + end + + # + # Returns the trace (sum of diagonal elements) of the matrix. + # Matrix[[7,6], [3,9]].trace + # => 16 + # + def trace + Matrix.Raise ErrDimensionMismatch unless square? + (0...column_count).inject(0) do |tr, i| + tr + @rows[i][i] + end + end + alias tr trace + + # + # Returns the transpose of the matrix. + # Matrix[[1,2], [3,4], [5,6]] + # => 1 2 + # 3 4 + # 5 6 + # Matrix[[1,2], [3,4], [5,6]].transpose + # => 1 3 5 + # 2 4 6 + # + def transpose + return self.class.empty(column_count, 0) if row_count.zero? + new_matrix @rows.transpose, row_count + end + alias t transpose + + #-- + # DECOMPOSITIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + #++ + + # + # Returns the Eigensystem of the matrix; see +EigenvalueDecomposition+. + # m = Matrix[[1, 2], [3, 4]] + # v, d, v_inv = m.eigensystem + # d.diagonal? # => true + # v.inv == v_inv # => true + # (v * d * v_inv).round(5) == m # => true + # + def eigensystem + EigenvalueDecomposition.new(self) + end + alias eigen eigensystem + + # + # Returns the LUP decomposition of the matrix; see +LUPDecomposition+. + # a = Matrix[[1, 2], [3, 4]] + # l, u, p = a.lup + # l.lower_triangular? # => true + # u.upper_triangular? # => true + # p.permutation? # => true + # l * u == p * a # => true + # a.lup.solve([2, 5]) # => Vector[(1/1), (1/2)] + # + def lup + LUPDecomposition.new(self) + end + alias lup_decomposition lup + + #-- + # COMPLEX ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + #++ + + # + # Returns the conjugate of the matrix. + # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]] + # => 1+2i i 0 + # 1 2 3 + # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].conjugate + # => 1-2i -i 0 + # 1 2 3 + # + def conjugate + collect(&:conjugate) + end + alias conj conjugate + + # + # Returns the imaginary part of the matrix. + # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]] + # => 1+2i i 0 + # 1 2 3 + # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].imaginary + # => 2i i 0 + # 0 0 0 + # + def imaginary + collect(&:imaginary) + end + alias imag imaginary + + # + # Returns the real part of the matrix. + # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]] + # => 1+2i i 0 + # 1 2 3 + # Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].real + # => 1 0 0 + # 1 2 3 + # + def real + collect(&:real) + end + + # + # Returns an array containing matrices corresponding to the real and imaginary + # parts of the matrix + # + # m.rect == [m.real, m.imag] # ==> true for all matrices m + # + def rect + [real, imag] + end + alias rectangular rect + + #-- + # CONVERTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # The coerce method provides support for Ruby type coercion. + # This coercion mechanism is used by Ruby to handle mixed-type + # numeric operations: it is intended to find a compatible common + # type between the two operands of the operator. + # See also Numeric#coerce. + # + def coerce(other) + case other + when Numeric + return Scalar.new(other), self + else + raise TypeError, "#{self.class} can't be coerced into #{other.class}" + end + end + + # + # Returns an array of the row vectors of the matrix. See Vector. + # + def row_vectors + Array.new(row_count) {|i| + row(i) + } + end + + # + # Returns an array of the column vectors of the matrix. See Vector. + # + def column_vectors + Array.new(column_count) {|i| + column(i) + } + end + + # + # Returns an array of arrays that describe the rows of the matrix. + # + def to_a + @rows.collect(&:dup) + end + + def elements_to_f + warn "#{caller(1)[0]}: warning: Matrix#elements_to_f is deprecated, use map(&:to_f)" + map(&:to_f) + end + + def elements_to_i + warn "#{caller(1)[0]}: warning: Matrix#elements_to_i is deprecated, use map(&:to_i)" + map(&:to_i) + end + + def elements_to_r + warn "#{caller(1)[0]}: warning: Matrix#elements_to_r is deprecated, use map(&:to_r)" + map(&:to_r) + end + + #-- + # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Overrides Object#to_s + # + def to_s + if empty? + "#{self.class}.empty(#{row_count}, #{column_count})" + else + "#{self.class}[" + @rows.collect{|row| + "[" + row.collect{|e| e.to_s}.join(", ") + "]" + }.join(", ")+"]" + end + end + + # + # Overrides Object#inspect + # + def inspect + if empty? + "#{self.class}.empty(#{row_count}, #{column_count})" + else + "#{self.class}#{@rows.inspect}" + end + end + + # Private helper modules + + module ConversionHelper # :nodoc: + # + # Converts the obj to an Array. If copy is set to true + # a copy of obj will be made if necessary. + # + def convert_to_array(obj, copy = false) # :nodoc: + case obj + when Array + copy ? obj.dup : obj + when Vector + obj.to_a + else + begin + converted = obj.to_ary + rescue Exception => e + raise TypeError, "can't convert #{obj.class} into an Array (#{e.message})" + end + raise TypeError, "#{obj.class}#to_ary should return an Array" unless converted.is_a? Array + converted + end + end + private :convert_to_array + end + + extend ConversionHelper + + module CoercionHelper # :nodoc: + # + # Applies the operator +oper+ with argument +obj+ + # through coercion of +obj+ + # + def apply_through_coercion(obj, oper) + coercion = obj.coerce(self) + raise TypeError unless coercion.is_a?(Array) && coercion.length == 2 + coercion[0].public_send(oper, coercion[1]) + rescue + raise TypeError, "#{obj.inspect} can't be coerced into #{self.class}" + end + private :apply_through_coercion + + # + # Helper method to coerce a value into a specific class. + # Raises a TypeError if the coercion fails or the returned value + # is not of the right class. + # (from Rubinius) + # + def self.coerce_to(obj, cls, meth) # :nodoc: + return obj if obj.kind_of?(cls) + + begin + ret = obj.__send__(meth) + rescue Exception => e + raise TypeError, "Coercion error: #{obj.inspect}.#{meth} => #{cls} failed:\n" \ + "(#{e.message})" + end + raise TypeError, "Coercion error: obj.#{meth} did NOT return a #{cls} (was #{ret.class})" unless ret.kind_of? cls + ret + end + + def self.coerce_to_int(obj) + coerce_to(obj, Integer, :to_int) + end + end + + include CoercionHelper + + # Private CLASS + + class Scalar < Numeric # :nodoc: + include ExceptionForMatrix + include CoercionHelper + + def initialize(value) + @value = value + end + + # ARITHMETIC + def +(other) + case other + when Numeric + Scalar.new(@value + other) + when Vector, Matrix + Scalar.Raise ErrOperationNotDefined, "+", @value.class, other.class + else + apply_through_coercion(other, __method__) + end + end + + def -(other) + case other + when Numeric + Scalar.new(@value - other) + when Vector, Matrix + Scalar.Raise ErrOperationNotDefined, "-", @value.class, other.class + else + apply_through_coercion(other, __method__) + end + end + + def *(other) + case other + when Numeric + Scalar.new(@value * other) + when Vector, Matrix + other.collect{|e| @value * e} + else + apply_through_coercion(other, __method__) + end + end + + def / (other) + case other + when Numeric + Scalar.new(@value / other) + when Vector + Scalar.Raise ErrOperationNotDefined, "/", @value.class, other.class + when Matrix + self * other.inverse + else + apply_through_coercion(other, __method__) + end + end + + def ** (other) + case other + when Numeric + Scalar.new(@value ** other) + when Vector + Scalar.Raise ErrOperationNotDefined, "**", @value.class, other.class + when Matrix + #other.powered_by(self) + Scalar.Raise ErrOperationNotImplemented, "**", @value.class, other.class + else + apply_through_coercion(other, __method__) + end + end + end + +end + + +# +# The +Vector+ class represents a mathematical vector, which is useful in its own right, and +# also constitutes a row or column of a Matrix. +# +# == Method Catalogue +# +# To create a Vector: +# * Vector.[](*array) +# * Vector.elements(array, copy = true) +# +# To access elements: +# * #[](i) +# +# To enumerate the elements: +# * #each2(v) +# * #collect2(v) +# +# Vector arithmetic: +# * #*(x) "is matrix or number" +# * #+(v) +# * #-(v) +# +# Vector functions: +# * #inner_product(v) +# * #cross_product(v) +# * #collect +# * #magnitude +# * #map +# * #map2(v) +# * #norm +# * #normalize +# * #r +# * #size +# +# Conversion to other data types: +# * #covector +# * #to_a +# * #coerce(other) +# +# String representations: +# * #to_s +# * #inspect +# +class Vector + include ExceptionForMatrix + include Enumerable + include Matrix::CoercionHelper + extend Matrix::ConversionHelper + #INSTANCE CREATION + + private_class_method :new + attr_reader :elements + protected :elements + + # + # Creates a Vector from a list of elements. + # Vector[7, 4, ...] + # + def Vector.[](*array) + new convert_to_array(array, false) + end + + # + # Creates a vector from an Array. The optional second argument specifies + # whether the array itself or a copy is used internally. + # + def Vector.elements(array, copy = true) + new convert_to_array(array, copy) + end + + # + # Vector.new is private; use Vector[] or Vector.elements to create. + # + def initialize(array) + # No checking is done at this point. + @elements = array + end + + # ACCESSING + + # + # Returns element number +i+ (starting at zero) of the vector. + # + def [](i) + @elements[i] + end + alias element [] + alias component [] + + def []=(i, v) + @elements[i]= v + end + alias set_element []= + alias set_component []= + private :[]=, :set_element, :set_component + + # + # Returns the number of elements in the vector. + # + def size + @elements.size + end + + #-- + # ENUMERATIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Iterate over the elements of this vector + # + def each(&block) + return to_enum(:each) unless block_given? + @elements.each(&block) + self + end + + # + # Iterate over the elements of this vector and +v+ in conjunction. + # + def each2(v) # :yield: e1, e2 + raise TypeError, "Integer is not like Vector" if v.kind_of?(Integer) + Vector.Raise ErrDimensionMismatch if size != v.size + return to_enum(:each2, v) unless block_given? + size.times do |i| + yield @elements[i], v[i] + end + self + end + + # + # Collects (as in Enumerable#collect) over the elements of this vector and +v+ + # in conjunction. + # + def collect2(v) # :yield: e1, e2 + raise TypeError, "Integer is not like Vector" if v.kind_of?(Integer) + Vector.Raise ErrDimensionMismatch if size != v.size + return to_enum(:collect2, v) unless block_given? + Array.new(size) do |i| + yield @elements[i], v[i] + end + end + + #-- + # COMPARING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Returns +true+ iff the two vectors have the same elements in the same order. + # + def ==(other) + return false unless Vector === other + @elements == other.elements + end + + def eql?(other) + return false unless Vector === other + @elements.eql? other.elements + end + + # + # Return a copy of the vector. + # + def clone + self.class.elements(@elements) + end + + # + # Return a hash-code for the vector. + # + def hash + @elements.hash + end + + #-- + # ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Multiplies the vector by +x+, where +x+ is a number or another vector. + # + def *(x) + case x + when Numeric + els = @elements.collect{|e| e * x} + self.class.elements(els, false) + when Matrix + Matrix.column_vector(self) * x + when Vector + Vector.Raise ErrOperationNotDefined, "*", self.class, x.class + else + apply_through_coercion(x, __method__) + end + end + + # + # Vector addition. + # + def +(v) + case v + when Vector + Vector.Raise ErrDimensionMismatch if size != v.size + els = collect2(v) {|v1, v2| + v1 + v2 + } + self.class.elements(els, false) + when Matrix + Matrix.column_vector(self) + v + else + apply_through_coercion(v, __method__) + end + end + + # + # Vector subtraction. + # + def -(v) + case v + when Vector + Vector.Raise ErrDimensionMismatch if size != v.size + els = collect2(v) {|v1, v2| + v1 - v2 + } + self.class.elements(els, false) + when Matrix + Matrix.column_vector(self) - v + else + apply_through_coercion(v, __method__) + end + end + + # + # Vector division. + # + def /(x) + case x + when Numeric + els = @elements.collect{|e| e / x} + self.class.elements(els, false) + when Matrix, Vector + Vector.Raise ErrOperationNotDefined, "/", self.class, x.class + else + apply_through_coercion(x, __method__) + end + end + + #-- + # VECTOR FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Returns the inner product of this vector with the other. + # Vector[4,7].inner_product Vector[10,1] => 47 + # + def inner_product(v) + Vector.Raise ErrDimensionMismatch if size != v.size + + p = 0 + each2(v) {|v1, v2| + p += v1 * v2.conj + } + p + end + + # + # Returns the cross product of this vector with the other. + # Vector[1, 0, 0].cross_product Vector[0, 1, 0] => Vector[0, 0, 1] + # + def cross_product(v) + Vector.Raise ErrDimensionMismatch unless size == v.size && v.size == 3 + Vector[ v[2]*@elements[1] - v[1]*@elements[2], + v[0]*@elements[2] - v[2]*@elements[0], + v[1]*@elements[0] - v[0]*@elements[1] ] + end + + # + # Like Array#collect. + # + def collect(&block) # :yield: e + return to_enum(:collect) unless block_given? + els = @elements.collect(&block) + self.class.elements(els, false) + end + alias map collect + + # + # Returns the modulus (Pythagorean distance) of the vector. + # Vector[5,8,2].r => 9.643650761 + # + def magnitude + Math.sqrt(@elements.inject(0) {|v, e| v + e.abs2}) + end + alias r magnitude + alias norm magnitude + + # + # Like Vector#collect2, but returns a Vector instead of an Array. + # + def map2(v, &block) # :yield: e1, e2 + return to_enum(:map2, v) unless block_given? + els = collect2(v, &block) + self.class.elements(els, false) + end + + class ZeroVectorError < StandardError + end + # + # Returns a new vector with the same direction but with norm 1. + # v = Vector[5,8,2].normalize + # # => Vector[0.5184758473652127, 0.8295613557843402, 0.20739033894608505] + # v.norm => 1.0 + # + def normalize + n = magnitude + raise ZeroVectorError, "Zero vectors can not be normalized" if n == 0 + self / n + end + + #-- + # CONVERTING + #++ + + # + # Creates a single-row matrix from this vector. + # + def covector + Matrix.row_vector(self) + end + + # + # Returns the elements of the vector in an array. + # + def to_a + @elements.dup + end + + def elements_to_f + warn "#{caller(1)[0]}: warning: Vector#elements_to_f is deprecated" + map(&:to_f) + end + + def elements_to_i + warn "#{caller(1)[0]}: warning: Vector#elements_to_i is deprecated" + map(&:to_i) + end + + def elements_to_r + warn "#{caller(1)[0]}: warning: Vector#elements_to_r is deprecated" + map(&:to_r) + end + + # + # The coerce method provides support for Ruby type coercion. + # This coercion mechanism is used by Ruby to handle mixed-type + # numeric operations: it is intended to find a compatible common + # type between the two operands of the operator. + # See also Numeric#coerce. + # + def coerce(other) + case other + when Numeric + return Matrix::Scalar.new(other), self + else + raise TypeError, "#{self.class} can't be coerced into #{other.class}" + end + end + + #-- + # PRINTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + #++ + + # + # Overrides Object#to_s + # + def to_s + "Vector[" + @elements.join(", ") + "]" + end + + # + # Overrides Object#inspect + # + def inspect + "Vector" + @elements.inspect + end +end diff --git a/ruby/lib/ruby/2.1.0/matrix/eigenvalue_decomposition.rb b/ruby/lib/ruby/2.1.0/matrix/eigenvalue_decomposition.rb new file mode 100644 index 0000000..0dd9d42 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/matrix/eigenvalue_decomposition.rb @@ -0,0 +1,882 @@ +class Matrix + # Adapted from JAMA: http://math.nist.gov/javanumerics/jama/ + + # Eigenvalues and eigenvectors of a real matrix. + # + # Computes the eigenvalues and eigenvectors of a matrix A. + # + # If A is diagonalizable, this provides matrices V and D + # such that A = V*D*V.inv, where D is the diagonal matrix with entries + # equal to the eigenvalues and V is formed by the eigenvectors. + # + # If A is symmetric, then V is orthogonal and thus A = V*D*V.t + + class EigenvalueDecomposition + + # Constructs the eigenvalue decomposition for a square matrix +A+ + # + def initialize(a) + # @d, @e: Arrays for internal storage of eigenvalues. + # @v: Array for internal storage of eigenvectors. + # @h: Array for internal storage of nonsymmetric Hessenberg form. + raise TypeError, "Expected Matrix but got #{a.class}" unless a.is_a?(Matrix) + @size = a.row_count + @d = Array.new(@size, 0) + @e = Array.new(@size, 0) + + if (@symmetric = a.symmetric?) + @v = a.to_a + tridiagonalize + diagonalize + else + @v = Array.new(@size) { Array.new(@size, 0) } + @h = a.to_a + @ort = Array.new(@size, 0) + reduce_to_hessenberg + hessenberg_to_real_schur + end + end + + # Returns the eigenvector matrix +V+ + # + def eigenvector_matrix + Matrix.send :new, build_eigenvectors.transpose + end + alias v eigenvector_matrix + + # Returns the inverse of the eigenvector matrix +V+ + # + def eigenvector_matrix_inv + r = Matrix.send :new, build_eigenvectors + r = r.transpose.inverse unless @symmetric + r + end + alias v_inv eigenvector_matrix_inv + + # Returns the eigenvalues in an array + # + def eigenvalues + values = @d.dup + @e.each_with_index{|imag, i| values[i] = Complex(values[i], imag) unless imag == 0} + values + end + + # Returns an array of the eigenvectors + # + def eigenvectors + build_eigenvectors.map{|ev| Vector.send :new, ev} + end + + # Returns the block diagonal eigenvalue matrix +D+ + # + def eigenvalue_matrix + Matrix.diagonal(*eigenvalues) + end + alias d eigenvalue_matrix + + # Returns [eigenvector_matrix, eigenvalue_matrix, eigenvector_matrix_inv] + # + def to_ary + [v, d, v_inv] + end + alias_method :to_a, :to_ary + + private + def build_eigenvectors + # JAMA stores complex eigenvectors in a strange way + # See http://web.archive.org/web/20111016032731/http://cio.nist.gov/esd/emaildir/lists/jama/msg01021.html + @e.each_with_index.map do |imag, i| + if imag == 0 + Array.new(@size){|j| @v[j][i]} + elsif imag > 0 + Array.new(@size){|j| Complex(@v[j][i], @v[j][i+1])} + else + Array.new(@size){|j| Complex(@v[j][i-1], -@v[j][i])} + end + end + end + # Complex scalar division. + + def cdiv(xr, xi, yr, yi) + if (yr.abs > yi.abs) + r = yi/yr + d = yr + r*yi + [(xr + r*xi)/d, (xi - r*xr)/d] + else + r = yr/yi + d = yi + r*yr + [(r*xr + xi)/d, (r*xi - xr)/d] + end + end + + + # Symmetric Householder reduction to tridiagonal form. + + def tridiagonalize + + # This is derived from the Algol procedures tred2 by + # Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + # Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + # Fortran subroutine in EISPACK. + + @size.times do |j| + @d[j] = @v[@size-1][j] + end + + # Householder reduction to tridiagonal form. + + (@size-1).downto(0+1) do |i| + + # Scale to avoid under/overflow. + + scale = 0.0 + h = 0.0 + i.times do |k| + scale = scale + @d[k].abs + end + if (scale == 0.0) + @e[i] = @d[i-1] + i.times do |j| + @d[j] = @v[i-1][j] + @v[i][j] = 0.0 + @v[j][i] = 0.0 + end + else + + # Generate Householder vector. + + i.times do |k| + @d[k] /= scale + h += @d[k] * @d[k] + end + f = @d[i-1] + g = Math.sqrt(h) + if (f > 0) + g = -g + end + @e[i] = scale * g + h -= f * g + @d[i-1] = f - g + i.times do |j| + @e[j] = 0.0 + end + + # Apply similarity transformation to remaining columns. + + i.times do |j| + f = @d[j] + @v[j][i] = f + g = @e[j] + @v[j][j] * f + (j+1).upto(i-1) do |k| + g += @v[k][j] * @d[k] + @e[k] += @v[k][j] * f + end + @e[j] = g + end + f = 0.0 + i.times do |j| + @e[j] /= h + f += @e[j] * @d[j] + end + hh = f / (h + h) + i.times do |j| + @e[j] -= hh * @d[j] + end + i.times do |j| + f = @d[j] + g = @e[j] + j.upto(i-1) do |k| + @v[k][j] -= (f * @e[k] + g * @d[k]) + end + @d[j] = @v[i-1][j] + @v[i][j] = 0.0 + end + end + @d[i] = h + end + + # Accumulate transformations. + + 0.upto(@size-1-1) do |i| + @v[@size-1][i] = @v[i][i] + @v[i][i] = 1.0 + h = @d[i+1] + if (h != 0.0) + 0.upto(i) do |k| + @d[k] = @v[k][i+1] / h + end + 0.upto(i) do |j| + g = 0.0 + 0.upto(i) do |k| + g += @v[k][i+1] * @v[k][j] + end + 0.upto(i) do |k| + @v[k][j] -= g * @d[k] + end + end + end + 0.upto(i) do |k| + @v[k][i+1] = 0.0 + end + end + @size.times do |j| + @d[j] = @v[@size-1][j] + @v[@size-1][j] = 0.0 + end + @v[@size-1][@size-1] = 1.0 + @e[0] = 0.0 + end + + + # Symmetric tridiagonal QL algorithm. + + def diagonalize + # This is derived from the Algol procedures tql2, by + # Bowdler, Martin, Reinsch, and Wilkinson, Handbook for + # Auto. Comp., Vol.ii-Linear Algebra, and the corresponding + # Fortran subroutine in EISPACK. + + 1.upto(@size-1) do |i| + @e[i-1] = @e[i] + end + @e[@size-1] = 0.0 + + f = 0.0 + tst1 = 0.0 + eps = Float::EPSILON + @size.times do |l| + + # Find small subdiagonal element + + tst1 = [tst1, @d[l].abs + @e[l].abs].max + m = l + while (m < @size) do + if (@e[m].abs <= eps*tst1) + break + end + m+=1 + end + + # If m == l, @d[l] is an eigenvalue, + # otherwise, iterate. + + if (m > l) + iter = 0 + begin + iter = iter + 1 # (Could check iteration count here.) + + # Compute implicit shift + + g = @d[l] + p = (@d[l+1] - g) / (2.0 * @e[l]) + r = Math.hypot(p, 1.0) + if (p < 0) + r = -r + end + @d[l] = @e[l] / (p + r) + @d[l+1] = @e[l] * (p + r) + dl1 = @d[l+1] + h = g - @d[l] + (l+2).upto(@size-1) do |i| + @d[i] -= h + end + f += h + + # Implicit QL transformation. + + p = @d[m] + c = 1.0 + c2 = c + c3 = c + el1 = @e[l+1] + s = 0.0 + s2 = 0.0 + (m-1).downto(l) do |i| + c3 = c2 + c2 = c + s2 = s + g = c * @e[i] + h = c * p + r = Math.hypot(p, @e[i]) + @e[i+1] = s * r + s = @e[i] / r + c = p / r + p = c * @d[i] - s * g + @d[i+1] = h + s * (c * g + s * @d[i]) + + # Accumulate transformation. + + @size.times do |k| + h = @v[k][i+1] + @v[k][i+1] = s * @v[k][i] + c * h + @v[k][i] = c * @v[k][i] - s * h + end + end + p = -s * s2 * c3 * el1 * @e[l] / dl1 + @e[l] = s * p + @d[l] = c * p + + # Check for convergence. + + end while (@e[l].abs > eps*tst1) + end + @d[l] = @d[l] + f + @e[l] = 0.0 + end + + # Sort eigenvalues and corresponding vectors. + + 0.upto(@size-2) do |i| + k = i + p = @d[i] + (i+1).upto(@size-1) do |j| + if (@d[j] < p) + k = j + p = @d[j] + end + end + if (k != i) + @d[k] = @d[i] + @d[i] = p + @size.times do |j| + p = @v[j][i] + @v[j][i] = @v[j][k] + @v[j][k] = p + end + end + end + end + + # Nonsymmetric reduction to Hessenberg form. + + def reduce_to_hessenberg + # This is derived from the Algol procedures orthes and ortran, + # by Martin and Wilkinson, Handbook for Auto. Comp., + # Vol.ii-Linear Algebra, and the corresponding + # Fortran subroutines in EISPACK. + + low = 0 + high = @size-1 + + (low+1).upto(high-1) do |m| + + # Scale column. + + scale = 0.0 + m.upto(high) do |i| + scale = scale + @h[i][m-1].abs + end + if (scale != 0.0) + + # Compute Householder transformation. + + h = 0.0 + high.downto(m) do |i| + @ort[i] = @h[i][m-1]/scale + h += @ort[i] * @ort[i] + end + g = Math.sqrt(h) + if (@ort[m] > 0) + g = -g + end + h -= @ort[m] * g + @ort[m] = @ort[m] - g + + # Apply Householder similarity transformation + # @h = (I-u*u'/h)*@h*(I-u*u')/h) + + m.upto(@size-1) do |j| + f = 0.0 + high.downto(m) do |i| + f += @ort[i]*@h[i][j] + end + f = f/h + m.upto(high) do |i| + @h[i][j] -= f*@ort[i] + end + end + + 0.upto(high) do |i| + f = 0.0 + high.downto(m) do |j| + f += @ort[j]*@h[i][j] + end + f = f/h + m.upto(high) do |j| + @h[i][j] -= f*@ort[j] + end + end + @ort[m] = scale*@ort[m] + @h[m][m-1] = scale*g + end + end + + # Accumulate transformations (Algol's ortran). + + @size.times do |i| + @size.times do |j| + @v[i][j] = (i == j ? 1.0 : 0.0) + end + end + + (high-1).downto(low+1) do |m| + if (@h[m][m-1] != 0.0) + (m+1).upto(high) do |i| + @ort[i] = @h[i][m-1] + end + m.upto(high) do |j| + g = 0.0 + m.upto(high) do |i| + g += @ort[i] * @v[i][j] + end + # Double division avoids possible underflow + g = (g / @ort[m]) / @h[m][m-1] + m.upto(high) do |i| + @v[i][j] += g * @ort[i] + end + end + end + end + end + + + + # Nonsymmetric reduction from Hessenberg to real Schur form. + + def hessenberg_to_real_schur + + # This is derived from the Algol procedure hqr2, + # by Martin and Wilkinson, Handbook for Auto. Comp., + # Vol.ii-Linear Algebra, and the corresponding + # Fortran subroutine in EISPACK. + + # Initialize + + nn = @size + n = nn-1 + low = 0 + high = nn-1 + eps = Float::EPSILON + exshift = 0.0 + p=q=r=s=z=0 + + # Store roots isolated by balanc and compute matrix norm + + norm = 0.0 + nn.times do |i| + if (i < low || i > high) + @d[i] = @h[i][i] + @e[i] = 0.0 + end + ([i-1, 0].max).upto(nn-1) do |j| + norm = norm + @h[i][j].abs + end + end + + # Outer loop over eigenvalue index + + iter = 0 + while (n >= low) do + + # Look for single small sub-diagonal element + + l = n + while (l > low) do + s = @h[l-1][l-1].abs + @h[l][l].abs + if (s == 0.0) + s = norm + end + if (@h[l][l-1].abs < eps * s) + break + end + l-=1 + end + + # Check for convergence + # One root found + + if (l == n) + @h[n][n] = @h[n][n] + exshift + @d[n] = @h[n][n] + @e[n] = 0.0 + n-=1 + iter = 0 + + # Two roots found + + elsif (l == n-1) + w = @h[n][n-1] * @h[n-1][n] + p = (@h[n-1][n-1] - @h[n][n]) / 2.0 + q = p * p + w + z = Math.sqrt(q.abs) + @h[n][n] = @h[n][n] + exshift + @h[n-1][n-1] = @h[n-1][n-1] + exshift + x = @h[n][n] + + # Real pair + + if (q >= 0) + if (p >= 0) + z = p + z + else + z = p - z + end + @d[n-1] = x + z + @d[n] = @d[n-1] + if (z != 0.0) + @d[n] = x - w / z + end + @e[n-1] = 0.0 + @e[n] = 0.0 + x = @h[n][n-1] + s = x.abs + z.abs + p = x / s + q = z / s + r = Math.sqrt(p * p+q * q) + p /= r + q /= r + + # Row modification + + (n-1).upto(nn-1) do |j| + z = @h[n-1][j] + @h[n-1][j] = q * z + p * @h[n][j] + @h[n][j] = q * @h[n][j] - p * z + end + + # Column modification + + 0.upto(n) do |i| + z = @h[i][n-1] + @h[i][n-1] = q * z + p * @h[i][n] + @h[i][n] = q * @h[i][n] - p * z + end + + # Accumulate transformations + + low.upto(high) do |i| + z = @v[i][n-1] + @v[i][n-1] = q * z + p * @v[i][n] + @v[i][n] = q * @v[i][n] - p * z + end + + # Complex pair + + else + @d[n-1] = x + p + @d[n] = x + p + @e[n-1] = z + @e[n] = -z + end + n -= 2 + iter = 0 + + # No convergence yet + + else + + # Form shift + + x = @h[n][n] + y = 0.0 + w = 0.0 + if (l < n) + y = @h[n-1][n-1] + w = @h[n][n-1] * @h[n-1][n] + end + + # Wilkinson's original ad hoc shift + + if (iter == 10) + exshift += x + low.upto(n) do |i| + @h[i][i] -= x + end + s = @h[n][n-1].abs + @h[n-1][n-2].abs + x = y = 0.75 * s + w = -0.4375 * s * s + end + + # MATLAB's new ad hoc shift + + if (iter == 30) + s = (y - x) / 2.0 + s *= s + w + if (s > 0) + s = Math.sqrt(s) + if (y < x) + s = -s + end + s = x - w / ((y - x) / 2.0 + s) + low.upto(n) do |i| + @h[i][i] -= s + end + exshift += s + x = y = w = 0.964 + end + end + + iter = iter + 1 # (Could check iteration count here.) + + # Look for two consecutive small sub-diagonal elements + + m = n-2 + while (m >= l) do + z = @h[m][m] + r = x - z + s = y - z + p = (r * s - w) / @h[m+1][m] + @h[m][m+1] + q = @h[m+1][m+1] - z - r - s + r = @h[m+2][m+1] + s = p.abs + q.abs + r.abs + p /= s + q /= s + r /= s + if (m == l) + break + end + if (@h[m][m-1].abs * (q.abs + r.abs) < + eps * (p.abs * (@h[m-1][m-1].abs + z.abs + + @h[m+1][m+1].abs))) + break + end + m-=1 + end + + (m+2).upto(n) do |i| + @h[i][i-2] = 0.0 + if (i > m+2) + @h[i][i-3] = 0.0 + end + end + + # Double QR step involving rows l:n and columns m:n + + m.upto(n-1) do |k| + notlast = (k != n-1) + if (k != m) + p = @h[k][k-1] + q = @h[k+1][k-1] + r = (notlast ? @h[k+2][k-1] : 0.0) + x = p.abs + q.abs + r.abs + next if x == 0 + p /= x + q /= x + r /= x + end + s = Math.sqrt(p * p + q * q + r * r) + if (p < 0) + s = -s + end + if (s != 0) + if (k != m) + @h[k][k-1] = -s * x + elsif (l != m) + @h[k][k-1] = -@h[k][k-1] + end + p += s + x = p / s + y = q / s + z = r / s + q /= p + r /= p + + # Row modification + + k.upto(nn-1) do |j| + p = @h[k][j] + q * @h[k+1][j] + if (notlast) + p += r * @h[k+2][j] + @h[k+2][j] = @h[k+2][j] - p * z + end + @h[k][j] = @h[k][j] - p * x + @h[k+1][j] = @h[k+1][j] - p * y + end + + # Column modification + + 0.upto([n, k+3].min) do |i| + p = x * @h[i][k] + y * @h[i][k+1] + if (notlast) + p += z * @h[i][k+2] + @h[i][k+2] = @h[i][k+2] - p * r + end + @h[i][k] = @h[i][k] - p + @h[i][k+1] = @h[i][k+1] - p * q + end + + # Accumulate transformations + + low.upto(high) do |i| + p = x * @v[i][k] + y * @v[i][k+1] + if (notlast) + p += z * @v[i][k+2] + @v[i][k+2] = @v[i][k+2] - p * r + end + @v[i][k] = @v[i][k] - p + @v[i][k+1] = @v[i][k+1] - p * q + end + end # (s != 0) + end # k loop + end # check convergence + end # while (n >= low) + + # Backsubstitute to find vectors of upper triangular form + + if (norm == 0.0) + return + end + + (nn-1).downto(0) do |n| + p = @d[n] + q = @e[n] + + # Real vector + + if (q == 0) + l = n + @h[n][n] = 1.0 + (n-1).downto(0) do |i| + w = @h[i][i] - p + r = 0.0 + l.upto(n) do |j| + r += @h[i][j] * @h[j][n] + end + if (@e[i] < 0.0) + z = w + s = r + else + l = i + if (@e[i] == 0.0) + if (w != 0.0) + @h[i][n] = -r / w + else + @h[i][n] = -r / (eps * norm) + end + + # Solve real equations + + else + x = @h[i][i+1] + y = @h[i+1][i] + q = (@d[i] - p) * (@d[i] - p) + @e[i] * @e[i] + t = (x * s - z * r) / q + @h[i][n] = t + if (x.abs > z.abs) + @h[i+1][n] = (-r - w * t) / x + else + @h[i+1][n] = (-s - y * t) / z + end + end + + # Overflow control + + t = @h[i][n].abs + if ((eps * t) * t > 1) + i.upto(n) do |j| + @h[j][n] = @h[j][n] / t + end + end + end + end + + # Complex vector + + elsif (q < 0) + l = n-1 + + # Last vector component imaginary so matrix is triangular + + if (@h[n][n-1].abs > @h[n-1][n].abs) + @h[n-1][n-1] = q / @h[n][n-1] + @h[n-1][n] = -(@h[n][n] - p) / @h[n][n-1] + else + cdivr, cdivi = cdiv(0.0, -@h[n-1][n], @h[n-1][n-1]-p, q) + @h[n-1][n-1] = cdivr + @h[n-1][n] = cdivi + end + @h[n][n-1] = 0.0 + @h[n][n] = 1.0 + (n-2).downto(0) do |i| + ra = 0.0 + sa = 0.0 + l.upto(n) do |j| + ra = ra + @h[i][j] * @h[j][n-1] + sa = sa + @h[i][j] * @h[j][n] + end + w = @h[i][i] - p + + if (@e[i] < 0.0) + z = w + r = ra + s = sa + else + l = i + if (@e[i] == 0) + cdivr, cdivi = cdiv(-ra, -sa, w, q) + @h[i][n-1] = cdivr + @h[i][n] = cdivi + else + + # Solve complex equations + + x = @h[i][i+1] + y = @h[i+1][i] + vr = (@d[i] - p) * (@d[i] - p) + @e[i] * @e[i] - q * q + vi = (@d[i] - p) * 2.0 * q + if (vr == 0.0 && vi == 0.0) + vr = eps * norm * (w.abs + q.abs + + x.abs + y.abs + z.abs) + end + cdivr, cdivi = cdiv(x*r-z*ra+q*sa, x*s-z*sa-q*ra, vr, vi) + @h[i][n-1] = cdivr + @h[i][n] = cdivi + if (x.abs > (z.abs + q.abs)) + @h[i+1][n-1] = (-ra - w * @h[i][n-1] + q * @h[i][n]) / x + @h[i+1][n] = (-sa - w * @h[i][n] - q * @h[i][n-1]) / x + else + cdivr, cdivi = cdiv(-r-y*@h[i][n-1], -s-y*@h[i][n], z, q) + @h[i+1][n-1] = cdivr + @h[i+1][n] = cdivi + end + end + + # Overflow control + + t = [@h[i][n-1].abs, @h[i][n].abs].max + if ((eps * t) * t > 1) + i.upto(n) do |j| + @h[j][n-1] = @h[j][n-1] / t + @h[j][n] = @h[j][n] / t + end + end + end + end + end + end + + # Vectors of isolated roots + + nn.times do |i| + if (i < low || i > high) + i.upto(nn-1) do |j| + @v[i][j] = @h[i][j] + end + end + end + + # Back transformation to get eigenvectors of original matrix + + (nn-1).downto(low) do |j| + low.upto(high) do |i| + z = 0.0 + low.upto([j, high].min) do |k| + z += @v[i][k] * @h[k][j] + end + @v[i][j] = z + end + end + end + + end +end diff --git a/ruby/lib/ruby/2.1.0/matrix/lup_decomposition.rb b/ruby/lib/ruby/2.1.0/matrix/lup_decomposition.rb new file mode 100644 index 0000000..30f3276 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/matrix/lup_decomposition.rb @@ -0,0 +1,218 @@ +class Matrix + # Adapted from JAMA: http://math.nist.gov/javanumerics/jama/ + + # + # For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n + # unit lower triangular matrix L, an n-by-n upper triangular matrix U, + # and a m-by-m permutation matrix P so that L*U = P*A. + # If m < n, then L is m-by-m and U is m-by-n. + # + # The LUP decomposition with pivoting always exists, even if the matrix is + # singular, so the constructor will never fail. The primary use of the + # LU decomposition is in the solution of square systems of simultaneous + # linear equations. This will fail if singular? returns true. + # + + class LUPDecomposition + # Returns the lower triangular factor +L+ + + include Matrix::ConversionHelper + + def l + Matrix.build(@row_count, [@column_count, @row_count].min) do |i, j| + if (i > j) + @lu[i][j] + elsif (i == j) + 1 + else + 0 + end + end + end + + # Returns the upper triangular factor +U+ + + def u + Matrix.build([@column_count, @row_count].min, @column_count) do |i, j| + if (i <= j) + @lu[i][j] + else + 0 + end + end + end + + # Returns the permutation matrix +P+ + + def p + rows = Array.new(@row_count){Array.new(@row_count, 0)} + @pivots.each_with_index{|p, i| rows[i][p] = 1} + Matrix.send :new, rows, @row_count + end + + # Returns +L+, +U+, +P+ in an array + + def to_ary + [l, u, p] + end + alias_method :to_a, :to_ary + + # Returns the pivoting indices + + attr_reader :pivots + + # Returns +true+ if +U+, and hence +A+, is singular. + + def singular? () + @column_count.times do |j| + if (@lu[j][j] == 0) + return true + end + end + false + end + + # Returns the determinant of +A+, calculated efficiently + # from the factorization. + + def det + if (@row_count != @column_count) + Matrix.Raise Matrix::ErrDimensionMismatch + end + d = @pivot_sign + @column_count.times do |j| + d *= @lu[j][j] + end + d + end + alias_method :determinant, :det + + # Returns +m+ so that A*m = b, + # or equivalently so that L*U*m = P*b + # +b+ can be a Matrix or a Vector + + def solve b + if (singular?) + Matrix.Raise Matrix::ErrNotRegular, "Matrix is singular." + end + if b.is_a? Matrix + if (b.row_count != @row_count) + Matrix.Raise Matrix::ErrDimensionMismatch + end + + # Copy right hand side with pivoting + nx = b.column_count + m = @pivots.map{|row| b.row(row).to_a} + + # Solve L*Y = P*b + @column_count.times do |k| + (k+1).upto(@column_count-1) do |i| + nx.times do |j| + m[i][j] -= m[k][j]*@lu[i][k] + end + end + end + # Solve U*m = Y + (@column_count-1).downto(0) do |k| + nx.times do |j| + m[k][j] = m[k][j].quo(@lu[k][k]) + end + k.times do |i| + nx.times do |j| + m[i][j] -= m[k][j]*@lu[i][k] + end + end + end + Matrix.send :new, m, nx + else # same algorithm, specialized for simpler case of a vector + b = convert_to_array(b) + if (b.size != @row_count) + Matrix.Raise Matrix::ErrDimensionMismatch + end + + # Copy right hand side with pivoting + m = b.values_at(*@pivots) + + # Solve L*Y = P*b + @column_count.times do |k| + (k+1).upto(@column_count-1) do |i| + m[i] -= m[k]*@lu[i][k] + end + end + # Solve U*m = Y + (@column_count-1).downto(0) do |k| + m[k] = m[k].quo(@lu[k][k]) + k.times do |i| + m[i] -= m[k]*@lu[i][k] + end + end + Vector.elements(m, false) + end + end + + def initialize a + raise TypeError, "Expected Matrix but got #{a.class}" unless a.is_a?(Matrix) + # Use a "left-looking", dot-product, Crout/Doolittle algorithm. + @lu = a.to_a + @row_count = a.row_count + @column_count = a.column_count + @pivots = Array.new(@row_count) + @row_count.times do |i| + @pivots[i] = i + end + @pivot_sign = 1 + lu_col_j = Array.new(@row_count) + + # Outer loop. + + @column_count.times do |j| + + # Make a copy of the j-th column to localize references. + + @row_count.times do |i| + lu_col_j[i] = @lu[i][j] + end + + # Apply previous transformations. + + @row_count.times do |i| + lu_row_i = @lu[i] + + # Most of the time is spent in the following dot product. + + kmax = [i, j].min + s = 0 + kmax.times do |k| + s += lu_row_i[k]*lu_col_j[k] + end + + lu_row_i[j] = lu_col_j[i] -= s + end + + # Find pivot and exchange if necessary. + + p = j + (j+1).upto(@row_count-1) do |i| + if (lu_col_j[i].abs > lu_col_j[p].abs) + p = i + end + end + if (p != j) + @column_count.times do |k| + t = @lu[p][k]; @lu[p][k] = @lu[j][k]; @lu[j][k] = t + end + k = @pivots[p]; @pivots[p] = @pivots[j]; @pivots[j] = k + @pivot_sign = -@pivot_sign + end + + # Compute multipliers. + + if (j < @row_count && @lu[j][j] != 0) + (j+1).upto(@row_count-1) do |i| + @lu[i][j] = @lu[i][j].quo(@lu[j][j]) + end + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/minitest/autorun.rb b/ruby/lib/ruby/2.1.0/minitest/autorun.rb new file mode 100644 index 0000000..cb4a3a0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/minitest/autorun.rb @@ -0,0 +1,19 @@ +# encoding: utf-8 +###################################################################### +# This file is imported from the minitest project. +# DO NOT make modifications in this repo. They _will_ be reverted! +# File a patch instead and assign it to Ryan Davis. +###################################################################### + +begin + require 'rubygems' + gem 'minitest' +rescue Gem::LoadError + # do nothing +end + +require 'minitest/unit' +require 'minitest/spec' +require 'minitest/mock' + +MiniTest::Unit.autorun diff --git a/ruby/lib/ruby/2.1.0/minitest/benchmark.rb b/ruby/lib/ruby/2.1.0/minitest/benchmark.rb new file mode 100644 index 0000000..e233282 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/minitest/benchmark.rb @@ -0,0 +1,423 @@ +# encoding: utf-8 +###################################################################### +# This file is imported from the minitest project. +# DO NOT make modifications in this repo. They _will_ be reverted! +# File a patch instead and assign it to Ryan Davis. +###################################################################### + +require 'minitest/unit' +require 'minitest/spec' + +class MiniTest::Unit # :nodoc: + def run_benchmarks # :nodoc: + _run_anything :benchmark + end + + def benchmark_suite_header suite # :nodoc: + "\n#{suite}\t#{suite.bench_range.join("\t")}" + end + + class TestCase + ## + # Returns a set of ranges stepped exponentially from +min+ to + # +max+ by powers of +base+. Eg: + # + # bench_exp(2, 16, 2) # => [2, 4, 8, 16] + + def self.bench_exp min, max, base = 10 + min = (Math.log10(min) / Math.log10(base)).to_i + max = (Math.log10(max) / Math.log10(base)).to_i + + (min..max).map { |m| base ** m }.to_a + end + + ## + # Returns a set of ranges stepped linearly from +min+ to +max+ by + # +step+. Eg: + # + # bench_linear(20, 40, 10) # => [20, 30, 40] + + def self.bench_linear min, max, step = 10 + (min..max).step(step).to_a + rescue LocalJumpError # 1.8.6 + r = []; (min..max).step(step) { |n| r << n }; r + end + + ## + # Returns the benchmark methods (methods that start with bench_) + # for that class. + + def self.benchmark_methods # :nodoc: + public_instance_methods(true).grep(/^bench_/).map { |m| m.to_s }.sort + end + + ## + # Returns all test suites that have benchmark methods. + + def self.benchmark_suites + TestCase.test_suites.reject { |s| s.benchmark_methods.empty? } + end + + ## + # Specifies the ranges used for benchmarking for that class. + # Defaults to exponential growth from 1 to 10k by powers of 10. + # Override if you need different ranges for your benchmarks. + # + # See also: ::bench_exp and ::bench_linear. + + def self.bench_range + bench_exp 1, 10_000 + end + + ## + # Runs the given +work+, gathering the times of each run. Range + # and times are then passed to a given +validation+ proc. Outputs + # the benchmark name and times in tab-separated format, making it + # easy to paste into a spreadsheet for graphing or further + # analysis. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # validation = proc { |x, y| ... } + # assert_performance validation do |n| + # @obj.algorithm(n) + # end + # end + + def assert_performance validation, &work + range = self.class.bench_range + + io.print "#{__name__}" + + times = [] + + range.each do |x| + GC.start + t0 = Time.now + instance_exec(x, &work) + t = Time.now - t0 + + io.print "\t%9.6f" % t + times << t + end + io.puts + + validation[range, times] + end + + ## + # Runs the given +work+ and asserts that the times gathered fit to + # match a constant rate (eg, linear slope == 0) within a given + # +threshold+. Note: because we're testing for a slope of 0, R^2 + # is not a good determining factor for the fit, so the threshold + # is applied against the slope itself. As such, you probably want + # to tighten it from the default. + # + # See http://www.graphpad.com/curvefit/goodness_of_fit.htm for + # more details. + # + # Fit is calculated by #fit_linear. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # assert_performance_constant 0.9999 do |n| + # @obj.algorithm(n) + # end + # end + + def assert_performance_constant threshold = 0.99, &work + validation = proc do |range, times| + a, b, rr = fit_linear range, times + assert_in_delta 0, b, 1 - threshold + [a, b, rr] + end + + assert_performance validation, &work + end + + ## + # Runs the given +work+ and asserts that the times gathered fit to + # match a exponential curve within a given error +threshold+. + # + # Fit is calculated by #fit_exponential. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # assert_performance_exponential 0.9999 do |n| + # @obj.algorithm(n) + # end + # end + + def assert_performance_exponential threshold = 0.99, &work + assert_performance validation_for_fit(:exponential, threshold), &work + end + + ## + # Runs the given +work+ and asserts that the times gathered fit to + # match a logarithmic curve within a given error +threshold+. + # + # Fit is calculated by #fit_logarithmic. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # assert_performance_logarithmic 0.9999 do |n| + # @obj.algorithm(n) + # end + # end + + def assert_performance_logarithmic threshold = 0.99, &work + assert_performance validation_for_fit(:logarithmic, threshold), &work + end + + ## + # Runs the given +work+ and asserts that the times gathered fit to + # match a straight line within a given error +threshold+. + # + # Fit is calculated by #fit_linear. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # assert_performance_linear 0.9999 do |n| + # @obj.algorithm(n) + # end + # end + + def assert_performance_linear threshold = 0.99, &work + assert_performance validation_for_fit(:linear, threshold), &work + end + + ## + # Runs the given +work+ and asserts that the times gathered curve + # fit to match a power curve within a given error +threshold+. + # + # Fit is calculated by #fit_power. + # + # Ranges are specified by ::bench_range. + # + # Eg: + # + # def bench_algorithm + # assert_performance_power 0.9999 do |x| + # @obj.algorithm + # end + # end + + def assert_performance_power threshold = 0.99, &work + assert_performance validation_for_fit(:power, threshold), &work + end + + ## + # Takes an array of x/y pairs and calculates the general R^2 value. + # + # See: http://en.wikipedia.org/wiki/Coefficient_of_determination + + def fit_error xys + y_bar = sigma(xys) { |x, y| y } / xys.size.to_f + ss_tot = sigma(xys) { |x, y| (y - y_bar) ** 2 } + ss_err = sigma(xys) { |x, y| (yield(x) - y) ** 2 } + + 1 - (ss_err / ss_tot) + end + + ## + # To fit a functional form: y = ae^(bx). + # + # Takes x and y values and returns [a, b, r^2]. + # + # See: http://mathworld.wolfram.com/LeastSquaresFittingExponential.html + + def fit_exponential xs, ys + n = xs.size + xys = xs.zip(ys) + sxlny = sigma(xys) { |x,y| x * Math.log(y) } + slny = sigma(xys) { |x,y| Math.log(y) } + sx2 = sigma(xys) { |x,y| x * x } + sx = sigma xs + + c = n * sx2 - sx ** 2 + a = (slny * sx2 - sx * sxlny) / c + b = ( n * sxlny - sx * slny ) / c + + return Math.exp(a), b, fit_error(xys) { |x| Math.exp(a + b * x) } + end + + ## + # To fit a functional form: y = a + b*ln(x). + # + # Takes x and y values and returns [a, b, r^2]. + # + # See: http://mathworld.wolfram.com/LeastSquaresFittingLogarithmic.html + + def fit_logarithmic xs, ys + n = xs.size + xys = xs.zip(ys) + slnx2 = sigma(xys) { |x,y| Math.log(x) ** 2 } + slnx = sigma(xys) { |x,y| Math.log(x) } + sylnx = sigma(xys) { |x,y| y * Math.log(x) } + sy = sigma(xys) { |x,y| y } + + c = n * slnx2 - slnx ** 2 + b = ( n * sylnx - sy * slnx ) / c + a = (sy - b * slnx) / n + + return a, b, fit_error(xys) { |x| a + b * Math.log(x) } + end + + + ## + # Fits the functional form: a + bx. + # + # Takes x and y values and returns [a, b, r^2]. + # + # See: http://mathworld.wolfram.com/LeastSquaresFitting.html + + def fit_linear xs, ys + n = xs.size + xys = xs.zip(ys) + sx = sigma xs + sy = sigma ys + sx2 = sigma(xs) { |x| x ** 2 } + sxy = sigma(xys) { |x,y| x * y } + + c = n * sx2 - sx**2 + a = (sy * sx2 - sx * sxy) / c + b = ( n * sxy - sx * sy ) / c + + return a, b, fit_error(xys) { |x| a + b * x } + end + + ## + # To fit a functional form: y = ax^b. + # + # Takes x and y values and returns [a, b, r^2]. + # + # See: http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html + + def fit_power xs, ys + n = xs.size + xys = xs.zip(ys) + slnxlny = sigma(xys) { |x, y| Math.log(x) * Math.log(y) } + slnx = sigma(xs) { |x | Math.log(x) } + slny = sigma(ys) { | y| Math.log(y) } + slnx2 = sigma(xs) { |x | Math.log(x) ** 2 } + + b = (n * slnxlny - slnx * slny) / (n * slnx2 - slnx ** 2); + a = (slny - b * slnx) / n + + return Math.exp(a), b, fit_error(xys) { |x| (Math.exp(a) * (x ** b)) } + end + + ## + # Enumerates over +enum+ mapping +block+ if given, returning the + # sum of the result. Eg: + # + # sigma([1, 2, 3]) # => 1 + 2 + 3 => 7 + # sigma([1, 2, 3]) { |n| n ** 2 } # => 1 + 4 + 9 => 14 + + def sigma enum, &block + enum = enum.map(&block) if block + enum.inject { |sum, n| sum + n } + end + + ## + # Returns a proc that calls the specified fit method and asserts + # that the error is within a tolerable threshold. + + def validation_for_fit msg, threshold + proc do |range, times| + a, b, rr = send "fit_#{msg}", range, times + assert_operator rr, :>=, threshold + [a, b, rr] + end + end + end +end + +class MiniTest::Spec + ## + # This is used to define a new benchmark method. You usually don't + # use this directly and is intended for those needing to write new + # performance curve fits (eg: you need a specific polynomial fit). + # + # See ::bench_performance_linear for an example of how to use this. + + def self.bench name, &block + define_method "bench_#{name.gsub(/\W+/, '_')}", &block + end + + ## + # Specifies the ranges used for benchmarking for that class. + # + # bench_range do + # bench_exp(2, 16, 2) + # end + # + # See Unit::TestCase.bench_range for more details. + + def self.bench_range &block + return super unless block + + meta = (class << self; self; end) + meta.send :define_method, "bench_range", &block + end + + ## + # Create a benchmark that verifies that the performance is linear. + # + # describe "my class" do + # bench_performance_linear "fast_algorithm", 0.9999 do |n| + # @obj.fast_algorithm(n) + # end + # end + + def self.bench_performance_linear name, threshold = 0.99, &work + bench name do + assert_performance_linear threshold, &work + end + end + + ## + # Create a benchmark that verifies that the performance is constant. + # + # describe "my class" do + # bench_performance_constant "zoom_algorithm!" do |n| + # @obj.zoom_algorithm!(n) + # end + # end + + def self.bench_performance_constant name, threshold = 0.99, &work + bench name do + assert_performance_constant threshold, &work + end + end + + ## + # Create a benchmark that verifies that the performance is exponential. + # + # describe "my class" do + # bench_performance_exponential "algorithm" do |n| + # @obj.algorithm(n) + # end + # end + + def self.bench_performance_exponential name, threshold = 0.99, &work + bench name do + assert_performance_exponential threshold, &work + end + end +end diff --git a/ruby/lib/ruby/2.1.0/minitest/hell.rb b/ruby/lib/ruby/2.1.0/minitest/hell.rb new file mode 100644 index 0000000..827bf0e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/minitest/hell.rb @@ -0,0 +1,20 @@ +# encoding: utf-8 +###################################################################### +# This file is imported from the minitest project. +# DO NOT make modifications in this repo. They _will_ be reverted! +# File a patch instead and assign it to Ryan Davis. +###################################################################### + +require "minitest/parallel_each" + +# :stopdoc: +class Minitest::Unit::TestCase + class << self + alias :old_test_order :test_order + + def test_order + :parallel + end + end +end +# :startdoc: diff --git a/ruby/lib/ruby/2.1.0/minitest/mock.rb b/ruby/lib/ruby/2.1.0/minitest/mock.rb new file mode 100644 index 0000000..a5b0f60 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/minitest/mock.rb @@ -0,0 +1,200 @@ +# encoding: utf-8 +###################################################################### +# This file is imported from the minitest project. +# DO NOT make modifications in this repo. They _will_ be reverted! +# File a patch instead and assign it to Ryan Davis. +###################################################################### + +class MockExpectationError < StandardError; end # :nodoc: + +## +# A simple and clean mock object framework. + +module MiniTest # :nodoc: + + ## + # All mock objects are an instance of Mock + + class Mock + alias :__respond_to? :respond_to? + + skip_methods = %w(object_id respond_to_missing? inspect === to_s) + + instance_methods.each do |m| + undef_method m unless skip_methods.include?(m.to_s) || m =~ /^__/ + end + + def initialize # :nodoc: + @expected_calls = Hash.new { |calls, name| calls[name] = [] } + @actual_calls = Hash.new { |calls, name| calls[name] = [] } + end + + ## + # Expect that method +name+ is called, optionally with +args+ or a + # +blk+, and returns +retval+. + # + # @mock.expect(:meaning_of_life, 42) + # @mock.meaning_of_life # => 42 + # + # @mock.expect(:do_something_with, true, [some_obj, true]) + # @mock.do_something_with(some_obj, true) # => true + # + # @mock.expect(:do_something_else, true) do |a1, a2| + # a1 == "buggs" && a2 == :bunny + # end + # + # +args+ is compared to the expected args using case equality (ie, the + # '===' operator), allowing for less specific expectations. + # + # @mock.expect(:uses_any_string, true, [String]) + # @mock.uses_any_string("foo") # => true + # @mock.verify # => true + # + # @mock.expect(:uses_one_string, true, ["foo"] + # @mock.uses_one_string("bar") # => true + # @mock.verify # => raises MockExpectationError + + def expect(name, retval, args=[], &blk) + if block_given? + raise ArgumentError, "args ignored when block given" unless args.empty? + @expected_calls[name] << { :retval => retval, :block => blk } + else + raise ArgumentError, "args must be an array" unless Array === args + @expected_calls[name] << { :retval => retval, :args => args } + end + self + end + + def __call name, data # :nodoc: + case data + when Hash then + "#{name}(#{data[:args].inspect[1..-2]}) => #{data[:retval].inspect}" + else + data.map { |d| __call name, d }.join ", " + end + end + + ## + # Verify that all methods were called as expected. Raises + # +MockExpectationError+ if the mock object was not called as + # expected. + + def verify + @expected_calls.each do |name, calls| + calls.each do |expected| + msg1 = "expected #{__call name, expected}" + msg2 = "#{msg1}, got [#{__call name, @actual_calls[name]}]" + + raise MockExpectationError, msg2 if + @actual_calls.has_key?(name) and + not @actual_calls[name].include?(expected) + + raise MockExpectationError, msg1 unless + @actual_calls.has_key?(name) and + @actual_calls[name].include?(expected) + end + end + true + end + + def method_missing(sym, *args) # :nodoc: + unless @expected_calls.has_key?(sym) then + raise NoMethodError, "unmocked method %p, expected one of %p" % + [sym, @expected_calls.keys.sort_by(&:to_s)] + end + + index = @actual_calls[sym].length + expected_call = @expected_calls[sym][index] + + unless expected_call then + raise MockExpectationError, "No more expects available for %p: %p" % + [sym, args] + end + + expected_args, retval, val_block = + expected_call.values_at(:args, :retval, :block) + + if val_block then + raise MockExpectationError, "mocked method %p failed block w/ %p" % + [sym, args] unless val_block.call(args) + + # keep "verify" happy + @actual_calls[sym] << expected_call + return retval + end + + if expected_args.size != args.size then + raise ArgumentError, "mocked method %p expects %d arguments, got %d" % + [sym, expected_args.size, args.size] + end + + fully_matched = expected_args.zip(args).all? { |mod, a| + mod === a or mod == a + } + + unless fully_matched then + raise MockExpectationError, "mocked method %p called with unexpected arguments %p" % + [sym, args] + end + + @actual_calls[sym] << { + :retval => retval, + :args => expected_args.zip(args).map { |mod, a| mod === a ? mod : a } + } + + retval + end + + def respond_to?(sym, include_private = false) # :nodoc: + return true if @expected_calls.has_key?(sym.to_sym) + return __respond_to?(sym, include_private) + end + end +end + +class Object # :nodoc: + + ## + # Add a temporary stubbed method replacing +name+ for the duration + # of the +block+. If +val_or_callable+ responds to #call, then it + # returns the result of calling it, otherwise returns the value + # as-is. Cleans up the stub at the end of the +block+. The method + # +name+ must exist before stubbing. + # + # def test_stale_eh + # obj_under_test = Something.new + # refute obj_under_test.stale? + # + # Time.stub :now, Time.at(0) do + # assert obj_under_test.stale? + # end + # end + + def stub name, val_or_callable, &block + new_name = "__minitest_stub__#{name}" + + metaclass = class << self; self; end + + if respond_to? name and not methods.map(&:to_s).include? name.to_s then + metaclass.send :define_method, name do |*args| + super(*args) + end + end + + metaclass.send :alias_method, new_name, name + + metaclass.send :define_method, name do |*args| + if val_or_callable.respond_to? :call then + val_or_callable.call(*args) + else + val_or_callable + end + end + + yield self + ensure + metaclass.send :undef_method, name + metaclass.send :alias_method, name, new_name + metaclass.send :undef_method, new_name + end +end diff --git a/ruby/lib/ruby/2.1.0/minitest/parallel_each.rb b/ruby/lib/ruby/2.1.0/minitest/parallel_each.rb new file mode 100644 index 0000000..e1020b3 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/minitest/parallel_each.rb @@ -0,0 +1,80 @@ +# encoding: utf-8 +###################################################################### +# This file is imported from the minitest project. +# DO NOT make modifications in this repo. They _will_ be reverted! +# File a patch instead and assign it to Ryan Davis. +###################################################################### + +## +# Provides a parallel #each that lets you enumerate using N threads. +# Use environment variable N to customize. Defaults to 2. Enumerable, +# so all the goodies come along (tho not all are wrapped yet to +# return another ParallelEach instance). + +class ParallelEach + require 'thread' + include Enumerable + + ## + # How many Threads to use for this parallel #each. + + N = (ENV['N'] || 2).to_i + + ## + # Create a new ParallelEach instance over +list+. + + def initialize list + @queue = Queue.new # *sigh*... the Queue api sucks sooo much... + + list.each { |i| @queue << i } + N.times { @queue << nil } + end + + def grep pattern # :nodoc: + self.class.new super + end + + def select(&block) # :nodoc: + self.class.new super + end + + alias find_all select # :nodoc: + + ## + # Starts N threads that yield each element to your block. Joins the + # threads at the end. + + def each + threads = N.times.map { + Thread.new do + Thread.current.abort_on_exception = true + while job = @queue.pop + yield job + end + end + } + threads.map(&:join) + end + + def count + [@queue.size - N, 0].max + end + + alias_method :size, :count +end + +class MiniTest::Unit + alias _old_run_suites _run_suites + + ## + # Runs all the +suites+ for a given +type+. Runs suites declaring + # a test_order of +:parallel+ in parallel, and everything else + # serial. + + def _run_suites suites, type + parallel, serial = suites.partition { |s| s.test_order == :parallel } + + ParallelEach.new(parallel).map { |suite| _run_suite suite, type } + + serial.map { |suite| _run_suite suite, type } + end +end diff --git a/ruby/lib/ruby/2.1.0/minitest/pride.rb b/ruby/lib/ruby/2.1.0/minitest/pride.rb new file mode 100644 index 0000000..40c3539 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/minitest/pride.rb @@ -0,0 +1,119 @@ +# encoding: utf-8 +###################################################################### +# This file is imported from the minitest project. +# DO NOT make modifications in this repo. They _will_ be reverted! +# File a patch instead and assign it to Ryan Davis. +###################################################################### + +require "minitest/unit" + +## +# Show your testing pride! + +class PrideIO + + # Start an escape sequence + ESC = "\e[" + + # End the escape sequence + NND = "#{ESC}0m" + + # The IO we're going to pipe through. + attr_reader :io + + def initialize io # :nodoc: + @io = io + # stolen from /System/Library/Perl/5.10.0/Term/ANSIColor.pm + # also reference http://en.wikipedia.org/wiki/ANSI_escape_code + @colors ||= (31..36).to_a + @size = @colors.size + @index = 0 + # io.sync = true + end + + ## + # Wrap print to colorize the output. + + def print o + case o + when "." then + io.print pride o + when "E", "F" then + io.print "#{ESC}41m#{ESC}37m#{o}#{NND}" + else + io.print o + end + end + + def puts(*o) # :nodoc: + o.map! { |s| + s.to_s.sub(/Finished tests/) { + @index = 0 + 'Fabulous tests'.split(//).map { |c| + pride(c) + }.join + } + } + + super + end + + ## + # Color a string. + + def pride string + string = "*" if string == "." + c = @colors[@index % @size] + @index += 1 + "#{ESC}#{c}m#{string}#{NND}" + end + + def method_missing msg, *args # :nodoc: + io.send(msg, *args) + end +end + +## +# If you thought the PrideIO was colorful... +# +# (Inspired by lolcat, but with clean math) + +class PrideLOL < PrideIO + PI_3 = Math::PI / 3 # :nodoc: + + def initialize io # :nodoc: + # walk red, green, and blue around a circle separated by equal thirds. + # + # To visualize, type this into wolfram-alpha: + # + # plot (3*sin(x)+3), (3*sin(x+2*pi/3)+3), (3*sin(x+4*pi/3)+3) + + # 6 has wide pretty gradients. 3 == lolcat, about half the width + @colors = (0...(6 * 7)).map { |n| + n *= 1.0 / 6 + r = (3 * Math.sin(n ) + 3).to_i + g = (3 * Math.sin(n + 2 * PI_3) + 3).to_i + b = (3 * Math.sin(n + 4 * PI_3) + 3).to_i + + # Then we take rgb and encode them in a single number using base 6. + # For some mysterious reason, we add 16... to clear the bottom 4 bits? + # Yes... they're ugly. + + 36 * r + 6 * g + b + 16 + } + + super + end + + ## + # Make the string even more colorful. Damnit. + + def pride string + c = @colors[@index % @size] + @index += 1 + "#{ESC}38;5;#{c}m#{string}#{NND}" + end +end + +klass = ENV['TERM'] =~ /^xterm|-256color$/ ? PrideLOL : PrideIO +MiniTest::Unit.output = klass.new(MiniTest::Unit.output) diff --git a/ruby/lib/ruby/2.1.0/minitest/spec.rb b/ruby/lib/ruby/2.1.0/minitest/spec.rb new file mode 100644 index 0000000..d91fccf --- /dev/null +++ b/ruby/lib/ruby/2.1.0/minitest/spec.rb @@ -0,0 +1,551 @@ +# encoding: utf-8 +###################################################################### +# This file is imported from the minitest project. +# DO NOT make modifications in this repo. They _will_ be reverted! +# File a patch instead and assign it to Ryan Davis. +###################################################################### + +#!/usr/bin/ruby -w + +require 'minitest/unit' + +class Module # :nodoc: + def infect_an_assertion meth, new_name, dont_flip = false # :nodoc: + # warn "%-22p -> %p %p" % [meth, new_name, dont_flip] + self.class_eval <<-EOM + def #{new_name} *args + case + when Proc === self then + MiniTest::Spec.current.#{meth}(*args, &self) + when #{!!dont_flip} then + MiniTest::Spec.current.#{meth}(self, *args) + else + MiniTest::Spec.current.#{meth}(args.first, self, *args[1..-1]) + end + end + EOM + end + + ## + # infect_with_assertions has been removed due to excessive clever. + # Use infect_an_assertion directly instead. + + def infect_with_assertions(pos_prefix, neg_prefix, + skip_re, + dont_flip_re = /\c0/, + map = {}) + abort "infect_with_assertions is dead. Use infect_an_assertion directly" + end +end + +module Kernel # :nodoc: + ## + # Describe a series of expectations for a given target +desc+. + # + # TODO: find good tutorial url. + # + # Defines a test class subclassing from either MiniTest::Spec or + # from the surrounding describe's class. The surrounding class may + # subclass MiniTest::Spec manually in order to easily share code: + # + # class MySpec < MiniTest::Spec + # # ... shared code ... + # end + # + # class TestStuff < MySpec + # it "does stuff" do + # # shared code available here + # end + # describe "inner stuff" do + # it "still does stuff" do + # # ...and here + # end + # end + # end + + def describe desc, additional_desc = nil, &block # :doc: + stack = MiniTest::Spec.describe_stack + name = [stack.last, desc, additional_desc].compact.join("::") + sclas = stack.last || if Class === self && is_a?(MiniTest::Spec::DSL) then + self + else + MiniTest::Spec.spec_type desc + end + + cls = sclas.create name, desc + + stack.push cls + cls.class_eval(&block) + stack.pop + cls + end + private :describe +end + +## +# MiniTest::Spec -- The faster, better, less-magical spec framework! +# +# For a list of expectations, see MiniTest::Expectations. + +class MiniTest::Spec < MiniTest::Unit::TestCase + + ## + # Oh look! A MiniTest::Spec::DSL module! Eat your heart out DHH. + + module DSL + ## + # Contains pairs of matchers and Spec classes to be used to + # calculate the superclass of a top-level describe. This allows for + # automatically customizable spec types. + # + # See: register_spec_type and spec_type + + TYPES = [[//, MiniTest::Spec]] + + ## + # Register a new type of spec that matches the spec's description. + # This method can take either a Regexp and a spec class or a spec + # class and a block that takes the description and returns true if + # it matches. + # + # Eg: + # + # register_spec_type(/Controller$/, MiniTest::Spec::Rails) + # + # or: + # + # register_spec_type(MiniTest::Spec::RailsModel) do |desc| + # desc.superclass == ActiveRecord::Base + # end + + def register_spec_type(*args, &block) + if block then + matcher, klass = block, args.first + else + matcher, klass = *args + end + TYPES.unshift [matcher, klass] + end + + ## + # Figure out the spec class to use based on a spec's description. Eg: + # + # spec_type("BlahController") # => MiniTest::Spec::Rails + + def spec_type desc + TYPES.find { |matcher, klass| + if matcher.respond_to? :call then + matcher.call desc + else + matcher === desc.to_s + end + }.last + end + + def describe_stack # :nodoc: + Thread.current[:describe_stack] ||= [] + end + + ## + # Returns the children of this spec. + + def children + @children ||= [] + end + + def nuke_test_methods! # :nodoc: + self.public_instance_methods.grep(/^test_/).each do |name| + self.send :undef_method, name + end + end + + ## + # Define a 'before' action. Inherits the way normal methods should. + # + # NOTE: +type+ is ignored and is only there to make porting easier. + # + # Equivalent to MiniTest::Unit::TestCase#setup. + + def before type = nil, &block + define_method :setup do + super() + self.instance_eval(&block) + end + end + + ## + # Define an 'after' action. Inherits the way normal methods should. + # + # NOTE: +type+ is ignored and is only there to make porting easier. + # + # Equivalent to MiniTest::Unit::TestCase#teardown. + + def after type = nil, &block + define_method :teardown do + self.instance_eval(&block) + super() + end + end + + ## + # Define an expectation with name +desc+. Name gets morphed to a + # proper test method name. For some freakish reason, people who + # write specs don't like class inheritance, so this goes way out of + # its way to make sure that expectations aren't inherited. + # + # This is also aliased to #specify and doesn't require a +desc+ arg. + # + # Hint: If you _do_ want inheritence, use minitest/unit. You can mix + # and match between assertions and expectations as much as you want. + + def it desc = "anonymous", &block + block ||= proc { skip "(no tests defined)" } + + @specs ||= 0 + @specs += 1 + + name = "test_%04d_%s" % [ @specs, desc ] + + define_method name, &block + + self.children.each do |mod| + mod.send :undef_method, name if mod.public_method_defined? name + end + + name + end + + ## + # Essentially, define an accessor for +name+ with +block+. + # + # Why use let instead of def? I honestly don't know. + + def let name, &block + define_method name do + @_memoized ||= {} + @_memoized.fetch(name) { |k| @_memoized[k] = instance_eval(&block) } + end + end + + ## + # Another lazy man's accessor generator. Made even more lazy by + # setting the name for you to +subject+. + + def subject &block + let :subject, &block + end + + def create name, desc # :nodoc: + cls = Class.new(self) do + @name = name + @desc = desc + + nuke_test_methods! + end + + children << cls + + cls + end + + def name # :nodoc: + defined?(@name) ? @name : super + end + + def to_s # :nodoc: + name # Can't alias due to 1.8.7, not sure why + end + + # :stopdoc: + attr_reader :desc + alias :specify :it + # :startdoc: + end + + extend DSL + + TYPES = DSL::TYPES # :nodoc: +end + +## +# It's where you hide your "assertions". + +module MiniTest::Expectations + ## + # See MiniTest::Assertions#assert_empty. + # + # collection.must_be_empty + # + # :method: must_be_empty + + infect_an_assertion :assert_empty, :must_be_empty, :unary + + ## + # See MiniTest::Assertions#assert_equal + # + # a.must_equal b + # + # :method: must_equal + + infect_an_assertion :assert_equal, :must_equal + + ## + # See MiniTest::Assertions#assert_in_delta + # + # n.must_be_close_to m [, delta] + # + # :method: must_be_close_to + + infect_an_assertion :assert_in_delta, :must_be_close_to + + alias :must_be_within_delta :must_be_close_to # :nodoc: + + ## + # See MiniTest::Assertions#assert_in_epsilon + # + # n.must_be_within_epsilon m [, epsilon] + # + # :method: must_be_within_epsilon + + infect_an_assertion :assert_in_epsilon, :must_be_within_epsilon + + ## + # See MiniTest::Assertions#assert_includes + # + # collection.must_include obj + # + # :method: must_include + + infect_an_assertion :assert_includes, :must_include, :reverse + + ## + # See MiniTest::Assertions#assert_instance_of + # + # obj.must_be_instance_of klass + # + # :method: must_be_instance_of + + infect_an_assertion :assert_instance_of, :must_be_instance_of + + ## + # See MiniTest::Assertions#assert_kind_of + # + # obj.must_be_kind_of mod + # + # :method: must_be_kind_of + + infect_an_assertion :assert_kind_of, :must_be_kind_of + + ## + # See MiniTest::Assertions#assert_match + # + # a.must_match b + # + # :method: must_match + + infect_an_assertion :assert_match, :must_match + + ## + # See MiniTest::Assertions#assert_nil + # + # obj.must_be_nil + # + # :method: must_be_nil + + infect_an_assertion :assert_nil, :must_be_nil, :unary + + ## + # See MiniTest::Assertions#assert_operator + # + # n.must_be :<=, 42 + # + # This can also do predicates: + # + # str.must_be :empty? + # + # :method: must_be + + infect_an_assertion :assert_operator, :must_be, :reverse + + ## + # See MiniTest::Assertions#assert_output + # + # proc { ... }.must_output out_or_nil [, err] + # + # :method: must_output + + infect_an_assertion :assert_output, :must_output + + ## + # See MiniTest::Assertions#assert_raises + # + # proc { ... }.must_raise exception + # + # :method: must_raise + + infect_an_assertion :assert_raises, :must_raise + + ## + # See MiniTest::Assertions#assert_respond_to + # + # obj.must_respond_to msg + # + # :method: must_respond_to + + infect_an_assertion :assert_respond_to, :must_respond_to, :reverse + + ## + # See MiniTest::Assertions#assert_same + # + # a.must_be_same_as b + # + # :method: must_be_same_as + + infect_an_assertion :assert_same, :must_be_same_as + + ## + # See MiniTest::Assertions#assert_send + # TODO: remove me + # + # a.must_send + # + # :method: must_send + + infect_an_assertion :assert_send, :must_send + + ## + # See MiniTest::Assertions#assert_silent + # + # proc { ... }.must_be_silent + # + # :method: must_be_silent + + infect_an_assertion :assert_silent, :must_be_silent + + ## + # See MiniTest::Assertions#assert_throws + # + # proc { ... }.must_throw sym + # + # :method: must_throw + + infect_an_assertion :assert_throws, :must_throw + + ## + # See MiniTest::Assertions#refute_empty + # + # collection.wont_be_empty + # + # :method: wont_be_empty + + infect_an_assertion :refute_empty, :wont_be_empty, :unary + + ## + # See MiniTest::Assertions#refute_equal + # + # a.wont_equal b + # + # :method: wont_equal + + infect_an_assertion :refute_equal, :wont_equal + + ## + # See MiniTest::Assertions#refute_in_delta + # + # n.wont_be_close_to m [, delta] + # + # :method: wont_be_close_to + + infect_an_assertion :refute_in_delta, :wont_be_close_to + + alias :wont_be_within_delta :wont_be_close_to # :nodoc: + + ## + # See MiniTest::Assertions#refute_in_epsilon + # + # n.wont_be_within_epsilon m [, epsilon] + # + # :method: wont_be_within_epsilon + + infect_an_assertion :refute_in_epsilon, :wont_be_within_epsilon + + ## + # See MiniTest::Assertions#refute_includes + # + # collection.wont_include obj + # + # :method: wont_include + + infect_an_assertion :refute_includes, :wont_include, :reverse + + ## + # See MiniTest::Assertions#refute_instance_of + # + # obj.wont_be_instance_of klass + # + # :method: wont_be_instance_of + + infect_an_assertion :refute_instance_of, :wont_be_instance_of + + ## + # See MiniTest::Assertions#refute_kind_of + # + # obj.wont_be_kind_of mod + # + # :method: wont_be_kind_of + + infect_an_assertion :refute_kind_of, :wont_be_kind_of + + ## + # See MiniTest::Assertions#refute_match + # + # a.wont_match b + # + # :method: wont_match + + infect_an_assertion :refute_match, :wont_match + + ## + # See MiniTest::Assertions#refute_nil + # + # obj.wont_be_nil + # + # :method: wont_be_nil + + infect_an_assertion :refute_nil, :wont_be_nil, :unary + + ## + # See MiniTest::Assertions#refute_operator + # + # n.wont_be :<=, 42 + # + # This can also do predicates: + # + # str.wont_be :empty? + # + # :method: wont_be + + infect_an_assertion :refute_operator, :wont_be, :reverse + + ## + # See MiniTest::Assertions#refute_respond_to + # + # obj.wont_respond_to msg + # + # :method: wont_respond_to + + infect_an_assertion :refute_respond_to, :wont_respond_to, :reverse + + ## + # See MiniTest::Assertions#refute_same + # + # a.wont_be_same_as b + # + # :method: wont_be_same_as + + infect_an_assertion :refute_same, :wont_be_same_as +end + +class Object # :nodoc: + include MiniTest::Expectations unless ENV["MT_NO_EXPECTATIONS"] +end diff --git a/ruby/lib/ruby/2.1.0/minitest/unit.rb b/ruby/lib/ruby/2.1.0/minitest/unit.rb new file mode 100644 index 0000000..465e5b4 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/minitest/unit.rb @@ -0,0 +1,1422 @@ +# encoding: utf-8 +###################################################################### +# This file is imported from the minitest project. +# DO NOT make modifications in this repo. They _will_ be reverted! +# File a patch instead and assign it to Ryan Davis. +###################################################################### + +require "optparse" +require "rbconfig" + +## +# Minimal (mostly drop-in) replacement for test-unit. +# +# :include: README.txt + +module MiniTest + + def self.const_missing name # :nodoc: + case name + when :MINI_DIR then + msg = "MiniTest::MINI_DIR was removed. Don't violate other's internals." + warn "WAR\NING: #{msg}" + warn "WAR\NING: Used by #{caller.first}." + const_set :MINI_DIR, "bad value" + else + super + end + end + + ## + # Assertion base class + + class Assertion < Exception; end + + ## + # Assertion raised when skipping a test + + class Skip < Assertion; end + + class << self + ## + # Filter object for backtraces. + + attr_accessor :backtrace_filter + end + + class BacktraceFilter # :nodoc: + def filter bt + return ["No backtrace"] unless bt + + new_bt = [] + + unless $DEBUG then + bt.each do |line| + break if line =~ /lib\/minitest/ + new_bt << line + end + + new_bt = bt.reject { |line| line =~ /lib\/minitest/ } if new_bt.empty? + new_bt = bt.dup if new_bt.empty? + else + new_bt = bt.dup + end + + new_bt + end + end + + self.backtrace_filter = BacktraceFilter.new + + def self.filter_backtrace bt # :nodoc: + backtrace_filter.filter bt + end + + ## + # MiniTest Assertions. All assertion methods accept a +msg+ which is + # printed if the assertion fails. + + module Assertions + UNDEFINED = Object.new # :nodoc: + + def UNDEFINED.inspect # :nodoc: + "UNDEFINED" # again with the rdoc bugs... :( + end + + ## + # Returns the diff command to use in #diff. Tries to intelligently + # figure out what diff to use. + + def self.diff + @diff = if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ && + system("diff.exe", __FILE__, __FILE__)) then + "diff.exe -u" + elsif Minitest::Unit::Guard.maglev? then # HACK + "diff -u" + elsif system("gdiff", __FILE__, __FILE__) + "gdiff -u" # solaris and kin suck + elsif system("diff", __FILE__, __FILE__) + "diff -u" + else + nil + end unless defined? @diff + + @diff + end + + ## + # Set the diff command to use in #diff. + + def self.diff= o + @diff = o + end + + ## + # Returns a diff between +exp+ and +act+. If there is no known + # diff command or if it doesn't make sense to diff the output + # (single line, short output), then it simply returns a basic + # comparison between the two. + + def diff exp, act + require "tempfile" + + expect = mu_pp_for_diff exp + butwas = mu_pp_for_diff act + result = nil + + need_to_diff = + MiniTest::Assertions.diff && + (expect.include?("\n") || + butwas.include?("\n") || + expect.size > 30 || + butwas.size > 30 || + expect == butwas) + + return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless + need_to_diff + + Tempfile.open("expect") do |a| + a.puts expect + a.flush + + Tempfile.open("butwas") do |b| + b.puts butwas + b.flush + + result = `#{MiniTest::Assertions.diff} #{a.path} #{b.path}` + result.sub!(/^\-\-\- .+/, "--- expected") + result.sub!(/^\+\+\+ .+/, "+++ actual") + + if result.empty? then + klass = exp.class + result = [ + "No visible difference in the #{klass}#inspect output.\n", + "You should look at the implementation of #== on ", + "#{klass} or its members.\n", + expect, + ].join + end + end + end + + result + end + + ## + # This returns a human-readable version of +obj+. By default + # #inspect is called. You can override this to use #pretty_print + # if you want. + + def mu_pp obj + s = obj.inspect + s = s.encode Encoding.default_external if defined? Encoding + s + end + + ## + # This returns a diff-able human-readable version of +obj+. This + # differs from the regular mu_pp because it expands escaped + # newlines and makes hex-values generic (like object_ids). This + # uses mu_pp to do the first pass and then cleans it up. + + def mu_pp_for_diff obj + mu_pp(obj).gsub(/\\n/, "\n").gsub(/:0x[a-fA-F0-9]{4,}/m, ':0xXXXXXX') + end + + def _assertions= n # :nodoc: + @_assertions = n + end + + def _assertions # :nodoc: + @_assertions ||= 0 + end + + ## + # Fails unless +test+ is a true value. + + def assert test, msg = nil + msg ||= "Failed assertion, no message given." + self._assertions += 1 + unless test then + msg = msg.call if Proc === msg + raise MiniTest::Assertion, msg + end + true + end + + ## + # Fails unless +obj+ is empty. + + def assert_empty obj, msg = nil + msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" } + assert_respond_to obj, :empty? + assert obj.empty?, msg + end + + ## + # Fails unless exp == act printing the difference between + # the two, if possible. + # + # If there is no visible difference but the assertion fails, you + # should suspect that your #== is buggy, or your inspect output is + # missing crucial details. + # + # For floats use assert_in_delta. + # + # See also: MiniTest::Assertions.diff + + def assert_equal exp, act, msg = nil + msg = message(msg, "") { diff exp, act } + assert exp == act, msg + end + + ## + # For comparing Floats. Fails unless +exp+ and +act+ are within +delta+ + # of each other. + # + # assert_in_delta Math::PI, (22.0 / 7.0), 0.01 + + def assert_in_delta exp, act, delta = 0.001, msg = nil + n = (exp - act).abs + msg = message(msg) { + "Expected |#{exp} - #{act}| (#{n}) to be <= #{delta}" + } + assert delta >= n, msg + end + + ## + # For comparing Floats. Fails unless +exp+ and +act+ have a relative + # error less than +epsilon+. + + def assert_in_epsilon a, b, epsilon = 0.001, msg = nil + assert_in_delta a, b, [a.abs, b.abs].min * epsilon, msg + end + + ## + # Fails unless +collection+ includes +obj+. + + def assert_includes collection, obj, msg = nil + msg = message(msg) { + "Expected #{mu_pp(collection)} to include #{mu_pp(obj)}" + } + assert_respond_to collection, :include? + assert collection.include?(obj), msg + end + + ## + # Fails unless +obj+ is an instance of +cls+. + + def assert_instance_of cls, obj, msg = nil + msg = message(msg) { + "Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}" + } + + assert obj.instance_of?(cls), msg + end + + ## + # Fails unless +obj+ is a kind of +cls+. + + def assert_kind_of cls, obj, msg = nil # TODO: merge with instance_of + msg = message(msg) { + "Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" } + + assert obj.kind_of?(cls), msg + end + + ## + # Fails unless +matcher+ =~ +obj+. + + def assert_match matcher, obj, msg = nil + msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" } + assert_respond_to matcher, :"=~" + matcher = Regexp.new Regexp.escape matcher if String === matcher + assert matcher =~ obj, msg + end + + ## + # Fails unless +obj+ is nil + + def assert_nil obj, msg = nil + msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" } + assert obj.nil?, msg + end + + ## + # For testing with binary operators. + # + # assert_operator 5, :<=, 4 + + def assert_operator o1, op, o2 = UNDEFINED, msg = nil + return assert_predicate o1, op, msg if UNDEFINED == o2 + msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" } + assert o1.__send__(op, o2), msg + end + + ## + # Fails if stdout or stderr do not output the expected results. + # Pass in nil if you don't care about that streams output. Pass in + # "" if you require it to be silent. Pass in a regexp if you want + # to pattern match. + # + # NOTE: this uses #capture_io, not #capture_subprocess_io. + # + # See also: #assert_silent + + def assert_output stdout = nil, stderr = nil + out, err = capture_io do + yield + end + + err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr + out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout + + y = send err_msg, stderr, err, "In stderr" if err_msg + x = send out_msg, stdout, out, "In stdout" if out_msg + + (!stdout || x) && (!stderr || y) + end + + ## + # For testing with predicates. + # + # assert_predicate str, :empty? + # + # This is really meant for specs and is front-ended by assert_operator: + # + # str.must_be :empty? + + def assert_predicate o1, op, msg = nil + msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op}" } + assert o1.__send__(op), msg + end + + ## + # Fails unless the block raises one of +exp+. Returns the + # exception matched so you can check the message, attributes, etc. + + def assert_raises *exp + msg = "#{exp.pop}.\n" if String === exp.last + + begin + yield + rescue MiniTest::Skip => e + return e if exp.include? MiniTest::Skip + raise e + rescue Exception => e + expected = exp.any? { |ex| + if ex.instance_of? Module then + e.kind_of? ex + else + e.instance_of? ex + end + } + + assert expected, proc { + exception_details(e, "#{msg}#{mu_pp(exp)} exception expected, not") + } + + return e + end + + exp = exp.first if exp.size == 1 + + flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised." + end + + ## + # Fails unless +obj+ responds to +meth+. + + def assert_respond_to obj, meth, msg = nil + msg = message(msg) { + "Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}" + } + assert obj.respond_to?(meth), msg + end + + ## + # Fails unless +exp+ and +act+ are #equal? + + def assert_same exp, act, msg = nil + msg = message(msg) { + data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id] + "Expected %s (oid=%d) to be the same as %s (oid=%d)" % data + } + assert exp.equal?(act), msg + end + + ## + # +send_ary+ is a receiver, message and arguments. + # + # Fails unless the call returns a true value + # TODO: I should prolly remove this from specs + + def assert_send send_ary, m = nil + recv, msg, *args = send_ary + m = message(m) { + "Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" } + assert recv.__send__(msg, *args), m + end + + ## + # Fails if the block outputs anything to stderr or stdout. + # + # See also: #assert_output + + def assert_silent + assert_output "", "" do + yield + end + end + + ## + # Fails unless the block throws +sym+ + + def assert_throws sym, msg = nil + default = "Expected #{mu_pp(sym)} to have been thrown" + caught = true + catch(sym) do + begin + yield + rescue ThreadError => e # wtf?!? 1.8 + threads == suck + default += ", not \:#{e.message[/uncaught throw \`(\w+?)\'/, 1]}" + rescue ArgumentError => e # 1.9 exception + default += ", not #{e.message.split(/ /).last}" + rescue NameError => e # 1.8 exception + default += ", not #{e.name.inspect}" + end + caught = false + end + + assert caught, message(msg) { default } + end + + ## + # Captures $stdout and $stderr into strings: + # + # out, err = capture_io do + # puts "Some info" + # warn "You did a bad thing" + # end + # + # assert_match %r%info%, out + # assert_match %r%bad%, err + # + # NOTE: For efficiency, this method uses StringIO and does not + # capture IO for subprocesses. Use #capture_subprocess_io for + # that. + + def capture_io + require 'stringio' + + captured_stdout, captured_stderr = StringIO.new, StringIO.new + + synchronize do + orig_stdout, orig_stderr = $stdout, $stderr + $stdout, $stderr = captured_stdout, captured_stderr + + begin + yield + ensure + $stdout = orig_stdout + $stderr = orig_stderr + end + end + + return captured_stdout.string, captured_stderr.string + end + + ## + # Captures $stdout and $stderr into strings, using Tempfile to + # ensure that subprocess IO is captured as well. + # + # out, err = capture_subprocess_io do + # system "echo Some info" + # system "echo You did a bad thing 1>&2" + # end + # + # assert_match %r%info%, out + # assert_match %r%bad%, err + # + # NOTE: This method is approximately 10x slower than #capture_io so + # only use it when you need to test the output of a subprocess. + + def capture_subprocess_io + require 'tempfile' + + captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err") + + synchronize do + orig_stdout, orig_stderr = $stdout.dup, $stderr.dup + $stdout.reopen captured_stdout + $stderr.reopen captured_stderr + + begin + yield + + $stdout.rewind + $stderr.rewind + + [captured_stdout.read, captured_stderr.read] + ensure + captured_stdout.unlink + captured_stderr.unlink + $stdout.reopen orig_stdout + $stderr.reopen orig_stderr + end + end + end + + ## + # Returns details for exception +e+ + + def exception_details e, msg + [ + "#{msg}", + "Class: <#{e.class}>", + "Message: <#{e.message.inspect}>", + "---Backtrace---", + "#{MiniTest::filter_backtrace(e.backtrace).join("\n")}", + "---------------", + ].join "\n" + end + + ## + # Fails with +msg+ + + def flunk msg = nil + msg ||= "Epic Fail!" + assert false, msg + end + + ## + # Returns a proc that will output +msg+ along with the default message. + + def message msg = nil, ending = ".", &default + proc { + msg = msg.call.chomp(".") if Proc === msg + custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty? + "#{custom_message}#{default.call}#{ending}" + } + end + + ## + # used for counting assertions + + def pass msg = nil + assert true + end + + ## + # Fails if +test+ is a true value + + def refute test, msg = nil + msg ||= "Failed refutation, no message given" + not assert(! test, msg) + end + + ## + # Fails if +obj+ is empty. + + def refute_empty obj, msg = nil + msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" } + assert_respond_to obj, :empty? + refute obj.empty?, msg + end + + ## + # Fails if exp == act. + # + # For floats use refute_in_delta. + + def refute_equal exp, act, msg = nil + msg = message(msg) { + "Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}" + } + refute exp == act, msg + end + + ## + # For comparing Floats. Fails if +exp+ is within +delta+ of +act+. + # + # refute_in_delta Math::PI, (22.0 / 7.0) + + def refute_in_delta exp, act, delta = 0.001, msg = nil + n = (exp - act).abs + msg = message(msg) { + "Expected |#{exp} - #{act}| (#{n}) to not be <= #{delta}" + } + refute delta >= n, msg + end + + ## + # For comparing Floats. Fails if +exp+ and +act+ have a relative error + # less than +epsilon+. + + def refute_in_epsilon a, b, epsilon = 0.001, msg = nil + refute_in_delta a, b, a * epsilon, msg + end + + ## + # Fails if +collection+ includes +obj+. + + def refute_includes collection, obj, msg = nil + msg = message(msg) { + "Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}" + } + assert_respond_to collection, :include? + refute collection.include?(obj), msg + end + + ## + # Fails if +obj+ is an instance of +cls+. + + def refute_instance_of cls, obj, msg = nil + msg = message(msg) { + "Expected #{mu_pp(obj)} to not be an instance of #{cls}" + } + refute obj.instance_of?(cls), msg + end + + ## + # Fails if +obj+ is a kind of +cls+. + + def refute_kind_of cls, obj, msg = nil # TODO: merge with instance_of + msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" } + refute obj.kind_of?(cls), msg + end + + ## + # Fails if +matcher+ =~ +obj+. + + def refute_match matcher, obj, msg = nil + msg = message(msg) {"Expected #{mu_pp matcher} to not match #{mu_pp obj}"} + assert_respond_to matcher, :"=~" + matcher = Regexp.new Regexp.escape matcher if String === matcher + refute matcher =~ obj, msg + end + + ## + # Fails if +obj+ is nil. + + def refute_nil obj, msg = nil + msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" } + refute obj.nil?, msg + end + + ## + # Fails if +o1+ is not +op+ +o2+. Eg: + # + # refute_operator 1, :>, 2 #=> pass + # refute_operator 1, :<, 2 #=> fail + + def refute_operator o1, op, o2 = UNDEFINED, msg = nil + return refute_predicate o1, op, msg if UNDEFINED == o2 + msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}"} + refute o1.__send__(op, o2), msg + end + + ## + # For testing with predicates. + # + # refute_predicate str, :empty? + # + # This is really meant for specs and is front-ended by refute_operator: + # + # str.wont_be :empty? + + def refute_predicate o1, op, msg = nil + msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op}" } + refute o1.__send__(op), msg + end + + ## + # Fails if +obj+ responds to the message +meth+. + + def refute_respond_to obj, meth, msg = nil + msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" } + + refute obj.respond_to?(meth), msg + end + + ## + # Fails if +exp+ is the same (by object identity) as +act+. + + def refute_same exp, act, msg = nil + msg = message(msg) { + data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id] + "Expected %s (oid=%d) to not be the same as %s (oid=%d)" % data + } + refute exp.equal?(act), msg + end + + ## + # Skips the current test. Gets listed at the end of the run but + # doesn't cause a failure exit code. + + def skip msg = nil, bt = caller + msg ||= "Skipped, no message given" + @skip = true + raise MiniTest::Skip, msg, bt + end + + ## + # Was this testcase skipped? Meant for #teardown. + + def skipped? + defined?(@skip) and @skip + end + + ## + # Takes a block and wraps it with the runner's shared mutex. + + def synchronize + Minitest::Unit.runner.synchronize do + yield + end + end + end + + class Unit # :nodoc: + VERSION = "4.7.5" # :nodoc: + + attr_accessor :report, :failures, :errors, :skips # :nodoc: + attr_accessor :assertion_count # :nodoc: + attr_writer :test_count # :nodoc: + attr_accessor :start_time # :nodoc: + attr_accessor :help # :nodoc: + attr_accessor :verbose # :nodoc: + attr_writer :options # :nodoc: + + ## + # :attr: + # + # if true, installs an "INFO" signal handler (only available to BSD and + # OS X users) which prints diagnostic information about the test run. + # + # This is auto-detected by default but may be overridden by custom + # runners. + + attr_accessor :info_signal + + ## + # Lazy accessor for options. + + def options + @options ||= {} + end + + @@installed_at_exit ||= false + @@out = $stdout + @@after_tests = [] + + ## + # A simple hook allowing you to run a block of code after _all_ of + # the tests are done. Eg: + # + # MiniTest::Unit.after_tests { p $debugging_info } + + def self.after_tests &block + @@after_tests << block + end + + ## + # Registers MiniTest::Unit to run tests at process exit + + def self.autorun + at_exit { + # don't run if there was a non-exit exception + next if $! and not $!.kind_of? SystemExit + + # the order here is important. The at_exit handler must be + # installed before anyone else gets a chance to install their + # own, that way we can be assured that our exit will be last + # to run (at_exit stacks). + exit_code = nil + + at_exit { + @@after_tests.reverse_each(&:call) + exit false if exit_code && exit_code != 0 + } + + exit_code = MiniTest::Unit.new.run ARGV + } unless @@installed_at_exit + @@installed_at_exit = true + end + + ## + # Returns the stream to use for output. + + def self.output + @@out + end + + ## + # Sets MiniTest::Unit to write output to +stream+. $stdout is the default + # output + + def self.output= stream + @@out = stream + end + + ## + # Tells MiniTest::Unit to delegate to +runner+, an instance of a + # MiniTest::Unit subclass, when MiniTest::Unit#run is called. + + def self.runner= runner + @@runner = runner + end + + ## + # Returns the MiniTest::Unit subclass instance that will be used + # to run the tests. A MiniTest::Unit instance is the default + # runner. + + def self.runner + @@runner ||= self.new + end + + ## + # Return all plugins' run methods (methods that start with "run_"). + + def self.plugins + @@plugins ||= (["run_tests"] + + public_instance_methods(false). + grep(/^run_/).map { |s| s.to_s }).uniq + end + + ## + # Return the IO for output. + + def output + self.class.output + end + + def puts *a # :nodoc: + output.puts(*a) + end + + def print *a # :nodoc: + output.print(*a) + end + + def test_count # :nodoc: + @test_count ||= 0 + end + + ## + # Runner for a given +type+ (eg, test vs bench). + + def _run_anything type + suites = TestCase.send "#{type}_suites" + return if suites.empty? + + start = Time.now + + puts + puts "# Running #{type}s:" + puts + + @test_count, @assertion_count = 0, 0 + sync = output.respond_to? :"sync=" # stupid emacs + old_sync, output.sync = output.sync, true if sync + + results = _run_suites suites, type + + @test_count = results.inject(0) { |sum, (tc, _)| sum + tc } + @assertion_count = results.inject(0) { |sum, (_, ac)| sum + ac } + + output.sync = old_sync if sync + + t = Time.now - start + + puts + puts + puts "Finished #{type}s in %.6fs, %.4f tests/s, %.4f assertions/s." % + [t, test_count / t, assertion_count / t] + + report.each_with_index do |msg, i| + puts "\n%3d) %s" % [i + 1, msg] + end + + puts + + status + end + + ## + # Runs all the +suites+ for a given +type+. + # + # NOTE: this method is redefined in parallel_each.rb, which is + # loaded if a test-suite calls parallelize_me!. + + def _run_suites suites, type + suites.map { |suite| _run_suite suite, type } + end + + ## + # Run a single +suite+ for a given +type+. + + def _run_suite suite, type + header = "#{type}_suite_header" + puts send(header, suite) if respond_to? header + + filter = options[:filter] || '/./' + filter = Regexp.new $1 if filter =~ /\/(.*)\// + + all_test_methods = suite.send "#{type}_methods" + + filtered_test_methods = all_test_methods.find_all { |m| + filter === m || filter === "#{suite}##{m}" + } + + assertions = filtered_test_methods.map { |method| + inst = suite.new method + inst._assertions = 0 + + print "#{suite}##{method} = " if @verbose + + start_time = Time.now if @verbose + result = inst.run self + + print "%.2f s = " % (Time.now - start_time) if @verbose + print result + puts if @verbose + + inst._assertions + } + + return assertions.size, assertions.inject(0) { |sum, n| sum + n } + end + + ## + # Record the result of a single test. Makes it very easy to gather + # information. Eg: + # + # class StatisticsRecorder < MiniTest::Unit + # def record suite, method, assertions, time, error + # # ... record the results somewhere ... + # end + # end + # + # MiniTest::Unit.runner = StatisticsRecorder.new + # + # NOTE: record might be sent more than once per test. It will be + # sent once with the results from the test itself. If there is a + # failure or error in teardown, it will be sent again with the + # error or failure. + + def record suite, method, assertions, time, error + end + + def location e # :nodoc: + last_before_assertion = "" + e.backtrace.reverse_each do |s| + break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/ + last_before_assertion = s + end + last_before_assertion.sub(/:in .*$/, '') + end + + ## + # Writes status for failed test +meth+ in +klass+ which finished with + # exception +e+ + + def puke klass, meth, e + e = case e + when MiniTest::Skip then + @skips += 1 + return "S" unless @verbose + "Skipped:\n#{klass}##{meth} [#{location e}]:\n#{e.message}\n" + when MiniTest::Assertion then + @failures += 1 + "Failure:\n#{klass}##{meth} [#{location e}]:\n#{e.message}\n" + else + @errors += 1 + bt = MiniTest::filter_backtrace(e.backtrace).join "\n " + "Error:\n#{klass}##{meth}:\n#{e.class}: #{e.message}\n #{bt}\n" + end + @report << e + e[0, 1] + end + + def initialize # :nodoc: + @report = [] + @errors = @failures = @skips = 0 + @verbose = false + @mutex = defined?(Mutex) ? Mutex.new : nil + @info_signal = Signal.list['INFO'] + end + + def synchronize # :nodoc: + if @mutex then + @mutex.synchronize { yield } + else + yield + end + end + + def process_args args = [] # :nodoc: + options = {} + orig_args = args.dup + + OptionParser.new do |opts| + opts.banner = 'minitest options:' + opts.version = MiniTest::Unit::VERSION + + opts.on '-h', '--help', 'Display this help.' do + puts opts + exit + end + + opts.on '-s', '--seed SEED', Integer, "Sets random seed" do |m| + options[:seed] = m.to_i + end + + opts.on '-v', '--verbose', "Verbose. Show progress processing files." do + options[:verbose] = true + end + + opts.on '-n', '--name PATTERN', "Filter test names on pattern (e.g. /foo/)" do |a| + options[:filter] = a + end + + opts.parse! args + orig_args -= args + end + + unless options[:seed] then + srand + options[:seed] = srand % 0xFFFF + orig_args << "--seed" << options[:seed].to_s + end + + srand options[:seed] + + self.verbose = options[:verbose] + @help = orig_args.map { |s| s =~ /[\s|&<>$()]/ ? s.inspect : s }.join " " + + options + end + + ## + # Begins the full test run. Delegates to +runner+'s #_run method. + + def run args = [] + self.class.runner._run(args) + end + + ## + # Top level driver, controls all output and filtering. + + def _run args = [] + args = process_args args # ARGH!! blame test/unit process_args + self.options.merge! args + + puts "Run options: #{help}" + + self.class.plugins.each do |plugin| + send plugin + break unless report.empty? + end + + return failures + errors if self.test_count > 0 # or return nil... + rescue Interrupt + abort 'Interrupted' + end + + ## + # Runs test suites matching +filter+. + + def run_tests + _run_anything :test + end + + ## + # Writes status to +io+ + + def status io = self.output + format = "%d tests, %d assertions, %d failures, %d errors, %d skips" + io.puts format % [test_count, assertion_count, failures, errors, skips] + end + + ## + # Provides a simple set of guards that you can use in your tests + # to skip execution if it is not applicable. These methods are + # mixed into TestCase as both instance and class methods so you + # can use them inside or outside of the test methods. + # + # def test_something_for_mri + # skip "bug 1234" if jruby? + # # ... + # end + # + # if windows? then + # # ... lots of test methods ... + # end + + module Guard + + ## + # Is this running on jruby? + + def jruby? platform = RUBY_PLATFORM + "java" == platform + end + + ## + # Is this running on mri? + + def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE + "maglev" == platform + end + + module_function :maglev? + + ## + # Is this running on mri? + + def mri? platform = RUBY_DESCRIPTION + /^ruby/ =~ platform + end + + ## + # Is this running on rubinius? + + def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE + "rbx" == platform + end + + ## + # Is this running on windows? + + def windows? platform = RUBY_PLATFORM + /mswin|mingw/ =~ platform + end + end + + ## + # Provides before/after hooks for setup and teardown. These are + # meant for library writers, NOT for regular test authors. See + # #before_setup for an example. + + module LifecycleHooks + ## + # Runs before every test, after setup. This hook is meant for + # libraries to extend minitest. It is not meant to be used by + # test developers. + # + # See #before_setup for an example. + + def after_setup; end + + ## + # Runs before every test, before setup. This hook is meant for + # libraries to extend minitest. It is not meant to be used by + # test developers. + # + # As a simplistic example: + # + # module MyMinitestPlugin + # def before_setup + # super + # # ... stuff to do before setup is run + # end + # + # def after_setup + # # ... stuff to do after setup is run + # super + # end + # + # def before_teardown + # super + # # ... stuff to do before teardown is run + # end + # + # def after_teardown + # # ... stuff to do after teardown is run + # super + # end + # end + # + # class MiniTest::Unit::TestCase + # include MyMinitestPlugin + # end + + def before_setup; end + + ## + # Runs after every test, before teardown. This hook is meant for + # libraries to extend minitest. It is not meant to be used by + # test developers. + # + # See #before_setup for an example. + + def before_teardown; end + + ## + # Runs after every test, after teardown. This hook is meant for + # libraries to extend minitest. It is not meant to be used by + # test developers. + # + # See #before_setup for an example. + + def after_teardown; end + end + + ## + # Subclass TestCase to create your own tests. Typically you'll want a + # TestCase subclass per implementation class. + # + # See MiniTest::Assertions + + class TestCase + include LifecycleHooks + include Guard + extend Guard + + attr_reader :__name__ # :nodoc: + + PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, + Interrupt, SystemExit] # :nodoc: + + ## + # Runs the tests reporting the status to +runner+ + + def run runner + trap "INFO" do + runner.report.each_with_index do |msg, i| + warn "\n%3d) %s" % [i + 1, msg] + end + warn '' + time = runner.start_time ? Time.now - runner.start_time : 0 + warn "Current Test: %s#%s %.2fs" % [self.class, self.__name__, time] + runner.status $stderr + end if runner.info_signal + + start_time = Time.now + + result = "" + begin + @passed = nil + self.before_setup + self.setup + self.after_setup + self.run_test self.__name__ + result = "." unless io? + time = Time.now - start_time + runner.record self.class, self.__name__, self._assertions, time, nil + @passed = true + rescue *PASSTHROUGH_EXCEPTIONS + raise + rescue Exception => e + @passed = Skip === e + time = Time.now - start_time + runner.record self.class, self.__name__, self._assertions, time, e + result = runner.puke self.class, self.__name__, e + ensure + %w{ before_teardown teardown after_teardown }.each do |hook| + begin + self.send hook + rescue *PASSTHROUGH_EXCEPTIONS + raise + rescue Exception => e + @passed = false + runner.record self.class, self.__name__, self._assertions, time, e + result = runner.puke self.class, self.__name__, e + end + end + trap 'INFO', 'DEFAULT' if runner.info_signal + end + result + end + + alias :run_test :__send__ + + def initialize name # :nodoc: + @__name__ = name + @__io__ = nil + @passed = nil + @@current = self # FIX: make thread local + end + + def self.current # :nodoc: + @@current # FIX: make thread local + end + + ## + # Return the output IO object + + def io + @__io__ = true + MiniTest::Unit.output + end + + ## + # Have we hooked up the IO yet? + + def io? + @__io__ + end + + def self.reset # :nodoc: + @@test_suites = {} + end + + reset + + ## + # Call this at the top of your tests when you absolutely + # positively need to have ordered tests. In doing so, you're + # admitting that you suck and your tests are weak. + + def self.i_suck_and_my_tests_are_order_dependent! + class << self + undef_method :test_order if method_defined? :test_order + define_method :test_order do :alpha end + end + end + + ## + # Make diffs for this TestCase use #pretty_inspect so that diff + # in assert_equal can be more details. NOTE: this is much slower + # than the regular inspect but much more usable for complex + # objects. + + def self.make_my_diffs_pretty! + require 'pp' + + define_method :mu_pp do |o| + o.pretty_inspect + end + end + + ## + # Call this at the top of your tests when you want to run your + # tests in parallel. In doing so, you're admitting that you rule + # and your tests are awesome. + + def self.parallelize_me! + require "minitest/parallel_each" + + class << self + undef_method :test_order if method_defined? :test_order + define_method :test_order do :parallel end + end + end + + def self.inherited klass # :nodoc: + @@test_suites[klass] = true + super + end + + def self.test_order # :nodoc: + :random + end + + def self.test_suites # :nodoc: + @@test_suites.keys.sort_by { |ts| ts.name.to_s } + end + + def self.test_methods # :nodoc: + methods = public_instance_methods(true).grep(/^test/).map { |m| m.to_s } + + case self.test_order + when :parallel + max = methods.size + ParallelEach.new methods.sort.sort_by { rand max } + when :random then + max = methods.size + methods.sort.sort_by { rand max } + when :alpha, :sorted then + methods.sort + else + raise "Unknown test_order: #{self.test_order.inspect}" + end + end + + ## + # Returns true if the test passed. + + def passed? + @passed + end + + ## + # Runs before every test. Use this to set up before each test + # run. + + def setup; end + + ## + # Runs after every test. Use this to clean up after each test + # run. + + def teardown; end + + include MiniTest::Assertions + end # class TestCase + end # class Unit +end # module MiniTest + +Minitest = MiniTest # :nodoc: because ugh... I typo this all the time diff --git a/ruby/lib/ruby/2.1.0/mkmf.rb b/ruby/lib/ruby/2.1.0/mkmf.rb new file mode 100644 index 0000000..b408ed7 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/mkmf.rb @@ -0,0 +1,2627 @@ +# -*- coding: us-ascii -*- +# module to create Makefile for extension modules +# invoke like: ruby -r mkmf extconf.rb + +require 'rbconfig' +require 'fileutils' +require 'shellwords' + +# :stopdoc: +class String + # Wraps a string in escaped quotes if it contains whitespace. + def quote + /\s/ =~ self ? "\"#{self}\"" : "#{self}" + end + + # Escape whitespaces for Makefile. + def unspace + gsub(/\s/, '\\\\\\&') + end + + # Generates a string used as cpp macro name. + def tr_cpp + strip.upcase.tr_s("^A-Z0-9_*", "_").tr_s("*", "P") + end + + def funcall_style + /\)\z/ =~ self ? dup : "#{self}()" + end + + def sans_arguments + self[/\A[^()]+/] + end +end + +class Array + # Wraps all strings in escaped quotes if they contain whitespace. + def quote + map {|s| s.quote} + end +end +# :startdoc: + +## +# mkmf.rb is used by Ruby C extensions to generate a Makefile which will +# correctly compile and link the C extension to Ruby and a third-party +# library. +module MakeMakefile + #### defer until this module become global-state free. + # def self.extended(obj) + # obj.init_mkmf + # super + # end + # + # def initialize(*args, rbconfig: RbConfig, **rest) + # init_mkmf(rbconfig::MAKEFILE_CONFIG, rbconfig::CONFIG) + # super(*args, **rest) + # end + + ## + # The makefile configuration using the defaults from when Ruby was built. + + CONFIG = RbConfig::MAKEFILE_CONFIG + ORIG_LIBPATH = ENV['LIB'] + + ## + # Extensions for files compiled with a C compiler + + C_EXT = %w[c m] + + ## + # Extensions for files complied with a C++ compiler + + CXX_EXT = %w[cc mm cxx cpp] + if File::FNM_SYSCASE.zero? + CXX_EXT.concat(%w[C]) + end + + ## + # Extensions for source files + + SRC_EXT = C_EXT + CXX_EXT + + ## + # Extensions for header files + + HDR_EXT = %w[h hpp] + $static = nil + $config_h = '$(arch_hdrdir)/ruby/config.h' + $default_static = $static + + unless defined? $configure_args + $configure_args = {} + args = CONFIG["configure_args"] + if ENV["CONFIGURE_ARGS"] + args << " " << ENV["CONFIGURE_ARGS"] + end + for arg in Shellwords::shellwords(args) + arg, val = arg.split('=', 2) + next unless arg + arg.tr!('_', '-') + if arg.sub!(/^(?!--)/, '--') + val or next + arg.downcase! + end + next if /^--(?:top|topsrc|src|cur)dir$/ =~ arg + $configure_args[arg] = val || true + end + for arg in ARGV + arg, val = arg.split('=', 2) + next unless arg + arg.tr!('_', '-') + if arg.sub!(/^(?!--)/, '--') + val or next + arg.downcase! + end + $configure_args[arg] = val || true + end + end + + $libdir = CONFIG["libdir"] + $rubylibdir = CONFIG["rubylibdir"] + $archdir = CONFIG["archdir"] + $sitedir = CONFIG["sitedir"] + $sitelibdir = CONFIG["sitelibdir"] + $sitearchdir = CONFIG["sitearchdir"] + $vendordir = CONFIG["vendordir"] + $vendorlibdir = CONFIG["vendorlibdir"] + $vendorarchdir = CONFIG["vendorarchdir"] + + $mswin = /mswin/ =~ RUBY_PLATFORM + $bccwin = /bccwin/ =~ RUBY_PLATFORM + $mingw = /mingw/ =~ RUBY_PLATFORM + $cygwin = /cygwin/ =~ RUBY_PLATFORM + $netbsd = /netbsd/ =~ RUBY_PLATFORM + $os2 = /os2/ =~ RUBY_PLATFORM + $beos = /beos/ =~ RUBY_PLATFORM + $haiku = /haiku/ =~ RUBY_PLATFORM + $solaris = /solaris/ =~ RUBY_PLATFORM + $universal = /universal/ =~ RUBY_PLATFORM + $dest_prefix_pattern = (File::PATH_SEPARATOR == ';' ? /\A([[:alpha:]]:)?/ : /\A/) + + # :stopdoc: + + def config_string(key, config = CONFIG) + s = config[key] and !s.empty? and block_given? ? yield(s) : s + end + module_function :config_string + + def dir_re(dir) + Regexp.new('\$(?:\('+dir+'\)|\{'+dir+'\})(?:\$(?:\(target_prefix\)|\{target_prefix\}))?') + end + module_function :dir_re + + def relative_from(path, base) + dir = File.join(path, "") + if File.expand_path(dir) == File.expand_path(dir, base) + path + else + File.join(base, path) + end + end + + INSTALL_DIRS = [ + [dir_re('commondir'), "$(RUBYCOMMONDIR)"], + [dir_re('sitedir'), "$(RUBYCOMMONDIR)"], + [dir_re('vendordir'), "$(RUBYCOMMONDIR)"], + [dir_re('rubylibdir'), "$(RUBYLIBDIR)"], + [dir_re('archdir'), "$(RUBYARCHDIR)"], + [dir_re('sitelibdir'), "$(RUBYLIBDIR)"], + [dir_re('vendorlibdir'), "$(RUBYLIBDIR)"], + [dir_re('sitearchdir'), "$(RUBYARCHDIR)"], + [dir_re('vendorarchdir'), "$(RUBYARCHDIR)"], + [dir_re('rubyhdrdir'), "$(RUBYHDRDIR)"], + [dir_re('sitehdrdir'), "$(SITEHDRDIR)"], + [dir_re('vendorhdrdir'), "$(VENDORHDRDIR)"], + [dir_re('bindir'), "$(BINDIR)"], + ] + + def install_dirs(target_prefix = nil) + if $extout + dirs = [ + ['BINDIR', '$(extout)/bin'], + ['RUBYCOMMONDIR', '$(extout)/common'], + ['RUBYLIBDIR', '$(RUBYCOMMONDIR)$(target_prefix)'], + ['RUBYARCHDIR', '$(extout)/$(arch)$(target_prefix)'], + ['HDRDIR', '$(extout)/include/ruby$(target_prefix)'], + ['ARCHHDRDIR', '$(extout)/include/$(arch)/ruby$(target_prefix)'], + ['extout', "#$extout"], + ['extout_prefix', "#$extout_prefix"], + ] + elsif $extmk + dirs = [ + ['BINDIR', '$(bindir)'], + ['RUBYCOMMONDIR', '$(rubylibdir)'], + ['RUBYLIBDIR', '$(rubylibdir)$(target_prefix)'], + ['RUBYARCHDIR', '$(archdir)$(target_prefix)'], + ['HDRDIR', '$(rubyhdrdir)/ruby$(target_prefix)'], + ['ARCHHDRDIR', '$(rubyhdrdir)/$(arch)/ruby$(target_prefix)'], + ] + elsif $configure_args.has_key?('--vendor') + dirs = [ + ['BINDIR', '$(bindir)'], + ['RUBYCOMMONDIR', '$(vendordir)$(target_prefix)'], + ['RUBYLIBDIR', '$(vendorlibdir)$(target_prefix)'], + ['RUBYARCHDIR', '$(vendorarchdir)$(target_prefix)'], + ['HDRDIR', '$(rubyhdrdir)/ruby$(target_prefix)'], + ['ARCHHDRDIR', '$(rubyhdrdir)/$(arch)/ruby$(target_prefix)'], + ] + else + dirs = [ + ['BINDIR', '$(bindir)'], + ['RUBYCOMMONDIR', '$(sitedir)$(target_prefix)'], + ['RUBYLIBDIR', '$(sitelibdir)$(target_prefix)'], + ['RUBYARCHDIR', '$(sitearchdir)$(target_prefix)'], + ['HDRDIR', '$(rubyhdrdir)/ruby$(target_prefix)'], + ['ARCHHDRDIR', '$(rubyhdrdir)/$(arch)/ruby$(target_prefix)'], + ] + end + dirs << ['target_prefix', (target_prefix ? "/#{target_prefix}" : "")] + dirs + end + + def map_dir(dir, map = nil) + map ||= INSTALL_DIRS + map.inject(dir) {|d, (orig, new)| d.gsub(orig, new)} + end + + topdir = File.dirname(File.dirname(__FILE__)) + path = File.expand_path($0) + until (dir = File.dirname(path)) == path + if File.identical?(dir, topdir) + $extmk = true if %r"\A(?:ext|enc|tool|test)\z" =~ File.basename(path) + break + end + path = dir + end + $extmk ||= false + if not $extmk and File.exist?(($hdrdir = RbConfig::CONFIG["rubyhdrdir"]) + "/ruby/ruby.h") + $topdir = $hdrdir + $top_srcdir = $hdrdir + $arch_hdrdir = RbConfig::CONFIG["rubyarchhdrdir"] + elsif File.exist?(($hdrdir = ($top_srcdir ||= topdir) + "/include") + "/ruby.h") + $topdir ||= RbConfig::CONFIG["topdir"] + $arch_hdrdir = "$(extout)/include/$(arch)" + else + abort "mkmf.rb can't find header files for ruby at #{$hdrdir}/ruby.h" + end + + CONFTEST = "conftest".freeze + CONFTEST_C = "#{CONFTEST}.c" + + OUTFLAG = CONFIG['OUTFLAG'] + COUTFLAG = CONFIG['COUTFLAG'] + CPPOUTFILE = config_string('CPPOUTFILE') {|str| str.sub(/\bconftest\b/, CONFTEST)} + + def rm_f(*files) + opt = (Hash === files.last ? [files.pop] : []) + FileUtils.rm_f(Dir[*files.flatten], *opt) + end + module_function :rm_f + + def rm_rf(*files) + opt = (Hash === files.last ? [files.pop] : []) + FileUtils.rm_rf(Dir[*files.flatten], *opt) + end + module_function :rm_rf + + # Returns time stamp of the +target+ file if it exists and is newer than or + # equal to all of +times+. + def modified?(target, times) + (t = File.mtime(target)) rescue return nil + Array === times or times = [times] + t if times.all? {|n| n <= t} + end + + def split_libs(*strs) + strs.map {|s| s.split(/\s+(?=-|\z)/)}.flatten + end + + def merge_libs(*libs) + libs.inject([]) do |x, y| + y = y.inject([]) {|ary, e| ary.last == e ? ary : ary << e} + y.each_with_index do |v, yi| + if xi = x.rindex(v) + x[(xi+1)..-1] = merge_libs(y[(yi+1)..-1], x[(xi+1)..-1]) + x[xi, 0] = y[0...yi] + break + end + end and x.concat(y) + x + end + end + + # This is a custom logging module. It generates an mkmf.log file when you + # run your extconf.rb script. This can be useful for debugging unexpected + # failures. + # + # This module and its associated methods are meant for internal use only. + # + module Logging + @log = nil + @logfile = 'mkmf.log' + @orgerr = $stderr.dup + @orgout = $stdout.dup + @postpone = 0 + @quiet = $extmk + + def self::log_open + @log ||= File::open(@logfile, 'wb') + @log.sync = true + end + + def self::log_opened? + @log and not @log.closed? + end + + def self::open + log_open + $stderr.reopen(@log) + $stdout.reopen(@log) + yield + ensure + $stderr.reopen(@orgerr) + $stdout.reopen(@orgout) + end + + def self::message(*s) + log_open + @log.printf(*s) + end + + def self::logfile file + @logfile = file + log_close + end + + def self::log_close + if @log and not @log.closed? + @log.flush + @log.close + @log = nil + end + end + + def self::postpone + tmplog = "mkmftmp#{@postpone += 1}.log" + open do + log, *save = @log, @logfile, @orgout, @orgerr + @log, @logfile, @orgout, @orgerr = nil, tmplog, log, log + begin + log.print(open {yield @log}) + ensure + @log.close if @log and not @log.closed? + File::open(tmplog) {|t| FileUtils.copy_stream(t, log)} if File.exist?(tmplog) + @log, @logfile, @orgout, @orgerr = log, *save + @postpone -= 1 + MakeMakefile.rm_f tmplog + end + end + end + + class << self + attr_accessor :quiet + end + end + + def libpath_env + # used only if native compiling + if libpathenv = config_string("LIBPATHENV") + pathenv = ENV[libpathenv] + libpath = RbConfig.expand($DEFLIBPATH.join(File::PATH_SEPARATOR)) + {libpathenv => [libpath, pathenv].compact.join(File::PATH_SEPARATOR)} + else + {} + end + end + + def xsystem command, opts = nil + varpat = /\$\((\w+)\)|\$\{(\w+)\}/ + if varpat =~ command + vars = Hash.new {|h, k| h[k] = ENV[k]} + command = command.dup + nil while command.gsub!(varpat) {vars[$1||$2]} + end + Logging::open do + puts command.quote + if opts and opts[:werror] + result = nil + Logging.postpone do |log| + result = (system(libpath_env, command) and File.zero?(log.path)) + "" + end + result + else + system(libpath_env, command) + end + end + end + + def xpopen command, *mode, &block + Logging::open do + case mode[0] + when nil, /^r/ + puts "#{command} |" + else + puts "| #{command}" + end + IO.popen(libpath_env, command, *mode, &block) + end + end + + def log_src(src, heading="checked program was") + src = src.split(/^/) + fmt = "%#{src.size.to_s.size}d: %s" + Logging::message <<"EOM" +#{heading}: +/* begin */ +EOM + src.each_with_index {|line, no| Logging::message fmt, no+1, line} + Logging::message <<"EOM" +/* end */ + +EOM + end + + def create_tmpsrc(src) + src = "#{COMMON_HEADERS}\n#{src}" + src = yield(src) if block_given? + src.gsub!(/[ \t]+$/, '') + src.gsub!(/\A\n+|^\n+$/, '') + src.sub!(/[^\n]\z/, "\\&\n") + count = 0 + begin + open(CONFTEST_C, "wb") do |cfile| + cfile.print src + end + rescue Errno::EACCES + if (count += 1) < 5 + sleep 0.2 + retry + end + end + src + end + + def have_devel? + unless defined? $have_devel + $have_devel = true + $have_devel = try_link(MAIN_DOES_NOTHING) + end + $have_devel + end + + def try_do(src, command, *opts, &b) + unless have_devel? + raise < $hdrdir.quote, + 'src' => "#{CONFTEST_C}", + 'arch_hdrdir' => $arch_hdrdir.quote, + 'top_srcdir' => $top_srcdir.quote, + 'INCFLAGS' => "#$INCFLAGS", + 'CPPFLAGS' => "#$CPPFLAGS", + 'CFLAGS' => "#$CFLAGS", + 'ARCH_FLAG' => "#$ARCH_FLAG", + 'LDFLAGS' => "#$LDFLAGS #{ldflags}", + 'LOCAL_LIBS' => "#$LOCAL_LIBS #$libs", + 'LIBS' => "#{librubyarg} #{opt} #$LIBS") + conf['LIBPATH'] = libpathflag(libpath.map {|s| RbConfig::expand(s.dup, conf)}) + RbConfig::expand(TRY_LINK.dup, conf) + end + + def cc_command(opt="") + conf = RbConfig::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote, + 'arch_hdrdir' => $arch_hdrdir.quote, + 'top_srcdir' => $top_srcdir.quote) + RbConfig::expand("$(CC) #$INCFLAGS #$CPPFLAGS #$CFLAGS #$ARCH_FLAG #{opt} -c #{CONFTEST_C}", + conf) + end + + def cpp_command(outfile, opt="") + conf = RbConfig::CONFIG.merge('hdrdir' => $hdrdir.quote, 'srcdir' => $srcdir.quote, + 'arch_hdrdir' => $arch_hdrdir.quote, + 'top_srcdir' => $top_srcdir.quote) + if $universal and (arch_flag = conf['ARCH_FLAG']) and !arch_flag.empty? + conf['ARCH_FLAG'] = arch_flag.gsub(/(?:\G|\s)-arch\s+\S+/, '') + end + RbConfig::expand("$(CPP) #$INCFLAGS #$CPPFLAGS #$CFLAGS #{opt} #{CONFTEST_C} #{outfile}", + conf) + end + + def libpathflag(libpath=$DEFLIBPATH|$LIBPATH) + libpath.map{|x| + case x + when "$(topdir)", /\A\./ + LIBPATHFLAG + else + LIBPATHFLAG+RPATHFLAG + end % x.quote + }.join + end + + def with_werror(opt, opts = nil) + if opts + if opts[:werror] and config_string("WERRORFLAG") {|flag| opt = opt ? "#{opt} #{flag}" : flag} + (opts = opts.dup).delete(:werror) + end + yield(opt, opts) + else + yield(opt) + end + end + + def try_link0(src, opt="", *opts, &b) # :nodoc: + cmd = link_command("", opt) + if $universal + require 'tmpdir' + Dir.mktmpdir("mkmf_", oldtmpdir = ENV["TMPDIR"]) do |tmpdir| + begin + ENV["TMPDIR"] = tmpdir + try_do(src, cmd, *opts, &b) + ensure + ENV["TMPDIR"] = oldtmpdir + end + end + else + try_do(src, cmd, *opts, &b) + end and File.executable?(CONFTEST+$EXEEXT) + end + + # Returns whether or not the +src+ can be compiled as a C source and linked + # with its depending libraries successfully. +opt+ is passed to the linker + # as options. Note that +$CFLAGS+ and +$LDFLAGS+ are also passed to the + # linker. + # + # If a block given, it is called with the source before compilation. You can + # modify the source in the block. + # + # [+src+] a String which contains a C source + # [+opt+] a String which contains linker options + def try_link(src, opt="", *opts, &b) + try_link0(src, opt, *opts, &b) + ensure + MakeMakefile.rm_f "#{CONFTEST}*", "c0x32*" + end + + # Returns whether or not the +src+ can be compiled as a C source. +opt+ is + # passed to the C compiler as options. Note that +$CFLAGS+ is also passed to + # the compiler. + # + # If a block given, it is called with the source before compilation. You can + # modify the source in the block. + # + # [+src+] a String which contains a C source + # [+opt+] a String which contains compiler options + def try_compile(src, opt="", *opts, &b) + with_werror(opt, *opts) {|_opt, *_opts| try_do(src, cc_command(_opt), *_opts, &b)} and + File.file?("#{CONFTEST}.#{$OBJEXT}") + ensure + MakeMakefile.rm_f "#{CONFTEST}*" + end + + # Returns whether or not the +src+ can be preprocessed with the C + # preprocessor. +opt+ is passed to the preprocessor as options. Note that + # +$CFLAGS+ is also passed to the preprocessor. + # + # If a block given, it is called with the source before preprocessing. You + # can modify the source in the block. + # + # [+src+] a String which contains a C source + # [+opt+] a String which contains preprocessor options + def try_cpp(src, opt="", *opts, &b) + try_do(src, cpp_command(CPPOUTFILE, opt), *opts, &b) and + File.file?("#{CONFTEST}.i") + ensure + MakeMakefile.rm_f "#{CONFTEST}*" + end + + alias_method :try_header, (config_string('try_header') || :try_cpp) + + def cpp_include(header) + if header + header = [header] unless header.kind_of? Array + header.map {|h| String === h ? "#include <#{h}>\n" : h}.join + else + "" + end + end + + def with_cppflags(flags) + cppflags = $CPPFLAGS + $CPPFLAGS = flags + ret = yield + ensure + $CPPFLAGS = cppflags unless ret + end + + def try_cppflags(flags) + with_cppflags(flags) do + try_header("int main() {return 0;}") + end + end + + def with_cflags(flags) + cflags = $CFLAGS + $CFLAGS = flags + ret = yield + ensure + $CFLAGS = cflags unless ret + end + + def try_cflags(flags) + with_cflags(flags) do + try_compile("int main() {return 0;}") + end + end + + def with_ldflags(flags) + ldflags = $LDFLAGS + $LDFLAGS = flags + ret = yield + ensure + $LDFLAGS = ldflags unless ret + end + + def try_ldflags(flags) + with_ldflags(flags) do + try_link("int main() {return 0;}") + end + end + + def try_static_assert(expr, headers = nil, opt = "", &b) + headers = cpp_include(headers) + try_compile(< 0", headers, opt) + # positive constant + elsif try_static_assert("#{const} == 0", headers, opt) + return 0 + else + # not a constant + return nil + end + upper = 1 + lower = 0 + until try_static_assert("#{const} <= #{upper}", headers, opt) + lower = upper + upper <<= 1 + end + return nil unless lower + while upper > lower + 1 + mid = (upper + lower) / 2 + if try_static_assert("#{const} > #{mid}", headers, opt) + lower = mid + else + upper = mid + end + end + upper = -upper if neg + return upper + else + src = %{#{includes} +#include +/*top*/ +typedef#{neg ? '' : ' unsigned'} +#ifdef PRI_LL_PREFIX +#define PRI_CONFTEST_PREFIX PRI_LL_PREFIX +LONG_LONG +#else +#define PRI_CONFTEST_PREFIX "l" +long +#endif +conftest_type; +conftest_type conftest_const = (conftest_type)(#{const}); +int main() {printf("%"PRI_CONFTEST_PREFIX"#{neg ? 'd' : 'u'}\\n", conftest_const); return 0;} +} + begin + if try_link0(src, opt, &b) + xpopen("./#{CONFTEST}") do |f| + return Integer(f.gets) + end + end + ensure + MakeMakefile.rm_f "#{CONFTEST}*" + end + end + nil + end + + # You should use +have_func+ rather than +try_func+. + # + # [+func+] a String which contains a symbol name + # [+libs+] a String which contains library names. + # [+headers+] a String or an Array of strings which contains names of header + # files. + def try_func(func, libs, headers = nil, opt = "", &b) + headers = cpp_include(headers) + case func + when /^&/ + decltype = proc {|x|"const volatile void *#{x}"} + when /\)$/ + call = func + else + call = "#{func}()" + decltype = proc {|x| "void ((*#{x})())"} + end + if opt and !opt.empty? + [[:to_str], [:join, " "], [:to_s]].each do |meth, *args| + if opt.respond_to?(meth) + break opt = opt.send(meth, *args) + end + end + opt = "#{opt} #{libs}" + else + opt = libs + end + decltype && try_link(<<"SRC", opt, &b) or +#{headers} +/*top*/ +extern int t(void); +#{MAIN_DOES_NOTHING 't'} +int t(void) { #{decltype["volatile p"]}; p = (#{decltype[]})#{func}; return 0; } +SRC + call && try_link(<<"SRC", opt, &b) +#{headers} +/*top*/ +extern int t(void); +#{MAIN_DOES_NOTHING 't'} +int t(void) { #{call}; return 0; } +SRC + end + + # You should use +have_var+ rather than +try_var+. + def try_var(var, headers = nil, opt = "", &b) + headers = cpp_include(headers) + try_compile(<<"SRC", opt, &b) +#{headers} +/*top*/ +extern int t(void); +#{MAIN_DOES_NOTHING 't'} +int t(void) { const volatile void *volatile p; p = &(&#{var})[0]; return 0; } +SRC + end + + # Returns whether or not the +src+ can be preprocessed with the C + # preprocessor and matches with +pat+. + # + # If a block given, it is called with the source before compilation. You can + # modify the source in the block. + # + # [+pat+] a Regexp or a String + # [+src+] a String which contains a C source + # [+opt+] a String which contains preprocessor options + # + # NOTE: When pat is a Regexp the matching will be checked in process, + # otherwise egrep(1) will be invoked to check it. + def egrep_cpp(pat, src, opt = "", &b) + src = create_tmpsrc(src, &b) + xpopen(cpp_command('', opt)) do |f| + if Regexp === pat + puts(" ruby -ne 'print if #{pat.inspect}'") + f.grep(pat) {|l| + puts "#{f.lineno}: #{l}" + return true + } + false + else + puts(" egrep '#{pat}'") + begin + stdin = $stdin.dup + $stdin.reopen(f) + system("egrep", pat) + ensure + $stdin.reopen(stdin) + end + end + end + ensure + MakeMakefile.rm_f "#{CONFTEST}*" + log_src(src) + end + + # This is used internally by the have_macro? method. + def macro_defined?(macro, src, opt = "", &b) + src = src.sub(/[^\n]\z/, "\\&\n") + try_compile(src + <<"SRC", opt, &b) +/*top*/ +#ifndef #{macro} +# error +|:/ === #{macro} undefined === /:| +#endif +SRC + end + + # Returns whether or not: + # * the +src+ can be compiled as a C source, + # * the result object can be linked with its depending libraries + # successfully, + # * the linked file can be invoked as an executable + # * and the executable exits successfully + # + # +opt+ is passed to the linker as options. Note that +$CFLAGS+ and + # +$LDFLAGS+ are also passed to the linker. + # + # If a block given, it is called with the source before compilation. You can + # modify the source in the block. + # + # [+src+] a String which contains a C source + # [+opt+] a String which contains linker options + # + # Returns true when the executable exits successfully, false when it fails, + # or nil when preprocessing, compilation or link fails. + def try_run(src, opt = "", &b) + raise "cannot run test program while cross compiling" if CROSS_COMPILING + if try_link0(src, opt, &b) + xsystem("./#{CONFTEST}") + else + nil + end + ensure + MakeMakefile.rm_f "#{CONFTEST}*" + end + + def install_files(mfile, ifiles, map = nil, srcprefix = nil) + ifiles or return + ifiles.empty? and return + srcprefix ||= "$(srcdir)/#{srcprefix}".chomp('/') + RbConfig::expand(srcdir = srcprefix.dup) + dirs = [] + path = Hash.new {|h, i| h[i] = dirs.push([i])[-1]} + ifiles.each do |files, dir, prefix| + dir = map_dir(dir, map) + prefix &&= %r|\A#{Regexp.quote(prefix)}/?| + if /\A\.\// =~ files + # install files which are in current working directory. + files = files[2..-1] + len = nil + else + # install files which are under the $(srcdir). + files = File.join(srcdir, files) + len = srcdir.size + end + f = nil + Dir.glob(files) do |fx| + f = fx + f[0..len] = "" if len + case File.basename(f) + when *$NONINSTALLFILES + next + end + d = File.dirname(f) + d.sub!(prefix, "") if prefix + d = (d.empty? || d == ".") ? dir : File.join(dir, d) + f = File.join(srcprefix, f) if len + path[d] << f + end + unless len or f + d = File.dirname(files) + d.sub!(prefix, "") if prefix + d = (d.empty? || d == ".") ? dir : File.join(dir, d) + path[d] << files + end + end + dirs + end + + def install_rb(mfile, dest, srcdir = nil) + install_files(mfile, [["lib/**/*.rb", dest, "lib"]], nil, srcdir) + end + + def append_library(libs, lib) # :no-doc: + format(LIBARG, lib) + " " + libs + end + + def message(*s) + unless Logging.quiet and not $VERBOSE + printf(*s) + $stdout.flush + end + end + + # This emits a string to stdout that allows users to see the results of the + # various have* and find* methods as they are tested. + # + # Internal use only. + # + def checking_for(m, fmt = nil) + f = caller[0][/in `([^<].*)'$/, 1] and f << ": " #` for vim #' + m = "checking #{/\Acheck/ =~ f ? '' : 'for '}#{m}... " + message "%s", m + a = r = nil + Logging::postpone do + r = yield + a = (fmt ? "#{fmt % r}" : r ? "yes" : "no") << "\n" + "#{f}#{m}-------------------- #{a}\n" + end + message(a) + Logging::message "--------------------\n\n" + r + end + + def checking_message(target, place = nil, opt = nil) + [["in", place], ["with", opt]].inject("#{target}") do |msg, (pre, noun)| + if noun + [[:to_str], [:join, ","], [:to_s]].each do |meth, *args| + if noun.respond_to?(meth) + break noun = noun.send(meth, *args) + end + end + msg << " #{pre} #{noun}" unless noun.empty? + end + msg + end + end + + # :startdoc: + + # Returns whether or not +macro+ is defined either in the common header + # files or within any +headers+ you provide. + # + # Any options you pass to +opt+ are passed along to the compiler. + # + def have_macro(macro, headers = nil, opt = "", &b) + checking_for checking_message(macro, headers, opt) do + macro_defined?(macro, cpp_include(headers), opt, &b) + end + end + + # Returns whether or not the given entry point +func+ can be found within + # +lib+. If +func+ is +nil+, the main() entry point is used by + # default. If found, it adds the library to list of libraries to be used + # when linking your extension. + # + # If +headers+ are provided, it will include those header files as the + # header files it looks in when searching for +func+. + # + # The real name of the library to be linked can be altered by + # --with-FOOlib configuration option. + # + def have_library(lib, func = nil, headers = nil, opt = "", &b) + func = "main" if !func or func.empty? + lib = with_config(lib+'lib', lib) + checking_for checking_message(func.funcall_style, LIBARG%lib, opt) do + if COMMON_LIBS.include?(lib) + true + else + libs = append_library($libs, lib) + if try_func(func, libs, headers, opt, &b) + $libs = libs + true + else + false + end + end + end + end + + # Returns whether or not the entry point +func+ can be found within the + # library +lib+ in one of the +paths+ specified, where +paths+ is an array + # of strings. If +func+ is +nil+ , then the main() function is + # used as the entry point. + # + # If +lib+ is found, then the path it was found on is added to the list of + # library paths searched and linked against. + # + def find_library(lib, func, *paths, &b) + func = "main" if !func or func.empty? + lib = with_config(lib+'lib', lib) + paths = paths.collect {|path| path.split(File::PATH_SEPARATOR)}.flatten + checking_for checking_message(func.funcall_style, LIBARG%lib) do + libpath = $LIBPATH + libs = append_library($libs, lib) + begin + until r = try_func(func, libs, &b) or paths.empty? + $LIBPATH = libpath | [paths.shift] + end + if r + $libs = libs + libpath = nil + end + ensure + $LIBPATH = libpath if libpath + end + r + end + end + + # Returns whether or not the function +func+ can be found in the common + # header files, or within any +headers+ that you provide. If found, a macro + # is passed as a preprocessor constant to the compiler using the function + # name, in uppercase, prepended with +HAVE_+. + # + # To check functions in an additional library, you need to check that + # library first using have_library(). The +func+ shall be + # either mere function name or function name with arguments. + # + # For example, if have_func('foo') returned +true+, then the + # +HAVE_FOO+ preprocessor macro would be passed to the compiler. + # + def have_func(func, headers = nil, opt = "", &b) + checking_for checking_message(func.funcall_style, headers, opt) do + if try_func(func, $libs, headers, opt, &b) + $defs << "-DHAVE_#{func.sans_arguments.tr_cpp}" + true + else + false + end + end + end + + # Returns whether or not the variable +var+ can be found in the common + # header files, or within any +headers+ that you provide. If found, a macro + # is passed as a preprocessor constant to the compiler using the variable + # name, in uppercase, prepended with +HAVE_+. + # + # To check variables in an additional library, you need to check that + # library first using have_library(). + # + # For example, if have_var('foo') returned true, then the + # +HAVE_FOO+ preprocessor macro would be passed to the compiler. + # + def have_var(var, headers = nil, opt = "", &b) + checking_for checking_message(var, headers, opt) do + if try_var(var, headers, opt, &b) + $defs.push(format("-DHAVE_%s", var.tr_cpp)) + true + else + false + end + end + end + + # Returns whether or not the given +header+ file can be found on your system. + # If found, a macro is passed as a preprocessor constant to the compiler + # using the header file name, in uppercase, prepended with +HAVE_+. + # + # For example, if have_header('foo.h') returned true, then the + # +HAVE_FOO_H+ preprocessor macro would be passed to the compiler. + # + def have_header(header, preheaders = nil, opt = "", &b) + checking_for header do + if try_header(cpp_include(preheaders)+cpp_include(header), opt, &b) + $defs.push(format("-DHAVE_%s", header.tr_cpp)) + true + else + false + end + end + end + + # Returns whether or not the given +framework+ can be found on your system. + # If found, a macro is passed as a preprocessor constant to the compiler + # using the framework name, in uppercase, prepended with +HAVE_FRAMEWORK_+. + # + # For example, if have_framework('Ruby') returned true, then + # the +HAVE_FRAMEWORK_RUBY+ preprocessor macro would be passed to the + # compiler. + # + # If +fw+ is a pair of the framework name and its header file name + # that header file is checked, instead of the normally used header + # file which is named same as the framework. + def have_framework(fw, &b) + if Array === fw + fw, header = *fw + else + header = "#{fw}.h" + end + checking_for fw do + src = cpp_include("#{fw}/#{header}") << "\n" "int main(void){return 0;}" + opt = " -framework #{fw}" + if try_link(src, "-ObjC#{opt}", &b) + $defs.push(format("-DHAVE_FRAMEWORK_%s", fw.tr_cpp)) + # TODO: non-worse way than this hack, to get rid of separating + # option and its argument. + $LDFLAGS << " -ObjC" unless /(\A|\s)-ObjC(\s|\z)/ =~ $LDFLAGS + $LIBS << opt + true + else + false + end + end + end + + # Instructs mkmf to search for the given +header+ in any of the +paths+ + # provided, and returns whether or not it was found in those paths. + # + # If the header is found then the path it was found on is added to the list + # of included directories that are sent to the compiler (via the + # -I switch). + # + def find_header(header, *paths) + message = checking_message(header, paths) + header = cpp_include(header) + checking_for message do + if try_header(header) + true + else + found = false + paths.each do |dir| + opt = "-I#{dir}".quote + if try_header(header, opt) + $INCFLAGS << " " << opt + found = true + break + end + end + found + end + end + end + + # Returns whether or not the struct of type +type+ contains +member+. If + # it does not, or the struct type can't be found, then false is returned. + # You may optionally specify additional +headers+ in which to look for the + # struct (in addition to the common header files). + # + # If found, a macro is passed as a preprocessor constant to the compiler + # using the type name and the member name, in uppercase, prepended with + # +HAVE_+. + # + # For example, if have_struct_member('struct foo', 'bar') + # returned true, then the +HAVE_STRUCT_FOO_BAR+ preprocessor macro would be + # passed to the compiler. + # + # +HAVE_ST_BAR+ is also defined for backward compatibility. + # + def have_struct_member(type, member, headers = nil, opt = "", &b) + checking_for checking_message("#{type}.#{member}", headers) do + if try_compile(<<"SRC", opt, &b) +#{cpp_include(headers)} +/*top*/ +int s = (char *)&((#{type}*)0)->#{member} - (char *)0; +#{MAIN_DOES_NOTHING} +SRC + $defs.push(format("-DHAVE_%s_%s", type.tr_cpp, member.tr_cpp)) + $defs.push(format("-DHAVE_ST_%s", member.tr_cpp)) # backward compatibility + true + else + false + end + end + end + + # Returns whether or not the static type +type+ is defined. + # + # See also +have_type+ + # + def try_type(type, headers = nil, opt = "", &b) + if try_compile(<<"SRC", opt, &b) +#{cpp_include(headers)} +/*top*/ +typedef #{type} conftest_type; +int conftestval[sizeof(conftest_type)?1:-1]; +SRC + $defs.push(format("-DHAVE_TYPE_%s", type.tr_cpp)) + true + else + false + end + end + + # Returns whether or not the static type +type+ is defined. You may + # optionally pass additional +headers+ to check against in addition to the + # common header files. + # + # You may also pass additional flags to +opt+ which are then passed along to + # the compiler. + # + # If found, a macro is passed as a preprocessor constant to the compiler + # using the type name, in uppercase, prepended with +HAVE_TYPE_+. + # + # For example, if have_type('foo') returned true, then the + # +HAVE_TYPE_FOO+ preprocessor macro would be passed to the compiler. + # + def have_type(type, headers = nil, opt = "", &b) + checking_for checking_message(type, headers, opt) do + try_type(type, headers, opt, &b) + end + end + + # Returns where the static type +type+ is defined. + # + # You may also pass additional flags to +opt+ which are then passed along to + # the compiler. + # + # See also +have_type+. + # + def find_type(type, opt, *headers, &b) + opt ||= "" + fmt = "not found" + def fmt.%(x) + x ? x.respond_to?(:join) ? x.join(",") : x : self + end + checking_for checking_message(type, nil, opt), fmt do + headers.find do |h| + try_type(type, h, opt, &b) + end + end + end + + # Returns whether or not the constant +const+ is defined. + # + # See also +have_const+ + # + def try_const(const, headers = nil, opt = "", &b) + const, type = *const + if try_compile(<<"SRC", opt, &b) +#{cpp_include(headers)} +/*top*/ +typedef #{type || 'int'} conftest_type; +conftest_type conftestval = #{type ? '' : '(int)'}#{const}; +SRC + $defs.push(format("-DHAVE_CONST_%s", const.tr_cpp)) + true + else + false + end + end + + # Returns whether or not the constant +const+ is defined. You may + # optionally pass the +type+ of +const+ as [const, type], + # such as: + # + # have_const(%w[PTHREAD_MUTEX_INITIALIZER pthread_mutex_t], "pthread.h") + # + # You may also pass additional +headers+ to check against in addition to the + # common header files, and additional flags to +opt+ which are then passed + # along to the compiler. + # + # If found, a macro is passed as a preprocessor constant to the compiler + # using the type name, in uppercase, prepended with +HAVE_CONST_+. + # + # For example, if have_const('foo') returned true, then the + # +HAVE_CONST_FOO+ preprocessor macro would be passed to the compiler. + # + def have_const(const, headers = nil, opt = "", &b) + checking_for checking_message([*const].compact.join(' '), headers, opt) do + try_const(const, headers, opt, &b) + end + end + + # :stopdoc: + STRING_OR_FAILED_FORMAT = "%s" + def STRING_OR_FAILED_FORMAT.%(x) # :nodoc: + x ? super : "failed" + end + + def typedef_expr(type, headers) + typename, member = type.split('.', 2) + prelude = cpp_include(headers).split(/$/) + prelude << "typedef #{typename} rbcv_typedef_;\n" + return "rbcv_typedef_", member, prelude + end + + def try_signedness(type, member, headers = nil, opts = nil) + raise ArgumentError, "don't know how to tell signedness of members" if member + if try_static_assert("(#{type})-1 < 0", headers, opts) + return -1 + elsif try_static_assert("(#{type})-1 > 0", headers, opts) + return +1 + end + end + + # :startdoc: + + # Returns the size of the given +type+. You may optionally specify + # additional +headers+ to search in for the +type+. + # + # If found, a macro is passed as a preprocessor constant to the compiler + # using the type name, in uppercase, prepended with +SIZEOF_+, followed by + # the type name, followed by =X where "X" is the actual size. + # + # For example, if check_sizeof('mystruct') returned 12, then + # the SIZEOF_MYSTRUCT=12 preprocessor macro would be passed to + # the compiler. + # + def check_sizeof(type, headers = nil, opts = "", &b) + typedef, member, prelude = typedef_expr(type, headers) + prelude << "static #{typedef} *rbcv_ptr_;\n" + prelude = [prelude] + expr = "sizeof((*rbcv_ptr_)#{"." << member if member})" + fmt = STRING_OR_FAILED_FORMAT + checking_for checking_message("size of #{type}", headers), fmt do + if size = try_constant(expr, prelude, opts, &b) + $defs.push(format("-DSIZEOF_%s=%s", type.tr_cpp, size)) + size + end + end + end + + # Returns the signedness of the given +type+. You may optionally specify + # additional +headers+ to search in for the +type+. + # + # If the +type+ is found and is a numeric type, a macro is passed as a + # preprocessor constant to the compiler using the +type+ name, in uppercase, + # prepended with +SIGNEDNESS_OF_+, followed by the +type+ name, followed by + # =X where "X" is positive integer if the +type+ is unsigned + # and a negative integer if the +type+ is signed. + # + # For example, if +size_t+ is defined as unsigned, then + # check_signedness('size_t') would return +1 and the + # SIGNEDNESS_OF_SIZE_T=+1 preprocessor macro would be passed to + # the compiler. The SIGNEDNESS_OF_INT=-1 macro would be set + # for check_signedness('int') + # + def check_signedness(type, headers = nil, opts = nil, &b) + typedef, member, prelude = typedef_expr(type, headers) + signed = nil + checking_for("signedness of #{type}", STRING_OR_FAILED_FORMAT) do + signed = try_signedness(typedef, member, [prelude], opts, &b) or next nil + $defs.push("-DSIGNEDNESS_OF_%s=%+d" % [type.tr_cpp, signed]) + signed < 0 ? "signed" : "unsigned" + end + signed + end + + # Returns the convertible integer type of the given +type+. You may + # optionally specify additional +headers+ to search in for the +type+. + # _convertible_ means actually the same type, or typedef'd from the same + # type. + # + # If the +type+ is a integer type and the _convertible_ type is found, + # the following macros are passed as preprocessor constants to the compiler + # using the +type+ name, in uppercase. + # + # * +TYPEOF_+, followed by the +type+ name, followed by =X + # where "X" is the found _convertible_ type name. + # * +TYP2NUM+ and +NUM2TYP+, + # where +TYP+ is the +type+ name in uppercase with replacing an +_t+ + # suffix with "T", followed by =X where "X" is the macro name + # to convert +type+ to an Integer object, and vice versa. + # + # For example, if +foobar_t+ is defined as unsigned long, then + # convertible_int("foobar_t") would return "unsigned long", and + # define these macros: + # + # #define TYPEOF_FOOBAR_T unsigned long + # #define FOOBART2NUM ULONG2NUM + # #define NUM2FOOBART NUM2ULONG + # + def convertible_int(type, headers = nil, opts = nil, &b) + type, macname = *type + checking_for("convertible type of #{type}", STRING_OR_FAILED_FORMAT) do + if UNIVERSAL_INTS.include?(type) + type + else + typedef, member, prelude = typedef_expr(type, headers, &b) + if member + prelude << "static rbcv_typedef_ rbcv_var;" + compat = UNIVERSAL_INTS.find {|t| + try_static_assert("sizeof(rbcv_var.#{member}) == sizeof(#{t})", [prelude], opts, &b) + } + else + next unless signed = try_signedness(typedef, member, [prelude]) + u = "unsigned " if signed > 0 + prelude << "extern rbcv_typedef_ foo();" + compat = UNIVERSAL_INTS.find {|t| + try_compile([prelude, "extern #{u}#{t} foo();"].join("\n"), opts, :werror=>true, &b) + } + end + if compat + macname ||= type.sub(/_(?=t\z)/, '').tr_cpp + conv = (compat == "long long" ? "LL" : compat.upcase) + compat = "#{u}#{compat}" + typename = type.tr_cpp + $defs.push(format("-DSIZEOF_%s=SIZEOF_%s", typename, compat.tr_cpp)) + $defs.push(format("-DTYPEOF_%s=%s", typename, compat.quote)) + $defs.push(format("-DPRI_%s_PREFIX=PRI_%s_PREFIX", macname, conv)) + conv = (u ? "U" : "") + conv + $defs.push(format("-D%s2NUM=%s2NUM", macname, conv)) + $defs.push(format("-DNUM2%s=NUM2%s", macname, conv)) + compat + end + end + end + end + # :stopdoc: + + # Used internally by the what_type? method to determine if +type+ is a scalar + # pointer. + def scalar_ptr_type?(type, member = nil, headers = nil, &b) + try_compile(<<"SRC", &b) # pointer +#{cpp_include(headers)} +/*top*/ +volatile #{type} conftestval; +extern int t(void); +#{MAIN_DOES_NOTHING 't'} +int t(void) {return (int)(1-*(conftestval#{member ? ".#{member}" : ""}));} +SRC + end + + # Used internally by the what_type? method to determine if +type+ is a scalar + # pointer. + def scalar_type?(type, member = nil, headers = nil, &b) + try_compile(<<"SRC", &b) # pointer +#{cpp_include(headers)} +/*top*/ +volatile #{type} conftestval; +extern int t(void); +#{MAIN_DOES_NOTHING 't'} +int t(void) {return (int)(1-(conftestval#{member ? ".#{member}" : ""}));} +SRC + end + + # Used internally by the what_type? method to check if the _typeof_ GCC + # extension is available. + def have_typeof? + return $typeof if defined?($typeof) + $typeof = %w[__typeof__ typeof].find do |t| + try_compile(<--with-_config_ or + # --without-_config_ option. Returns +true+ if the with option is + # given, +false+ if the without option is given, and the default value + # otherwise. + # + # This can be useful for adding custom definitions, such as debug + # information. + # + # Example: + # + # if with_config("debug") + # $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG" + # end + # + def with_config(config, default=nil) + config = config.sub(/^--with[-_]/, '') + val = arg_config("--with-"+config) do + if arg_config("--without-"+config) + false + elsif block_given? + yield(config, default) + else + break default + end + end + case val + when "yes" + true + when "no" + false + else + val + end + end + + # Tests for the presence of an --enable-_config_ or + # --disable-_config_ option. Returns +true+ if the enable option is + # given, +false+ if the disable option is given, and the default value + # otherwise. + # + # This can be useful for adding custom definitions, such as debug + # information. + # + # Example: + # + # if enable_config("debug") + # $defs.push("-DOSSL_DEBUG") unless $defs.include? "-DOSSL_DEBUG" + # end + # + def enable_config(config, default=nil) + if arg_config("--enable-"+config) + true + elsif arg_config("--disable-"+config) + false + elsif block_given? + yield(config, default) + else + return default + end + end + + # Generates a header file consisting of the various macro definitions + # generated by other methods such as have_func and have_header. These are + # then wrapped in a custom #ifndef based on the +header+ file + # name, which defaults to "extconf.h". + # + # For example: + # + # # extconf.rb + # require 'mkmf' + # have_func('realpath') + # have_header('sys/utime.h') + # create_header + # create_makefile('foo') + # + # The above script would generate the following extconf.h file: + # + # #ifndef EXTCONF_H + # #define EXTCONF_H + # #define HAVE_REALPATH 1 + # #define HAVE_SYS_UTIME_H 1 + # #endif + # + # Given that the create_header method generates a file based on definitions + # set earlier in your extconf.rb file, you will probably want to make this + # one of the last methods you call in your script. + # + def create_header(header = "extconf.h") + message "creating %s\n", header + sym = header.tr_cpp + hdr = ["#ifndef #{sym}\n#define #{sym}\n"] + for line in $defs + case line + when /^-D([^=]+)(?:=(.*))?/ + hdr << "#define #$1 #{$2 ? Shellwords.shellwords($2)[0].gsub(/(?=\t+)/, "\\\n") : 1}\n" + when /^-U(.*)/ + hdr << "#undef #$1\n" + end + end + hdr << "#endif\n" + hdr = hdr.join("") + log_src(hdr, "#{header} is") + unless (IO.read(header) == hdr rescue false) + open(header, "wb") do |hfile| + hfile.write(hdr) + end + end + $extconf_h = header + end + + # Sets a +target+ name that the user can then use to configure various + # "with" options with on the command line by using that name. For example, + # if the target is set to "foo", then the user could use the + # --with-foo-dir command line option. + # + # You may pass along additional "include" or "lib" defaults via the + # +idefault+ and +ldefault+ parameters, respectively. + # + # Note that dir_config only adds to the list of places to search for + # libraries and include files. It does not link the libraries into your + # application. + # + def dir_config(target, idefault=nil, ldefault=nil) + if dir = with_config(target + "-dir", (idefault unless ldefault)) + defaults = Array === dir ? dir : dir.split(File::PATH_SEPARATOR) + idefault = ldefault = nil + end + + idir = with_config(target + "-include", idefault) + $arg_config.last[1] ||= "${#{target}-dir}/include" + ldir = with_config(target + "-lib", ldefault) + $arg_config.last[1] ||= "${#{target}-dir}/#{_libdir_basename}" + + idirs = idir ? Array === idir ? idir.dup : idir.split(File::PATH_SEPARATOR) : [] + if defaults + idirs.concat(defaults.collect {|d| d + "/include"}) + idir = ([idir] + idirs).compact.join(File::PATH_SEPARATOR) + end + unless idirs.empty? + idirs.collect! {|d| "-I" + d} + idirs -= Shellwords.shellwords($CPPFLAGS) + unless idirs.empty? + $CPPFLAGS = (idirs.quote << $CPPFLAGS).join(" ") + end + end + + ldirs = ldir ? Array === ldir ? ldir.dup : ldir.split(File::PATH_SEPARATOR) : [] + if defaults + ldirs.concat(defaults.collect {|d| "#{d}/#{_libdir_basename}"}) + ldir = ([ldir] + ldirs).compact.join(File::PATH_SEPARATOR) + end + $LIBPATH = ldirs | $LIBPATH + + [idir, ldir] + end + + # Returns compile/link information about an installed library in a + # tuple of [cflags, ldflags, libs], by using the + # command found first in the following commands: + # + # 1. If --with-{pkg}-config={command} is given via + # command line option: {command} {option} + # + # 2. {pkg}-config {option} + # + # 3. pkg-config {option} {pkg} + # + # Where {option} is, for instance, --cflags. + # + # The values obtained are appended to +$CFLAGS+, +$LDFLAGS+ and + # +$libs+. + # + # If an option argument is given, the config command is + # invoked with the option and a stripped output string is returned + # without modifying any of the global values mentioned above. + def pkg_config(pkg, option=nil) + if pkgconfig = with_config("#{pkg}-config") and find_executable0(pkgconfig) + # iff package specific config command is given + get = proc {|opt| `#{pkgconfig} --#{opt}`.strip} + elsif ($PKGCONFIG ||= + (pkgconfig = with_config("pkg-config", ("pkg-config" unless CROSS_COMPILING))) && + find_executable0(pkgconfig) && pkgconfig) and + system("#{$PKGCONFIG} --exists #{pkg}") + # default to pkg-config command + get = proc {|opt| `#{$PKGCONFIG} --#{opt} #{pkg}`.strip} + elsif find_executable0(pkgconfig = "#{pkg}-config") + # default to package specific config command, as a last resort. + get = proc {|opt| `#{pkgconfig} --#{opt}`.strip} + end + orig_ldflags = $LDFLAGS + if get and option + get[option] + elsif get and try_ldflags(ldflags = get['libs']) + cflags = get['cflags'] + libs = get['libs-only-l'] + ldflags = (Shellwords.shellwords(ldflags) - Shellwords.shellwords(libs)).quote.join(" ") + $CFLAGS += " " << cflags + $CXXFLAGS += " " << cflags + $LDFLAGS = [orig_ldflags, ldflags].join(' ') + $libs += " " << libs + Logging::message "package configuration for %s\n", pkg + Logging::message "cflags: %s\nldflags: %s\nlibs: %s\n\n", + cflags, ldflags, libs + [cflags, ldflags, libs] + else + Logging::message "package configuration for %s is not found\n", pkg + nil + end + end + + # :stopdoc: + + def with_destdir(dir) + dir = dir.sub($dest_prefix_pattern, '') + /\A\$[\(\{]/ =~ dir ? dir : "$(DESTDIR)"+dir + end + + # Converts forward slashes to backslashes. Aimed at MS Windows. + # + # Internal use only. + # + def winsep(s) + s.tr('/', '\\') + end + + # Converts native path to format acceptable in Makefile + # + # Internal use only. + # + if !CROSS_COMPILING + case CONFIG['build_os'] + when 'mingw32' + def mkintpath(path) + # mingw uses make from msys and it needs special care + # converts from C:\some\path to /C/some/path + path = path.dup + path.tr!('\\', '/') + path.sub!(/\A([A-Za-z]):(?=\/)/, '/\1') + path + end + when 'cygwin' + if CONFIG['target_os'] != 'cygwin' + def mkintpath(path) + IO.popen(["cygpath", "-u", path], &:read).chomp + end + end + end + end + unless method_defined?(:mkintpath) + def mkintpath(path) + path + end + end + + def configuration(srcdir) + mk = [] + vpath = $VPATH.dup + CONFIG["hdrdir"] ||= $hdrdir + mk << %{ +SHELL = /bin/sh + +# V=0 quiet, V=1 verbose. other values don't work. +V = 0 +Q1 = $(V:1=) +Q = $(Q1:0=@) +ECHO1 = $(V:1=@#{CONFIG['NULLCMD']}) +ECHO = $(ECHO1:0=@echo) + +#### Start of system configuration section. #### +#{"top_srcdir = " + $top_srcdir.sub(%r"\A#{Regexp.quote($topdir)}/", "$(topdir)/") if $extmk} +srcdir = #{srcdir.gsub(/\$\((srcdir)\)|\$\{(srcdir)\}/) {mkintpath(CONFIG[$1||$2]).unspace}} +topdir = #{mkintpath(topdir = $extmk ? CONFIG["topdir"] : $topdir).unspace} +hdrdir = #{(hdrdir = CONFIG["hdrdir"]) == topdir ? "$(topdir)" : mkintpath(hdrdir).unspace} +arch_hdrdir = #{$arch_hdrdir.quote} +PATH_SEPARATOR = #{CONFIG['PATH_SEPARATOR']} +VPATH = #{vpath.join(CONFIG['PATH_SEPARATOR'])} +} + if $extmk + mk << "RUBYLIB =\n""RUBYOPT = -\n" + end + prefix = mkintpath(CONFIG["prefix"]) + if destdir = prefix[$dest_prefix_pattern, 1] + mk << "\nDESTDIR = #{destdir}\n" + prefix = prefix[destdir.size..-1] + end + mk << "prefix = #{with_destdir(prefix).unspace}\n" + CONFIG.each do |key, var| + mk << "#{key} = #{with_destdir(mkintpath(var)).unspace}\n" if /.prefix$/ =~ key + end + CONFIG.each do |key, var| + next if /^abs_/ =~ key + next if /^(?:src|top|hdr)dir$/ =~ key + next unless /dir$/ =~ key + mk << "#{key} = #{with_destdir(var)}\n" + end + if !$extmk and !$configure_args.has_key?('--ruby') and + sep = config_string('BUILD_FILE_SEPARATOR') + sep = ":/=#{sep}" + else + sep = "" + end + possible_command = (proc {|s| s if /top_srcdir/ !~ s} unless $extmk) + extconf_h = $extconf_h ? "-DRUBY_EXTCONF_H=\\\"$(RUBY_EXTCONF_H)\\\" " : $defs.join(" ") << " " + headers = %w[ + $(hdrdir)/ruby.h + $(hdrdir)/ruby/ruby.h + $(hdrdir)/ruby/defines.h + $(hdrdir)/ruby/missing.h + $(hdrdir)/ruby/intern.h + $(hdrdir)/ruby/st.h + $(hdrdir)/ruby/subst.h + ] + if RULE_SUBST + headers.each {|h| h.sub!(/.*/, &RULE_SUBST.method(:%))} + end + headers << $config_h + headers << '$(RUBY_EXTCONF_H)' if $extconf_h + mk << %{ + +CC = #{CONFIG['CC']} +CXX = #{CONFIG['CXX']} +LIBRUBY = #{CONFIG['LIBRUBY']} +LIBRUBY_A = #{CONFIG['LIBRUBY_A']} +LIBRUBYARG_SHARED = #$LIBRUBYARG_SHARED +LIBRUBYARG_STATIC = #$LIBRUBYARG_STATIC +empty = +OUTFLAG = #{OUTFLAG}$(empty) +COUTFLAG = #{COUTFLAG}$(empty) + +RUBY_EXTCONF_H = #{$extconf_h} +cflags = #{CONFIG['cflags']} +optflags = #{CONFIG['optflags']} +debugflags = #{CONFIG['debugflags']} +warnflags = #{$warnflags} +CCDLFLAGS = #{$static ? '' : CONFIG['CCDLFLAGS']} +CFLAGS = $(CCDLFLAGS) #$CFLAGS $(ARCH_FLAG) +INCFLAGS = -I. #$INCFLAGS +DEFS = #{CONFIG['DEFS']} +CPPFLAGS = #{extconf_h}#{$CPPFLAGS} +CXXFLAGS = $(CCDLFLAGS) #$CXXFLAGS $(ARCH_FLAG) +ldflags = #{$LDFLAGS} +dldflags = #{$DLDFLAGS} #{CONFIG['EXTDLDFLAGS']} +ARCH_FLAG = #{$ARCH_FLAG} +DLDFLAGS = $(ldflags) $(dldflags) $(ARCH_FLAG) +LDSHARED = #{CONFIG['LDSHARED']} +LDSHAREDXX = #{config_string('LDSHAREDXX') || '$(LDSHARED)'} +AR = #{CONFIG['AR']} +EXEEXT = #{CONFIG['EXEEXT']} + +} + CONFIG.each do |key, val| + mk << "#{key} = #{val}\n" if /^RUBY.*NAME/ =~ key + end + mk << %{ +arch = #{CONFIG['arch']} +sitearch = #{CONFIG['sitearch']} +ruby_version = #{RbConfig::CONFIG['ruby_version']} +ruby = #{$ruby.sub(%r[\A#{Regexp.quote(RbConfig::CONFIG['bindir'])}(?=/|\z)]) {'$(bindir)'}} +RUBY = $(ruby#{sep}) +ruby_headers = #{headers.join(' ')} + +RM = #{config_string('RM', &possible_command) || '$(RUBY) -run -e rm -- -f'} +RM_RF = #{'$(RUBY) -run -e rm -- -rf'} +RMDIRS = #{config_string('RMDIRS', &possible_command) || '$(RUBY) -run -e rmdir -- -p'} +MAKEDIRS = #{config_string('MAKEDIRS', &possible_command) || '@$(RUBY) -run -e mkdir -- -p'} +INSTALL = #{config_string('INSTALL', &possible_command) || '@$(RUBY) -run -e install -- -vp'} +INSTALL_PROG = #{config_string('INSTALL_PROG') || '$(INSTALL) -m 0755'} +INSTALL_DATA = #{config_string('INSTALL_DATA') || '$(INSTALL) -m 0644'} +COPY = #{config_string('CP', &possible_command) || '@$(RUBY) -run -e cp -- -v'} +TOUCH = exit > + +#### End of system configuration section. #### + +preload = #{defined?($preload) && $preload ? $preload.join(' ') : ''} +} + if $nmake == ?b + mk.each do |x| + x.gsub!(/^(MAKEDIRS|INSTALL_(?:PROG|DATA))+\s*=.*\n/) do + "!ifndef " + $1 + "\n" + + $& + + "!endif\n" + end + end + end + mk + end + + def timestamp_file(name, target_prefix = nil) + if target_prefix + pat = [] + install_dirs.each do |n, d| + pat << n if /\$\(target_prefix\)\z/ =~ d + end + name = name.gsub(/\$\((#{pat.join("|")})\)/) {$&+target_prefix} + end + name = name.gsub(/(\$[({]|[})])|(\/+)|[^-.\w]+/) {$1 ? "" : $2 ? ".-." : "_"} + "$(TIMESTAMP_DIR)/.#{name}.time" + end + # :startdoc: + + # creates a stub Makefile. + # + def dummy_makefile(srcdir) + configuration(srcdir) << <require 'test/foo'. + # + # The +srcprefix+ should be used when your source files are not in the same + # directory as your build script. This will not only eliminate the need for + # you to manually copy the source files into the same directory as your + # build script, but it also sets the proper +target_prefix+ in the generated + # Makefile. + # + # Setting the +target_prefix+ will, in turn, install the generated binary in + # a directory under your RbConfig::CONFIG['sitearchdir'] that + # mimics your local filesystem when you run make install. + # + # For example, given the following file tree: + # + # ext/ + # extconf.rb + # test/ + # foo.c + # + # And given the following code: + # + # create_makefile('test/foo', 'test') + # + # That will set the +target_prefix+ in the generated Makefile to "test". + # That, in turn, will create the following file tree when installed via the + # make install command: + # + # /path/to/ruby/sitearchdir/test/foo.so + # + # It is recommended that you use this approach to generate your makefiles, + # instead of copying files around manually, because some third party + # libraries may depend on the +target_prefix+ being set properly. + # + # The +srcprefix+ argument can be used to override the default source + # directory, i.e. the current directory. It is included as part of the + # +VPATH+ and added to the list of +INCFLAGS+. + # + def create_makefile(target, srcprefix = nil) + $target = target + libpath = $DEFLIBPATH|$LIBPATH + message "creating Makefile\n" + MakeMakefile.rm_f "#{CONFTEST}*" + if CONFIG["DLEXT"] == $OBJEXT + for lib in libs = $libs.split + lib.sub!(/-l(.*)/, %%"lib\\1.#{$LIBEXT}"%) + end + $defs.push(format("-DEXTLIB='%s'", libs.join(","))) + end + + if target.include?('/') + target_prefix, target = File.split(target) + target_prefix[0,0] = '/' + else + target_prefix = "" + end + + srcprefix ||= "$(srcdir)/#{srcprefix}".chomp('/') + RbConfig.expand(srcdir = srcprefix.dup) + + ext = ".#{$OBJEXT}" + orig_srcs = Dir[File.join(srcdir, "*.{#{SRC_EXT.join(%q{,})}}")] + if not $objs + srcs = $srcs || orig_srcs + objs = srcs.inject(Hash.new {[]}) {|h, f| h[File.basename(f, ".*") << ext] <<= f; h} + $objs = objs.keys + unless objs.delete_if {|b, f| f.size == 1}.empty? + dups = objs.sort.map {|b, f| + "#{b[/.*\./]}{#{f.collect {|n| n[/([^.]+)\z/]}.join(',')}}" + } + abort "source files duplication - #{dups.join(", ")}" + end + else + $objs.collect! {|o| File.basename(o, ".*") << ext} unless $OBJEXT == "o" + srcs = $srcs || $objs.collect {|o| o.chomp(ext) << ".c"} + end + $srcs = srcs + + hdrs = Dir[File.join(srcdir, "*.{#{HDR_EXT.join(%q{,})}}")] + + target = nil if $objs.empty? + + if target and EXPORT_PREFIX + if File.exist?(File.join(srcdir, target + '.def')) + deffile = "$(srcdir)/$(TARGET).def" + unless EXPORT_PREFIX.empty? + makedef = %{-pe "$_.sub!(/^(?=\\w)/,'#{EXPORT_PREFIX}') unless 1../^EXPORTS$/i"} + end + else + makedef = %{-e "puts 'EXPORTS', '$(TARGET_ENTRY)'"} + end + if makedef + $cleanfiles << '$(DEFFILE)' + origdef = deffile + deffile = "$(TARGET)-$(arch).def" + end + end + origdef ||= '' + + if $extout and $INSTALLFILES + $cleanfiles.concat($INSTALLFILES.collect {|files, dir|File.join(dir, files.sub(/\A\.\//, ''))}) + $distcleandirs.concat($INSTALLFILES.collect {|files, dir| dir}) + end + + if $extmk and $static + $defs << "-DRUBY_EXPORT=1" + end + + if $extmk and not $extconf_h + create_header + end + + libpath = libpathflag(libpath) + + dllib = target ? "$(TARGET).#{CONFIG['DLEXT']}" : "" + staticlib = target ? "$(TARGET).#$LIBEXT" : "" + mfile = open("Makefile", "wb") + conf = configuration(srcprefix) + conf = yield(conf) if block_given? + mfile.puts(conf) + mfile.print " +libpath = #{($DEFLIBPATH|$LIBPATH).join(" ")} +LIBPATH = #{libpath} +DEFFILE = #{deffile} + +CLEANFILES = #{$cleanfiles.join(' ')} +DISTCLEANFILES = #{$distcleanfiles.join(' ')} +DISTCLEANDIRS = #{$distcleandirs.join(' ')} + +extout = #{$extout && $extout.quote} +extout_prefix = #{$extout_prefix} +target_prefix = #{target_prefix} +LOCAL_LIBS = #{$LOCAL_LIBS} +LIBS = #{$LIBRUBYARG} #{$libs} #{$LIBS} +ORIG_SRCS = #{orig_srcs.collect(&File.method(:basename)).join(' ')} +SRCS = $(ORIG_SRCS) #{(srcs - orig_srcs).collect(&File.method(:basename)).join(' ')} +OBJS = #{$objs.join(" ")} +HDRS = #{hdrs.map{|h| '$(srcdir)/' + File.basename(h)}.join(' ')} +TARGET = #{target} +TARGET_NAME = #{target && target[/\A\w+/]} +TARGET_ENTRY = #{EXPORT_PREFIX || ''}Init_$(TARGET_NAME) +DLLIB = #{dllib} +EXTSTATIC = #{$static || ""} +STATIC_LIB = #{staticlib unless $static.nil?} +#{!$extout && defined?($installed_list) ? "INSTALLED_LIST = #{$installed_list}\n" : ""} +TIMESTAMP_DIR = #{$extout ? '$(extout)/.timestamp' : '.'} +" #" + # TODO: fixme + install_dirs.each {|d| mfile.print("%-14s= %s\n" % d) if /^[[:upper:]]/ =~ d[0]} + n = ($extout ? '$(RUBYARCHDIR)/' : '') + '$(TARGET)' + mfile.print " +TARGET_SO = #{($extout ? '$(RUBYARCHDIR)/' : '')}$(DLLIB) +CLEANLIBS = #{n}.#{CONFIG['DLEXT']} #{config_string('cleanlibs') {|t| t.gsub(/\$\*/) {n}}} +CLEANOBJS = *.#{$OBJEXT} #{config_string('cleanobjs') {|t| t.gsub(/\$\*/, "$(TARGET)#{deffile ? '-$(arch)': ''}")} if target} *.bak + +all: #{$extout ? "install" : target ? "$(DLLIB)" : "Makefile"} +static: $(STATIC_LIB)#{$extout ? " install-rb" : ""} +.PHONY: all install static install-so install-rb +.PHONY: clean clean-so clean-static clean-rb +" + mfile.print CLEANINGS + fsep = config_string('BUILD_FILE_SEPARATOR') {|s| s unless s == "/"} + if fsep + sep = ":/=#{fsep}" + fseprepl = proc {|s| + s = s.gsub("/", fsep) + s = s.gsub(/(\$\(\w+)(\))/) {$1+sep+$2} + s = s.gsub(/(\$\{\w+)(\})/) {$1+sep+$2} + } + rsep = ":#{fsep}=/" + else + fseprepl = proc {|s| s} + sep = "" + rsep = "" + end + dirs = [] + mfile.print "install: install-so install-rb\n\n" + sodir = (dir = "$(RUBYARCHDIR)").dup + mfile.print("install-so: ") + if target + f = "$(DLLIB)" + dest = "#{dir}/#{f}" + if $extout + mfile.puts dest + mfile.print "clean-so::\n" + mfile.print "\t-$(Q)$(RM) #{fseprepl[dest]}\n" + mfile.print "\t-$(Q)$(RMDIRS) #{fseprepl[dir]}#{$ignore_error}\n" + else + mfile.print "#{f} #{timestamp_file(dir, target_prefix)}\n" + mfile.print "\t$(INSTALL_PROG) #{fseprepl[f]} #{dir}\n" + if defined?($installed_list) + mfile.print "\t@echo #{dir}/#{File.basename(f)}>>$(INSTALLED_LIST)\n" + end + end + mfile.print "clean-static::\n" + mfile.print "\t-$(Q)$(RM) $(STATIC_LIB)\n" + else + mfile.puts "Makefile" + end + mfile.print("install-rb: pre-install-rb install-rb-default\n") + mfile.print("install-rb-default: pre-install-rb-default\n") + mfile.print("pre-install-rb: Makefile\n") + mfile.print("pre-install-rb-default: Makefile\n") + for sfx, i in [["-default", [["lib/**/*.rb", "$(RUBYLIBDIR)", "lib"]]], ["", $INSTALLFILES]] + files = install_files(mfile, i, nil, srcprefix) or next + for dir, *files in files + unless dirs.include?(dir) + dirs << dir + mfile.print "pre-install-rb#{sfx}: #{timestamp_file(dir, target_prefix)}\n" + end + for f in files + dest = "#{dir}/#{File.basename(f)}" + mfile.print("install-rb#{sfx}: #{dest}\n") + mfile.print("#{dest}: #{f} #{timestamp_file(dir, target_prefix)}\n") + mfile.print("\t$(Q) $(#{$extout ? 'COPY' : 'INSTALL_DATA'}) #{f} $(@D#{sep})\n") + if defined?($installed_list) and !$extout + mfile.print("\t@echo #{dest}>>$(INSTALLED_LIST)\n") + end + if $extout + mfile.print("clean-rb#{sfx}::\n") + mfile.print("\t-$(Q)$(RM) #{fseprepl[dest]}\n") + end + end + end + mfile.print "pre-install-rb#{sfx}:\n" + mfile.print("\t$(ECHO) installing#{sfx.sub(/^-/, " ")} #{target} libraries\n") + if $extout + dirs.uniq! + unless dirs.empty? + mfile.print("clean-rb#{sfx}::\n") + for dir in dirs.sort_by {|d| -d.count('/')} + mfile.print("\t-$(Q)$(RMDIRS) #{fseprepl[dir]}#{$ignore_error}\n") + end + end + end + end + dirs.unshift(sodir) if target and !dirs.include?(sodir) + dirs.each do |d| + t = timestamp_file(d, target_prefix) + mfile.print "#{t}:\n\t$(Q) $(MAKEDIRS) $(@D) #{d}\n\t$(Q) $(TOUCH) $@\n" + end + + mfile.print <<-SITEINSTALL + +site-install: site-install-so site-install-rb +site-install-so: install-so +site-install-rb: install-rb + + SITEINSTALL + + return unless target + + mfile.puts SRC_EXT.collect {|e| ".path.#{e} = $(VPATH)"} if $nmake == ?b + mfile.print ".SUFFIXES: .#{SRC_EXT.join(' .')} .#{$OBJEXT}\n" + mfile.print "\n" + + compile_command = "\n\t$(ECHO) compiling $(<#{rsep})\n\t$(Q) %s\n\n" + CXX_EXT.each do |e| + each_compile_rules do |rule| + mfile.printf(rule, e, $OBJEXT) + mfile.printf(compile_command, COMPILE_CXX) + end + end + C_EXT.each do |e| + each_compile_rules do |rule| + mfile.printf(rule, e, $OBJEXT) + mfile.printf(compile_command, COMPILE_C) + end + end + + mfile.print "$(RUBYARCHDIR)/" if $extout + mfile.print "$(DLLIB): " + mfile.print "$(DEFFILE) " if makedef + mfile.print "$(OBJS) Makefile" + mfile.print " #{timestamp_file('$(RUBYARCHDIR)', target_prefix)}" if $extout + mfile.print "\n" + mfile.print "\t$(ECHO) linking shared-object #{target_prefix.sub(/\A\/(.*)/, '\1/')}$(DLLIB)\n" + mfile.print "\t-$(Q)$(RM) $(@#{sep})\n" + link_so = LINK_SO.gsub(/^/, "\t$(Q) ") + if srcs.any?(&%r"\.(?:#{CXX_EXT.join('|')})\z".method(:===)) + link_so = link_so.sub(/\bLDSHARED\b/, '\&XX') + end + mfile.print link_so, "\n\n" + unless $static.nil? + mfile.print "$(STATIC_LIB): $(OBJS)\n\t-$(Q)$(RM) $(@#{sep})\n\t" + mfile.print "$(ECHO) linking static-library $(@#{rsep})\n\t$(Q) " + mfile.print "$(AR) #{config_string('ARFLAGS') || 'cru '}$@ $(OBJS)" + config_string('RANLIB') do |ranlib| + mfile.print "\n\t-$(Q)#{ranlib} $(@) 2> /dev/null || true" + end + end + mfile.print "\n\n" + if makedef + mfile.print "$(DEFFILE): #{origdef}\n" + mfile.print "\t$(ECHO) generating $(@#{rsep})\n" + mfile.print "\t$(Q) $(RUBY) #{makedef} #{origdef} > $@\n\n" + end + + depend = File.join(srcdir, "depend") + if File.exist?(depend) + mfile.print("###\n", *depend_rules(File.read(depend))) + else + mfile.print "$(OBJS): $(HDRS) $(ruby_headers)\n" + end + + $makefile_created = true + ensure + mfile.close if mfile + end + + # :stopdoc: + + def init_mkmf(config = CONFIG, rbconfig = RbConfig::CONFIG) + $makefile_created = false + $arg_config = [] + $enable_shared = config['ENABLE_SHARED'] == 'yes' + $defs = [] + $extconf_h = nil + if $warnflags = CONFIG['warnflags'] and CONFIG['GCC'] == 'yes' + # turn warnings into errors only for bundled extensions. + config['warnflags'] = $warnflags.gsub(/(\A|\s)-Werror[-=]/, '\1-W') + RbConfig.expand(rbconfig['warnflags'] = config['warnflags'].dup) + config.each do |key, val| + RbConfig.expand(rbconfig[key] = val.dup) if /warnflags/ =~ val + end + $warnflags = config['warnflags'] unless $extmk + end + $CFLAGS = with_config("cflags", arg_config("CFLAGS", config["CFLAGS"])).dup + $CXXFLAGS = (with_config("cxxflags", arg_config("CXXFLAGS", config["CXXFLAGS"]))||'').dup + $ARCH_FLAG = with_config("arch_flag", arg_config("ARCH_FLAG", config["ARCH_FLAG"])).dup + $CPPFLAGS = with_config("cppflags", arg_config("CPPFLAGS", config["CPPFLAGS"])).dup + $LDFLAGS = with_config("ldflags", arg_config("LDFLAGS", config["LDFLAGS"])).dup + $INCFLAGS = "-I$(arch_hdrdir)" + $INCFLAGS << " -I$(hdrdir)/ruby/backward" unless $extmk + $INCFLAGS << " -I$(hdrdir) -I$(srcdir)" + $DLDFLAGS = with_config("dldflags", arg_config("DLDFLAGS", config["DLDFLAGS"])).dup + $LIBEXT = config['LIBEXT'].dup + $OBJEXT = config["OBJEXT"].dup + $EXEEXT = config["EXEEXT"].dup + $LIBS = "#{config['LIBS']} #{config['DLDLIBS']}" + $LIBRUBYARG = "" + $LIBRUBYARG_STATIC = config['LIBRUBYARG_STATIC'] + $LIBRUBYARG_SHARED = config['LIBRUBYARG_SHARED'] + $DEFLIBPATH = [$extmk ? "$(topdir)" : "$(#{config["libdirname"] || "libdir"})"] + $DEFLIBPATH.unshift(".") + $LIBPATH = [] + $INSTALLFILES = [] + $NONINSTALLFILES = [/~\z/, /\A#.*#\z/, /\A\.#/, /\.bak\z/i, /\.orig\z/, /\.rej\z/, /\.l[ao]\z/, /\.o\z/] + $VPATH = %w[$(srcdir) $(arch_hdrdir)/ruby $(hdrdir)/ruby] + + $objs = nil + $srcs = nil + $libs = "" + if $enable_shared or RbConfig.expand(config["LIBRUBY"].dup) != RbConfig.expand(config["LIBRUBY_A"].dup) + $LIBRUBYARG = config['LIBRUBYARG'] + end + + $LOCAL_LIBS = "" + + $cleanfiles = config_string('CLEANFILES') {|s| Shellwords.shellwords(s)} || [] + $cleanfiles << "mkmf.log" + $distcleanfiles = config_string('DISTCLEANFILES') {|s| Shellwords.shellwords(s)} || [] + $distcleandirs = config_string('DISTCLEANDIRS') {|s| Shellwords.shellwords(s)} || [] + + $extout ||= nil + $extout_prefix ||= nil + + $arg_config.clear + dir_config("opt") + end + + FailedMessage = < 1000000) {\n" + + refs.map {|n|" printf(\"%p\", &#{n});\n"}.join("") + + " }\n" + end + end + src + end + + extend self + init_mkmf + + $make = with_config("make-prog", ENV["MAKE"] || "make") + make, = Shellwords.shellwords($make) + $nmake = nil + case + when $mswin + $nmake = ?m if /nmake/i =~ make + when $bccwin + $nmake = ?b if /Borland/i =~ `#{make} -h` + end + $ignore_error = $nmake ? '' : ' 2> /dev/null || true' + + RbConfig::CONFIG["srcdir"] = CONFIG["srcdir"] = + $srcdir = arg_config("--srcdir", File.dirname($0)) + $configure_args["--topsrcdir"] ||= $srcdir + if $curdir = arg_config("--curdir") + RbConfig.expand(curdir = $curdir.dup) + else + curdir = $curdir = "." + end + unless File.expand_path(RbConfig::CONFIG["topdir"]) == File.expand_path(curdir) + CONFIG["topdir"] = $curdir + RbConfig::CONFIG["topdir"] = curdir + end + $configure_args["--topdir"] ||= $curdir + $ruby = arg_config("--ruby", File.join(RbConfig::CONFIG["bindir"], CONFIG["ruby_install_name"])) + + RbConfig.expand(CONFIG["RUBY_SO_NAME"]) + + # :startdoc: + + split = Shellwords.method(:shellwords).to_proc + + EXPORT_PREFIX = config_string('EXPORT_PREFIX') {|s| s.strip} + + hdr = ['#include "ruby.h"' "\n"] + config_string('COMMON_MACROS') do |s| + Shellwords.shellwords(s).each do |w| + w, v = w.split(/=/, 2) + hdr << "#ifndef #{w}" + hdr << "#define #{[w, v].compact.join(" ")}" + hdr << "#endif /* #{w} */" + end + end + config_string('COMMON_HEADERS') do |s| + Shellwords.shellwords(s).each {|w| hdr << "#include <#{w}>"} + end + + ## + # Common headers for Ruby C extensions + + COMMON_HEADERS = hdr.join("\n") + + ## + # Common libraries for Ruby C extensions + + COMMON_LIBS = config_string('COMMON_LIBS', &split) || [] + + ## + # make compile rules + + COMPILE_RULES = config_string('COMPILE_RULES', &split) || %w[.%s.%s:] + RULE_SUBST = config_string('RULE_SUBST') + + ## + # Command which will compile C files in the generated Makefile + + COMPILE_C = config_string('COMPILE_C') || '$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<' + + ## + # Command which will compile C++ files in the generated Makefile + + COMPILE_CXX = config_string('COMPILE_CXX') || '$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<' + + ## + # Command which will compile a program in order to test linking a library + + TRY_LINK = config_string('TRY_LINK') || + "$(CC) #{OUTFLAG}#{CONFTEST}#{$EXEEXT} $(INCFLAGS) $(CPPFLAGS) " \ + "$(CFLAGS) $(src) $(LIBPATH) $(LDFLAGS) $(ARCH_FLAG) $(LOCAL_LIBS) $(LIBS)" + + ## + # Command which will link a shared library + + LINK_SO = (config_string('LINK_SO') || "").sub(/^$/) do + if CONFIG["DLEXT"] == $OBJEXT + "ld $(DLDFLAGS) -r -o $@ $(OBJS)\n" + else + "$(LDSHARED) #{OUTFLAG}$@ $(OBJS) " \ + "$(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)" + end + end + + ## + # Argument which will add a library path to the linker + + LIBPATHFLAG = config_string('LIBPATHFLAG') || ' -L%s' + RPATHFLAG = config_string('RPATHFLAG') || '' + + ## + # Argument which will add a library to the linker + + LIBARG = config_string('LIBARG') || '-l%s' + + ## + # A C main function which does no work + + MAIN_DOES_NOTHING = config_string('MAIN_DOES_NOTHING') || "int main(int argc, char **argv)\n{\n return 0;\n}" + UNIVERSAL_INTS = config_string('UNIVERSAL_INTS') {|s| Shellwords.shellwords(s)} || + %w[int short long long\ long] + + sep = config_string('BUILD_FILE_SEPARATOR') {|s| ":/=#{s}" if s != "/"} || "" + + ## + # Makefile rules that will clean the extension build directory + + CLEANINGS = " +clean-static:: +clean-rb-default:: +clean-rb:: +clean-so:: +clean: clean-so clean-static clean-rb-default clean-rb +\t\t-$(Q)$(RM) $(CLEANLIBS#{sep}) $(CLEANOBJS#{sep}) $(CLEANFILES#{sep}) .*.time + +distclean-rb-default:: +distclean-rb:: +distclean-so:: +distclean-static:: +distclean: clean distclean-so distclean-static distclean-rb-default distclean-rb +\t\t-$(Q)$(RM) Makefile $(RUBY_EXTCONF_H) #{CONFTEST}.* mkmf.log +\t\t-$(Q)$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES#{sep}) +\t\t-$(Q)$(RMDIRS) $(DISTCLEANDIRS#{sep})#{$ignore_error} + +realclean: distclean +" +end + +include MakeMakefile + +if not $extmk and /\A(extconf|makefile).rb\z/ =~ File.basename($0) + END {mkmf_failed($0)} +end diff --git a/ruby/lib/ruby/2.1.0/monitor.rb b/ruby/lib/ruby/2.1.0/monitor.rb new file mode 100644 index 0000000..07394b5 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/monitor.rb @@ -0,0 +1,300 @@ +# = monitor.rb +# +# Copyright (C) 2001 Shugo Maeda +# +# This library is distributed under the terms of the Ruby license. +# You can freely distribute/modify this library. +# + +require 'thread' + +# +# In concurrent programming, a monitor is an object or module intended to be +# used safely by more than one thread. The defining characteristic of a +# monitor is that its methods are executed with mutual exclusion. That is, at +# each point in time, at most one thread may be executing any of its methods. +# This mutual exclusion greatly simplifies reasoning about the implementation +# of monitors compared to reasoning about parallel code that updates a data +# structure. +# +# You can read more about the general principles on the Wikipedia page for +# Monitors[http://en.wikipedia.org/wiki/Monitor_%28synchronization%29] +# +# == Examples +# +# === Simple object.extend +# +# require 'monitor.rb' +# +# buf = [] +# buf.extend(MonitorMixin) +# empty_cond = buf.new_cond +# +# # consumer +# Thread.start do +# loop do +# buf.synchronize do +# empty_cond.wait_while { buf.empty? } +# print buf.shift +# end +# end +# end +# +# # producer +# while line = ARGF.gets +# buf.synchronize do +# buf.push(line) +# empty_cond.signal +# end +# end +# +# The consumer thread waits for the producer thread to push a line to buf +# while buf.empty?. The producer thread (main thread) reads a +# line from ARGF and pushes it into buf then calls empty_cond.signal +# to notify the consumer thread of new data. +# +# === Simple Class include +# +# require 'monitor' +# +# class SynchronizedArray < Array +# +# include MonitorMixin +# +# def initialize(*args) +# super(*args) +# end +# +# alias :old_shift :shift +# alias :old_unshift :unshift +# +# def shift(n=1) +# self.synchronize do +# self.old_shift(n) +# end +# end +# +# def unshift(item) +# self.synchronize do +# self.old_unshift(item) +# end +# end +# +# # other methods ... +# end +# +# +SynchronizedArray+ implements an Array with synchronized access to items. +# This Class is implemented as subclass of Array which includes the +# MonitorMixin module. +# +module MonitorMixin + # + # FIXME: This isn't documented in Nutshell. + # + # Since MonitorMixin.new_cond returns a ConditionVariable, and the example + # above calls while_wait and signal, this class should be documented. + # + class ConditionVariable + class Timeout < Exception; end + + # + # Releases the lock held in the associated monitor and waits; reacquires the lock on wakeup. + # + # If +timeout+ is given, this method returns after +timeout+ seconds passed, + # even if no other thread doesn't signal. + # + def wait(timeout = nil) + @monitor.__send__(:mon_check_owner) + count = @monitor.__send__(:mon_exit_for_cond) + begin + @cond.wait(@monitor.instance_variable_get(:@mon_mutex), timeout) + return true + ensure + @monitor.__send__(:mon_enter_for_cond, count) + end + end + + # + # Calls wait repeatedly while the given block yields a truthy value. + # + def wait_while + while yield + wait + end + end + + # + # Calls wait repeatedly until the given block yields a truthy value. + # + def wait_until + until yield + wait + end + end + + # + # Wakes up the first thread in line waiting for this lock. + # + def signal + @monitor.__send__(:mon_check_owner) + @cond.signal + end + + # + # Wakes up all threads waiting for this lock. + # + def broadcast + @monitor.__send__(:mon_check_owner) + @cond.broadcast + end + + private + + def initialize(monitor) + @monitor = monitor + @cond = ::ConditionVariable.new + end + end + + def self.extend_object(obj) + super(obj) + obj.__send__(:mon_initialize) + end + + # + # Attempts to enter exclusive section. Returns +false+ if lock fails. + # + def mon_try_enter + if @mon_owner != Thread.current + unless @mon_mutex.try_lock + return false + end + @mon_owner = Thread.current + end + @mon_count += 1 + return true + end + # For backward compatibility + alias try_mon_enter mon_try_enter + + # + # Enters exclusive section. + # + def mon_enter + if @mon_owner != Thread.current + @mon_mutex.lock + @mon_owner = Thread.current + end + @mon_count += 1 + end + + # + # Leaves exclusive section. + # + def mon_exit + mon_check_owner + @mon_count -=1 + if @mon_count == 0 + @mon_owner = nil + @mon_mutex.unlock + end + end + + # + # Enters exclusive section and executes the block. Leaves the exclusive + # section automatically when the block exits. See example under + # +MonitorMixin+. + # + def mon_synchronize + mon_enter + begin + yield + ensure + mon_exit + end + end + alias synchronize mon_synchronize + + # + # Creates a new MonitorMixin::ConditionVariable associated with the + # receiver. + # + def new_cond + return ConditionVariable.new(self) + end + + private + + # Use extend MonitorMixin or include MonitorMixin instead + # of this constructor. Have look at the examples above to understand how to + # use this module. + def initialize(*args) + super + mon_initialize + end + + # Initializes the MonitorMixin after being included in a class or when an + # object has been extended with the MonitorMixin + def mon_initialize + @mon_owner = nil + @mon_count = 0 + @mon_mutex = Mutex.new + end + + def mon_check_owner + if @mon_owner != Thread.current + raise ThreadError, "current thread not owner" + end + end + + def mon_enter_for_cond(count) + @mon_owner = Thread.current + @mon_count = count + end + + def mon_exit_for_cond + count = @mon_count + @mon_owner = nil + @mon_count = 0 + return count + end +end + +# Use the Monitor class when you want to have a lock object for blocks with +# mutual exclusion. +# +# require 'monitor' +# +# lock = Monitor.new +# lock.synchronize do +# # exclusive access +# end +# +class Monitor + include MonitorMixin + alias try_enter try_mon_enter + alias enter mon_enter + alias exit mon_exit +end + + +# Documentation comments: +# - All documentation comes from Nutshell. +# - MonitorMixin.new_cond appears in the example, but is not documented in +# Nutshell. +# - All the internals (internal modules Accessible and Initializable, class +# ConditionVariable) appear in RDoc. It might be good to hide them, by +# making them private, or marking them :nodoc:, etc. +# - RDoc doesn't recognise aliases, so we have mon_synchronize documented, but +# not synchronize. +# - mon_owner is in Nutshell, but appears as an accessor in a separate module +# here, so is hard/impossible to RDoc. Some other useful accessors +# (mon_count and some queue stuff) are also in this module, and don't appear +# directly in the RDoc output. +# - in short, it may be worth changing the code layout in this file to make the +# documentation easier + +# Local variables: +# mode: Ruby +# tab-width: 8 +# End: diff --git a/ruby/lib/ruby/2.1.0/multi-tk.rb b/ruby/lib/ruby/2.1.0/multi-tk.rb new file mode 100644 index 0000000..68bd849 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/multi-tk.rb @@ -0,0 +1,3754 @@ +# +# multi-tk.rb - supports multi Tk interpreters +# by Hidetoshi NAGAI + +require 'tcltklib' +require 'tkutil' +require 'thread' + +if defined? Tk + fail RuntimeError,"'multi-tk' library must be required before requiring 'tk'" +end + +################################################ +# ignore exception on the mainloop? + +TclTkLib.mainloop_abort_on_exception = true +# TclTkLib.mainloop_abort_on_exception = false +# TclTkLib.mainloop_abort_on_exception = nil + + +################################################ +# add ThreadGroup check to TclTkIp.new +class << TclTkIp + alias __new__ new + private :__new__ + + def new(*args) + if Thread.current.group != ThreadGroup::Default + raise SecurityError, 'only ThreadGroup::Default can call TclTkIp.new' + end + obj = __new__(*args) + obj.instance_eval{ + @force_default_encoding ||= TkUtil.untrust([false]) + @encoding ||= TkUtil.untrust([nil]) + def @encoding.to_s; self.join(nil); end + } + obj + end +end + + +################################################ +# exceptiopn to treat the return value from IP +class MultiTkIp_OK < Exception + def self.send(thread, ret=nil) + thread.raise self.new(ret) + end + + def initialize(ret=nil) + super('succeed') + @return_value = ret + end + + attr_reader :return_value + alias value return_value +end +MultiTkIp_OK.freeze + + +################################################ +# methods for construction +class MultiTkIp + class Command_Queue < Queue + def initialize(interp) + @interp = interp + super() + end + + def push(value) + if !@interp || @interp.deleted? + fail RuntimeError, "Tk interpreter is already deleted" + end + super(value) + end + alias << push + alias enq push + + def close + @interp = nil + end + end + Command_Queue.freeze + + BASE_DIR = File.dirname(__FILE__) + + WITH_RUBY_VM = Object.const_defined?(:RubyVM) && ::RubyVM.class == Class + WITH_ENCODING = defined?(::Encoding.default_external) + #WITH_ENCODING = Object.const_defined?(:Encoding) && ::Encoding.class == Class + + (@@SLAVE_IP_ID = ['slave'.freeze, TkUtil.untrust('0')]).instance_eval{ + @mutex = Mutex.new + def mutex; @mutex; end + freeze + } + + @@IP_TABLE = TkUtil.untrust({}) unless defined?(@@IP_TABLE) + + @@INIT_IP_ENV = TkUtil.untrust([]) unless defined?(@@INIT_IP_ENV) # table of Procs + @@ADD_TK_PROCS = TkUtil.untrust([]) unless defined?(@@ADD_TK_PROCS) # table of [name, args, body] + + @@TK_TABLE_LIST = TkUtil.untrust([]) unless defined?(@@TK_TABLE_LIST) + + unless defined?(@@TK_CMD_TBL) + @@TK_CMD_TBL = TkUtil.untrust(Object.new) + + # @@TK_CMD_TBL.instance_variable_set('@tbl', {}.taint) + tbl_obj = TkUtil.untrust(Hash.new{|hash,key| + fail IndexError, "unknown command ID '#{key}'" + }) + @@TK_CMD_TBL.instance_variable_set('@tbl', tbl_obj) + + class << @@TK_CMD_TBL + allow = [ + '__send__', '__id__', 'freeze', 'inspect', 'kind_of?', 'object_id', + '[]', '[]=', 'delete', 'each', 'has_key?' + ] + instance_methods.each{|m| undef_method(m) unless allow.index(m.to_s)} + + def kind_of?(klass) + @tbl.kind_of?(klass) + end + + def inspect + if Thread.current.group == ThreadGroup::Default + @tbl.inspect + else + ip = MultiTkIp.__getip + @tbl.reject{|idx, ent| ent.respond_to?(:ip) && ent.ip != ip}.inspect + end + end + + def [](idx) + return unless (ent = @tbl[idx]) + if Thread.current.group == ThreadGroup::Default + ent + elsif ent.respond_to?(:ip) + (ent.ip == MultiTkIp.__getip)? ent: nil + else + ent + end + end + + def []=(idx,val) + if self.has_key?(idx) && Thread.current.group != ThreadGroup::Default + fail SecurityError,"cannot change the entried command" + end + @tbl[idx] = val + end + + def delete(idx, &blk) + # if gets an entry, is permited to delete + if self[idx] + @tbl.delete(idx) + elsif blk + blk.call(idx) + else + nil + end + end + + def each(&blk) + if Thread.current.group == ThreadGroup::Default + @tbl.each(&blk) + else + ip = MultiTkIp.__getip + @tbl.each{|idx, ent| + blk.call(idx, ent) unless ent.respond_to?(:ip) && ent.ip != ip + } + end + self + end + + def has_key?(k) + @tbl.has_key?(k) + end + alias include? has_key? + alias key? has_key? + alias member? has_key? + end + + @@TK_CMD_TBL.freeze + end + + ###################################### + + @@CB_ENTRY_CLASS = Class.new(TkCallbackEntry){ + def initialize(ip, cmd) + @ip = ip + @safe = safe = $SAFE + # @cmd = cmd + cmd = MultiTkIp._proc_on_safelevel(&cmd) + @cmd = proc{|*args| cmd.call(safe, *args)} + self.freeze + end + attr_reader :ip, :cmd + def inspect + cmd.inspect + end + def call(*args) + unless @ip.deleted? + current = Thread.current + backup_ip = current[:callback_ip] + current[:callback_ip] = @ip + begin + ret = @ip.cb_eval(@cmd, *args) + fail ret if ret.kind_of?(Exception) + ret + rescue TkCallbackBreak, TkCallbackContinue => e + fail e + rescue SecurityError => e + # in 'exit', 'exit!', and 'abort' : security error --> delete IP + if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/ + @ip.delete + elsif @ip.safe? + if @ip.respond_to?(:cb_error) + @ip.cb_error(e) + else + nil # ignore + end + else + fail e + end + rescue Exception => e + fail e if e.message =~ /^TkCallback/ + + if @ip.safe? + if @ip.respond_to?(:cb_error) + @ip.cb_error(e) + else + nil # ignore + end + else + fail e + end + ensure + current[:callback_ip] = backup_ip + end + end + end + }.freeze + + ###################################### + + def _keys2opts(src_keys) + return nil if src_keys == nil + keys = {}; src_keys.each{|k, v| keys[k.to_s] = v} + #keys.collect{|k,v| "-#{k} #{v}"}.join(' ') + keys.collect{|k,v| "-#{k} #{TclTkLib._conv_listelement(TkComm::_get_eval_string(v))}"}.join(' ') + end + private :_keys2opts + + def _check_and_return(thread, exception, wait=0) + unless thread + unless exception.kind_of?(MultiTkIp_OK) + msg = "#{exception.class}: #{exception.message}" + + if @interp.deleted? + warn("Warning (#{self}): " + msg) + return nil + end + + if safe? + warn("Warning (#{self}): " + msg) if $DEBUG + return nil + end + + begin + @interp._eval_without_enc(@interp._merge_tklist('bgerror', msg)) + rescue Exception => e + warn("Warning (#{self}): " + msg) + end + end + return nil + end + + if wait == 0 + # no wait + Thread.pass + if thread.stop? + thread.raise exception + end + return thread + end + + # wait to stop the caller thread + wait.times{ + if thread.stop? + # ready to send exception + thread.raise exception + return thread + end + + # wait + Thread.pass + } + + # unexpected error + thread.raise RuntimeError, "the thread may not wait for the return value" + return thread + end + + ###################################### + + def set_cb_error(cmd = Proc.new) + @cb_error_proc[0] = cmd + end + + def cb_error(e) + if @cb_error_proc[0].respond_to?(:call) + @cb_error_proc[0].call(e) + end + end + + ###################################### + + def set_safe_level(safe) + if safe > @safe_level[0] + @safe_level[0] = safe + @cmd_queue.enq([@system, 'set_safe_level', safe]) + end + @safe_level[0] + end + def safe_level=(safe) + set_safe_level(safe) + end + def self.set_safe_level(safe) + __getip.set_safe_level(safe) + end + def self.safe_level=(safe) + self.set_safe_level(safe) + end + def safe_level + @safe_level[0] + end + def self.safe_level + __getip.safe_level + end + + def wait_on_mainloop? + @wait_on_mainloop[0] + end + def wait_on_mainloop=(bool) + @wait_on_mainloop[0] = bool + end + + def running_mainloop? + @wait_on_mainloop[1] > 0 + end + + def _destroy_slaves_of_slaveIP(ip) + unless ip.deleted? + # ip._split_tklist(ip._invoke('interp', 'slaves')).each{|name| + ip._split_tklist(ip._invoke_without_enc('interp', 'slaves')).each{|name| + name = _fromUTF8(name) + begin + # ip._eval_without_enc("#{name} eval {foreach i [after info] {after cancel $i}}") + after_ids = ip._eval_without_enc("#{name} eval {after info}") + ip._eval_without_enc("#{name} eval {foreach i {#{after_ids}} {after cancel $i}}") + rescue Exception + end + begin + # ip._invoke('interp', 'eval', name, 'destroy', '.') + ip._invoke(name, 'eval', 'destroy', '.') + rescue Exception + end + + # safe_base? + if ip._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0' + begin + ip._eval_without_enc("::safe::interpDelete #{name}") + rescue Exception + end + end +=begin + if ip._invoke('interp', 'exists', name) == '1' + begin + ip._invoke(name, 'eval', 'exit') + rescue Exception + end + end +=end + unless ip.deleted? + if ip._invoke('interp', 'exists', name) == '1' + begin + ip._invoke('interp', 'delete', name) + rescue Exception + end + end + end + } + end + end + + def _receiver_eval_proc_core(safe_level, thread, cmd, *args) + begin + #ret = proc{$SAFE = safe_level; cmd.call(*args)}.call + #ret = cmd.call(safe_level, *args) + normal_ret = false + ret = catch(:IRB_EXIT) do # IRB hack + retval = cmd.call(safe_level, *args) + normal_ret = true + retval + end + unless normal_ret + # catch IRB_EXIT + exit(ret) + end + ret + rescue SystemExit => e + # delete IP + unless @interp.deleted? + @slave_ip_tbl.each{|name, subip| + _destroy_slaves_of_slaveIP(subip) + begin + # subip._eval_without_enc("foreach i [after info] {after cancel $i}") + after_ids = subip._eval_without_enc("after info") + subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") + rescue Exception + end +=begin + begin + subip._invoke('destroy', '.') unless subip.deleted? + rescue Exception + end +=end + # safe_base? + if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0' + begin + @interp._eval_without_enc("::safe::interpDelete #{name}") + rescue Exception + else + next if subip.deleted? + end + end + if subip.respond_to?(:safe_base?) && subip.safe_base? && + !subip.deleted? + # do 'exit' to call the delete_hook procedure + begin + subip._eval_without_enc('exit') + rescue Exception + end + else + begin + subip.delete unless subip.deleted? + rescue Exception + end + end + } + + begin + # @interp._eval_without_enc("foreach i [after info] {after cancel $i}") + after_ids = @interp._eval_without_enc("after info") + @interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") + rescue Exception + end + begin + @interp._invoke('destroy', '.') unless @interp.deleted? + rescue Exception + end + if @safe_base && !@interp.deleted? + # do 'exit' to call the delete_hook procedure + @interp._eval_without_enc('exit') + else + @interp.delete unless @interp.deleted? + end + end + + if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/ + _check_and_return(thread, MultiTkIp_OK.new($3 == 'exit')) + else + _check_and_return(thread, MultiTkIp_OK.new(nil)) + end + + # if master? && !safe? && allow_ruby_exit? + if !@interp.deleted? && master? && !safe? && allow_ruby_exit? +=begin + ObjectSpace.each_object(TclTkIp){|obj| + obj.delete unless obj.deleted? + } +=end + #exit(e.status) + fail e + end + # break + + rescue SecurityError => e + # in 'exit', 'exit!', and 'abort' : security error --> delete IP + if e.backtrace[0] =~ /^(.+?):(\d+):in `(exit|exit!|abort)'/ + ret = ($3 == 'exit') + unless @interp.deleted? + @slave_ip_tbl.each{|name, subip| + _destroy_slaves_of_slaveIP(subip) + begin + # subip._eval_without_enc("foreach i [after info] {after cancel $i}") + after_ids = subip._eval_without_enc("after info") + subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") + rescue Exception + end +=begin + begin + subip._invoke('destroy', '.') unless subip.deleted? + rescue Exception + end +=end + # safe_base? + if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0' + begin + @interp._eval_without_enc("::safe::interpDelete #{name}") + rescue Exception + else + next if subip.deleted? + end + end + if subip.respond_to?(:safe_base?) && subip.safe_base? && + !subip.deleted? + # do 'exit' to call the delete_hook procedure + begin + subip._eval_without_enc('exit') + rescue Exception + end + else + begin + subip.delete unless subip.deleted? + rescue Exception + end + end + } + + begin + # @interp._eval_without_enc("foreach i [after info] {after cancel $i}") + after_ids = @interp._eval_without_enc("after info") + @interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") + rescue Exception + end +=begin + begin + @interp._invoke('destroy', '.') unless @interp.deleted? + rescue Exception + end +=end + if @safe_base && !@interp.deleted? + # do 'exit' to call the delete_hook procedure + @interp._eval_without_enc('exit') + else + @interp.delete unless @interp.deleted? + end + end + _check_and_return(thread, MultiTkIp_OK.new(ret)) + # break + + else + # raise security error + _check_and_return(thread, e) + end + + rescue Exception => e + # raise exception + begin + bt = _toUTF8(e.backtrace.join("\n")) + if MultiTkIp::WITH_ENCODING + bt.force_encoding('utf-8') + else + bt.instance_variable_set(:@encoding, 'utf-8') + end + rescue Exception + bt = e.backtrace.join("\n") + end + begin + @interp._set_global_var('errorInfo', bt) + rescue Exception + end + _check_and_return(thread, e) + + else + # no exception + _check_and_return(thread, MultiTkIp_OK.new(ret)) + end + end + + def _receiver_eval_proc(last_thread, safe_level, thread, cmd, *args) + if thread + Thread.new{ + last_thread.join if last_thread + unless @interp.deleted? + _receiver_eval_proc_core(safe_level, thread, cmd, *args) + end + } + else + Thread.new{ + unless @interp.deleted? + _receiver_eval_proc_core(safe_level, thread, cmd, *args) + end + } + last_thread + end + end + + private :_receiver_eval_proc, :_receiver_eval_proc_core + + def _receiver_mainloop(check_root) + if @evloop_thread[0] && @evloop_thread[0].alive? + @evloop_thread[0] + else + @evloop_thread[0] = Thread.new{ + while !@interp.deleted? + #if check_root + # inf = @interp._invoke_without_enc('info', 'command', '.') + # break if !inf.kind_of?(String) || inf != '.' + #end + break if check_root && !@interp.has_mainwindow? + sleep 0.5 + end + } + @evloop_thread[0] + end + end + + def _create_receiver_and_watchdog(lvl = $SAFE) + lvl = $SAFE if lvl < $SAFE + + # command-procedures receiver + receiver = Thread.new(lvl){|safe_level| + last_thread = {} + + loop do + break if @interp.deleted? + thread, cmd, *args = @cmd_queue.deq + if thread == @system + # control command + case cmd + when 'set_safe_level' + begin + safe_level = args[0] if safe_level < args[0] + rescue Exception + end + when 'call_mainloop' + thread = args.shift + _check_and_return(thread, + MultiTkIp_OK.new(_receiver_mainloop(*args))) + else + # ignore + end + + else + # procedure + last_thread[thread] = _receiver_eval_proc(last_thread[thread], + safe_level, thread, + cmd, *args) + end + end + } + + # watchdog of receiver + watchdog = Thread.new{ + begin + loop do + sleep 1 + if @interp.deleted? + receiver.kill + @cmd_queue.close + end + break unless receiver.alive? + end + rescue Exception + # ignore all kind of Exception + end + + # receiver is dead + retry_count = 3 + loop do + Thread.pass + begin + thread, cmd, *args = @cmd_queue.deq(true) # non-block + rescue ThreadError + # queue is empty + retry_count -= 1 + break if retry_count <= 0 + sleep 0.5 + retry + end + next unless thread + if thread.alive? + if @interp.deleted? + thread.raise RuntimeError, 'the interpreter is already deleted' + else + thread.raise RuntimeError, + 'the interpreter no longer receives command procedures' + end + end + end + } + + # return threads + [receiver, watchdog] + end + private :_check_and_return, :_create_receiver_and_watchdog + + ###################################### + + unless self.const_defined? :RUN_EVENTLOOP_ON_MAIN_THREAD + ### Ruby 1.9 !!!!!!!!!!!!!!!!!!!!!!!!!! + RUN_EVENTLOOP_ON_MAIN_THREAD = false + end + + if self.const_defined? :DEFAULT_MASTER_NAME + name = DEFAULT_MASTER_NAME.to_s + else + name = nil + end + if self.const_defined?(:DEFAULT_MASTER_OPTS) && + DEFAULT_MASTER_OPTS.kind_of?(Hash) + keys = DEFAULT_MASTER_OPTS + else + keys = {} + end + + @@DEFAULT_MASTER = self.allocate + @@DEFAULT_MASTER.instance_eval{ + @tk_windows = TkUtil.untrust({}) + + @tk_table_list = TkUtil.untrust([]) + + @slave_ip_tbl = TkUtil.untrust({}) + + @slave_ip_top = TkUtil.untrust({}) + + @evloop_thread = TkUtil.untrust([]) + + unless keys.kind_of? Hash + fail ArgumentError, "expecting a Hash object for the 2nd argument" + end + + if !WITH_RUBY_VM || RUN_EVENTLOOP_ON_MAIN_THREAD ### check Ruby 1.9 !!!!!!! + @interp = TclTkIp.new(name, _keys2opts(keys)) + else ### Ruby 1.9 !!!!!!!!!!! + @interp_thread = Thread.new{ + current = Thread.current + begin + current[:interp] = interp = TclTkIp.new(name, _keys2opts(keys)) + rescue e + current[:interp] = e + raise e + end + #sleep + current[:mutex] = mutex = Mutex.new + current[:root_check] = cond_var = ConditionVariable.new + + status = [nil] + def status.value + self[0] + end + def status.value=(val) + self[0] = val + end + current[:status] = status + + begin + begin + #TclTkLib.mainloop_abort_on_exception = false + #Thread.current[:status].value = TclTkLib.mainloop(true) + interp.mainloop_abort_on_exception = true + current[:status].value = interp.mainloop(true) + rescue SystemExit=>e + current[:status].value = e + rescue Exception=>e + current[:status].value = e + retry if interp.has_mainwindow? + ensure + mutex.synchronize{ cond_var.broadcast } + end + + #Thread.current[:status].value = TclTkLib.mainloop(false) + current[:status].value = interp.mainloop(false) + + ensure + # interp must be deleted before the thread for interp is dead. + # If not, raise Tcl_Panic on Tcl_AsyncDelete because async handler + # deleted by the wrong thread. + interp.delete + end + } + until @interp_thread[:interp] + Thread.pass + end + # INTERP_THREAD.run + raise @interp_thread[:interp] if @interp_thread[:interp].kind_of? Exception + @interp = @interp_thread[:interp] + + # delete the interpreter and kill the eventloop thread at exit + interp = @interp + interp_thread = @interp_thread + END{ + if interp_thread.alive? + interp.delete + interp_thread.kill + end + } + + def self.mainloop(check_root = true) + begin + TclTkLib.set_eventloop_window_mode(true) + @interp_thread.value + ensure + TclTkLib.set_eventloop_window_mode(false) + end + end + end + + @interp.instance_eval{ + @force_default_encoding ||= TkUtil.untrust([false]) + @encoding ||= TkUtil.untrust([nil]) + def @encoding.to_s; self.join(nil); end + } + + @ip_name = nil + + @callback_status = TkUtil.untrust([]) + + @system = Object.new + + @wait_on_mainloop = TkUtil.untrust([true, 0]) + + @threadgroup = Thread.current.group + + @safe_base = false + + @safe_level = [$SAFE] + + @cmd_queue = MultiTkIp::Command_Queue.new(@interp) + + @cmd_receiver, @receiver_watchdog = _create_receiver_and_watchdog(@safe_level[0]) + + @threadgroup.add @cmd_receiver + @threadgroup.add @receiver_watchdog + + # NOT enclose @threadgroup for @@DEFAULT_MASTER + + @@IP_TABLE[ThreadGroup::Default] = self + @@IP_TABLE[@threadgroup] = self + + ################################# + + @pseudo_toplevel = [false, nil] + + def self.__pseudo_toplevel + Thread.current.group == ThreadGroup::Default && + MultiTkIp.__getip == @@DEFAULT_MASTER && + self.__pseudo_toplevel_evaluable? && @pseudo_toplevel[1] + end + + def self.__pseudo_toplevel=(m) + unless (Thread.current.group == ThreadGroup::Default && + MultiTkIp.__getip == @@DEFAULT_MASTER) + fail SecurityError, "no permission to manipulate" + end + + # if m.kind_of?(Module) && m.respond_to?(:pseudo_toplevel_evaluable?) + if m.respond_to?(:pseudo_toplevel_evaluable?) + @pseudo_toplevel[0] = true + @pseudo_toplevel[1] = m + else + fail ArgumentError, 'fail to set pseudo-toplevel' + end + self + end + + def self.__pseudo_toplevel_evaluable? + begin + @pseudo_toplevel[0] && @pseudo_toplevel[1].pseudo_toplevel_evaluable? + rescue Exception + false + end + end + + def self.__pseudo_toplevel_evaluable=(mode) + unless (Thread.current.group == ThreadGroup::Default && + MultiTkIp.__getip == @@DEFAULT_MASTER) + fail SecurityError, "no permission to manipulate" + end + + @pseudo_toplevel[0] = (mode)? true: false + end + + ################################# + + @assign_request = Class.new(Exception){ + def self.new(target, ret) + obj = super() + obj.target = target + obj.ret = ret + obj + end + attr_accessor :target, :ret + } + + @assign_thread = Thread.new{ + loop do + begin + Thread.stop + rescue @assign_request=>req + begin + req.ret[0] = req.target.instance_eval{ + @cmd_receiver, @receiver_watchdog = + _create_receiver_and_watchdog(@safe_level[0]) + @threadgroup.add @cmd_receiver + @threadgroup.add @receiver_watchdog + @threadgroup.enclose + true + } + rescue Exception=>e + begin + req.ret[0] = e + rescue Exception + # ignore + end + end + rescue Exception + # ignore + end + end + } + + def self.assign_receiver_and_watchdog(target) + ret = [nil] + @assign_thread.raise(@assign_request.new(target, ret)) + while ret[0] == nil + unless @assign_thread.alive? + raise RuntimeError, 'lost the thread to assign a receiver and a watchdog thread' + end + end + if ret[0].kind_of?(Exception) + raise ret[0] + else + ret[0] + end + end + + ################################# + + @init_ip_env_queue = Queue.new + Thread.new{ + current = Thread.current + loop { + mtx, cond, ret, table, script = @init_ip_env_queue.deq + begin + ret[0] = table.each{|tg, ip| ip._init_ip_env(script) } + rescue Exception => e + ret[0] = e + ensure + mtx.synchronize{ cond.signal } + end + mtx = cond = ret = table = script = nil # clear variables for GC + } + } + + def self.__init_ip_env__(table, script) + ret = [] + mtx = (Thread.current[:MultiTk_ip_Mutex] ||= Mutex.new) + cond = (Thread.current[:MultiTk_ip_CondVar] ||= ConditionVariable.new) + mtx.synchronize{ + @init_ip_env_queue.enq([mtx, cond, ret, table, script]) + cond.wait(mtx) + } + if ret[0].kind_of?(Exception) + raise ret[0] + else + ret[0] + end + end + + ################################# + + class << self + undef :instance_eval + end + } + + @@DEFAULT_MASTER.freeze # defend against modification + + ###################################### + + def self.inherited(subclass) + # trust if on ThreadGroup::Default or @@DEFAULT_MASTER's ThreadGroup + if @@IP_TABLE[Thread.current.group] == @@DEFAULT_MASTER + begin + class << subclass + self.methods.each{|m| + name = m.to_s + begin + unless name == '__id__' || name == '__send__' || name == 'freeze' + undef_method(m) + end + rescue Exception + # ignore all exceptions + end + } + end + ensure + subclass.freeze + fail SecurityError, + "cannot create subclass of MultiTkIp on a untrusted ThreadGroup" + end + end + end + + ###################################### + + @@SAFE_OPT_LIST = [ + 'accessPath'.freeze, + 'statics'.freeze, + 'nested'.freeze, + 'deleteHook'.freeze + ].freeze + + def _parse_slaveopts(keys) + name = nil + safe = false + safe_opts = {} + tk_opts = {} + + keys.each{|k,v| + k_str = k.to_s + if k_str == 'name' + name = v + elsif k_str == 'safe' + safe = v + elsif @@SAFE_OPT_LIST.member?(k_str) + safe_opts[k_str] = v + else + tk_opts[k_str] = v + end + } + + if keys['without_tk'] || keys[:without_tk] + [name, safe, safe_opts, nil] + else + [name, safe, safe_opts, tk_opts] + end + end + private :_parse_slaveopts + + def _create_slave_ip_name + @@SLAVE_IP_ID.mutex.synchronize{ + name = @@SLAVE_IP_ID.join('') + @@SLAVE_IP_ID[1].succ! + name.freeze + } + end + private :_create_slave_ip_name + + ###################################### + + def __check_safetk_optkeys(optkeys) + # based on 'safetk.tcl' + new_keys = {} + optkeys.each{|k,v| new_keys[k.to_s] = v} + + # check 'display' + if !new_keys.key?('display') + begin + #new_keys['display'] = @interp._invoke('winfo screen .') + new_keys['display'] = @interp._invoke('winfo', 'screen', '.') + rescue + if ENV[DISPLAY] + new_keys['display'] = ENV[DISPLAY] + elsif !new_keys.key?('use') + warn "Warning: no screen info or ENV[DISPLAY], so use ':0.0'" + new_keys['display'] = ':0.0' + end + end + end + + # check 'use' + if new_keys.key?('use') + # given 'use' + case new_keys['use'] + when TkWindow + new_keys['use'] = TkWinfo.id(new_keys['use']) + #assoc_display = @interp._eval('winfo screen .') + assoc_display = @interp._invoke('winfo', 'screen', '.') + when /^\..*/ + new_keys['use'] = @interp._invoke('winfo', 'id', new_keys['use']) + assoc_display = @interp._invoke('winfo', 'screen', new_keys['use']) + else + begin + pathname = @interp._invoke('winfo', 'pathname', new_keys['use']) + assoc_display = @interp._invoke('winfo', 'screen', pathname) + rescue + assoc_display = new_keys['display'] + end + end + + # match display? + if assoc_display != new_keys['display'] + if optkeys.key?(:display) || optkeys.key?('display') + fail RuntimeError, + "conflicting 'display'=>#{new_keys['display']} " + + "and display '#{assoc_display}' on 'use'=>#{new_keys['use']}" + else + new_keys['display'] = assoc_display + end + end + end + + # return + new_keys + end + private :__check_safetk_optkeys + + def __create_safetk_frame(slave_ip, slave_name, app_name, keys) + # display option is used by ::safe::loadTk + loadTk_keys = {} + loadTk_keys['display'] = keys['display'] + dup_keys = keys.dup + + # keys for toplevel : allow followings + toplevel_keys = {} + ['height', 'width', 'background', 'menu'].each{|k| + toplevel_keys[k] = dup_keys.delete(k) if dup_keys.key?(k) + } + toplevel_keys['classname'] = 'SafeTk' + toplevel_keys['screen'] = dup_keys.delete('display') + + # other keys used by pack option of container frame + + # create toplevel widget + begin + top = TkToplevel.new(toplevel_keys) + rescue NameError => e + fail e unless @interp.safe? + fail SecurityError, "unable create toplevel on the safe interpreter" + end + msg = "Untrusted Ruby/Tk applet (#{slave_name})" + if app_name.kind_of?(String) + top.title "#{app_name} (#{slave_name})" + else + top.title msg + end + + # procedure to delete slave interpreter + slave_delete_proc = proc{ + unless slave_ip.deleted? + #if slave_ip._invoke('info', 'command', '.') != "" + # slave_ip._invoke('destroy', '.') + #end + #slave_ip.delete + slave_ip._eval_without_enc('exit') + end + begin + top.destroy if top.winfo_exist? + rescue + # ignore + end + } + tag = TkBindTag.new.bind('Destroy', slave_delete_proc) + + top.bindtags = top.bindtags.unshift(tag) + + # create control frame + TkFrame.new(top, :bg=>'red', :borderwidth=>3, :relief=>'ridge') {|fc| + fc.bindtags = fc.bindtags.unshift(tag) + + TkFrame.new(fc, :bd=>0){|f| + TkButton.new(f, + :text=>'Delete', :bd=>1, :padx=>2, :pady=>0, + :highlightthickness=>0, :command=>slave_delete_proc + ).pack(:side=>:right, :fill=>:both) + f.pack(:side=>:right, :fill=>:both, :expand=>true) + } + + TkLabel.new(fc, :text=>msg, :padx=>2, :pady=>0, + :anchor=>:w).pack(:side=>:left, :fill=>:both, :expand=>true) + + fc.pack(:side=>:bottom, :fill=>:x) + } + + # container frame for slave interpreter + dup_keys['fill'] = :both unless dup_keys.key?('fill') + dup_keys['expand'] = true unless dup_keys.key?('expand') + c = TkFrame.new(top, :container=>true).pack(dup_keys) + c.bind('Destroy', proc{top.destroy}) + + # return keys + loadTk_keys['use'] = TkWinfo.id(c) + [loadTk_keys, top.path] + end + private :__create_safetk_frame + + def __create_safe_slave_obj(safe_opts, app_name, tk_opts) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + + # safe interpreter + ip_name = _create_slave_ip_name + slave_ip = @interp.create_slave(ip_name, true) + slave_ip.instance_eval{ + @force_default_encoding ||= TkUtil.untrust([false]) + @encoding ||= TkUtil.untrust([nil]) + def @encoding.to_s; self.join(nil); end + } + @slave_ip_tbl[ip_name] = slave_ip + def slave_ip.safe_base? + true + end + + @interp._eval("::safe::interpInit #{ip_name}") + + slave_ip._invoke('set', 'argv0', app_name) if app_name.kind_of?(String) + + if tk_opts + tk_opts = __check_safetk_optkeys(tk_opts) + if tk_opts.key?('use') + @slave_ip_top[ip_name] = '' + else + tk_opts, top_path = __create_safetk_frame(slave_ip, ip_name, app_name, + tk_opts) + @slave_ip_top[ip_name] = top_path + end + @interp._eval("::safe::loadTk #{ip_name} #{_keys2opts(tk_opts)}") + @interp._invoke('__replace_slave_tk_commands__', ip_name) + else + @slave_ip_top[ip_name] = nil + end + + if safe_opts.key?('deleteHook') || safe_opts.key?(:deleteHook) + @interp._eval("::safe::interpConfigure #{ip_name} " + + _keys2opts(safe_opts)) + else + @interp._eval("::safe::interpConfigure #{ip_name} " + + _keys2opts(safe_opts) + '-deleteHook {' + + TkComm._get_eval_string(proc{|slave| + self._default_delete_hook(slave) + }) + '}') + end + + [slave_ip, ip_name] + end + + def __create_trusted_slave_obj(name, keys) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + + ip_name = _create_slave_ip_name + slave_ip = @interp.create_slave(ip_name, false) + slave_ip.instance_eval{ + @force_default_encoding ||= TkUtil.untrust([false]) + @encoding ||= TkUtil.untrust([nil]) + def @encoding.to_s; self.join(nil); end + } + slave_ip._invoke('set', 'argv0', name) if name.kind_of?(String) + slave_ip._invoke('set', 'argv', _keys2opts(keys)) + @interp._invoke('load', '', 'Tk', ip_name) + @interp._invoke('__replace_slave_tk_commands__', ip_name) + @slave_ip_tbl[ip_name] = slave_ip + [slave_ip, ip_name] + end + + ###################################### + + def _create_slave_object(keys={}) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + + ip = MultiTkIp.new_slave(self, keys={}) + @slave_ip_tbl[ip.name] = ip + end + + ###################################### + + def initialize(master, safeip=true, keys={}) + if $SAFE >= 4 + fail SecurityError, "cannot create a new interpreter at level #{$SAFE}" + end + + if safeip == nil && $SAFE >= 2 + fail SecurityError, "cannot create a master-ip at level #{$SAFE}" + end + + if master.deleted? && safeip == nil + fail RuntimeError, "cannot create a slave of a deleted interpreter" + end + + if !master.deleted? && !master.master? && master.safe? + fail SecurityError, "safe-slave-ip cannot create a new interpreter" + end + + if safeip == nil && !master.master? + fail SecurityError, "slave-ip cannot create a master-ip" + end + + unless keys.kind_of? Hash + fail ArgumentError, "expecting a Hash object for the 2nd argument" + end + + @tk_windows = {} + @tk_table_list = [] + @slave_ip_tbl = {} + @slave_ip_top = {} + @cb_error_proc = [] + @evloop_thread = [] + + TkUtil.untrust(@tk_windows) unless @tk_windows.tainted? + TkUtil.untrust(@tk_table_list) unless @tk_table_list.tainted? + TkUtil.untrust(@slave_ip_tbl) unless @slave_ip_tbl.tainted? + TkUtil.untrust(@slave_ip_top) unless @slave_ip_top.tainted? + TkUtil.untrust(@cb_error_proc) unless @cb_error_proc.tainted? + TkUtil.untrust(@evloop_thread) unless @evloop_thread.tainted? + + @callback_status = [] + + name, safe, safe_opts, tk_opts = _parse_slaveopts(keys) + + safe = 4 if safe && !safe.kind_of?(Fixnum) + + @safe_base = false + + if safeip == nil + # create master-ip + unless WITH_RUBY_VM + @interp = TclTkIp.new(name, _keys2opts(tk_opts)) + @interp.instance_eval{ + @force_default_encoding ||= TkUtil.untrust([false]) + @encoding ||= TkUtil.untrust([nil]) + def @encoding.to_s; self.join(nil); end + } + + else ### Ruby 1.9 !!!!!!!!!!! +=begin + @interp_thread = Thread.new{ + Thread.current[:interp] = interp = TclTkIp.new(name, _keys2opts(tk_opts)) + interp.instance_eval{ + @force_default_encoding ||= TkUtil.untrust([false]) + @encoding ||= TkUtil.untrust([nil]) + def @encoding.to_s; self.join(nil); end + } + + #sleep + TclTkLib.mainloop(true) + } + until @interp_thread[:interp] + Thread.pass + end + # INTERP_THREAD.run + @interp = @interp_thread[:interp] +=end + @interp_thread = Thread.new{ + current = Thread.current + begin + current[:interp] = interp = TclTkIp.new(name, _keys2opts(tk_opts)) + rescue e + current[:interp] = e + raise e + end + #sleep + #TclTkLib.mainloop(true) + current[:mutex] = mutex = Mutex.new + current[:root_check] = cond_ver = ConditionVariable.new + + status = [nil] + def status.value + self[0] + end + def status.value=(val) + self[0] = val + end + current[:status] = status + + begin + begin + current[:status].value = interp.mainloop(true) + rescue SystemExit=>e + current[:status].value = e + rescue Exception=>e + current[:status].value = e + retry if interp.has_mainwindow? + ensure + mutex.synchronize{ cond_var.broadcast } + end + current[:status].value = interp.mainloop(false) + ensure + interp.delete + end + } + until @interp_thread[:interp] + Thread.pass + end + # INTERP_THREAD.run + @interp = @interp_thread[:interp] + + @evloop_thread[0] = @interp_thread + + def self.mainloop(check_root = true) + begin + TclTkLib.set_eventloop_window_mode(true) + @interp_thread.value + ensure + TclTkLib.set_eventloop_window_mode(false) + end + end + end + + @interp.instance_eval{ + @force_default_encoding ||= TkUtil.untrust([false]) + @encoding ||= TkUtil.untrust([nil]) + def @encoding.to_s; self.join(nil); end + } + + @ip_name = nil + + if safe + safe = $SAFE if safe < $SAFE + @safe_level = [safe] + else + @safe_level = [$SAFE] + end + + else + # create slave-ip + if safeip || master.safe? + @safe_base = true + @interp, @ip_name = master.__create_safe_slave_obj(safe_opts, + name, tk_opts) + # @interp_thread = nil if RUBY_VERSION < '1.9.0' ### !!!!!!!!!!! + @interp_thread = nil unless WITH_RUBY_VM ### Ruby 1.9 !!!!!!!!!!! + if safe + safe = master.safe_level if safe < master.safe_level + @safe_level = [safe] + else + @safe_level = [4] + end + else + @interp, @ip_name = master.__create_trusted_slave_obj(name, tk_opts) + # @interp_thread = nil if RUBY_VERSION < '1.9.0' ### !!!!!!!!!!! + @interp_thread = nil unless WITH_RUBY_VM ### Ruby 1.9 !!!!!!!!!!! + if safe + safe = master.safe_level if safe < master.safe_level + @safe_level = [safe] + else + @safe_level = [master.safe_level] + end + end + @set_alias_proc = proc{|name| + master._invoke('interp', 'alias', @ip_name, name, '', name) + }.freeze + end + + @system = Object.new + + @wait_on_mainloop = TkUtil.untrust([true, 0]) + # @wait_on_mainloop = TkUtil.untrust([false, 0]) + + @threadgroup = ThreadGroup.new + + @pseudo_toplevel = [false, nil] + + @cmd_queue = MultiTkIp::Command_Queue.new(@interp) + +=begin + @cmd_receiver, @receiver_watchdog = _create_receiver_and_watchdog(@safe_level[0]) + + @threadgroup.add @cmd_receiver + @threadgroup.add @receiver_watchdog + + @threadgroup.enclose +=end + @@DEFAULT_MASTER.assign_receiver_and_watchdog(self) + + @@IP_TABLE[@threadgroup] = self + @@TK_TABLE_LIST.size.times{ + @tk_table_list << TkUtil.untrust({}) + } + _init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS) + + class << self + undef :instance_eval + end + + # dummy call for initialization + self.eval_proc{ Tk.tk_call('set', 'tcl_patchLevel') } + + self.freeze # defend against modification + end + + ###################################### + + def _default_delete_hook(slave) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @slave_ip_tbl.delete(slave) + top = @slave_ip_top.delete(slave) + if top.kind_of?(String) + # call default hook of safetk.tcl (ignore exceptions) + if top == '' + begin + @interp._eval("::safe::disallowTk #{slave}") + rescue + warn("Waring: fail to call '::safe::disallowTk'") if $DEBUG + end + else # toplevel path + begin + @interp._eval("::safe::tkDelete {} #{top} #{slave}") + rescue + warn("Waring: fail to call '::safe::tkDelete'") if $DEBUG + begin + @interp._eval("destroy #{top}") + rescue + warn("Waring: fail to destroy toplevel") if $DEBUG + end + end + end + end + end + +end + + +# get target IP +class MultiTkIp + @@CALLBACK_SUBTHREAD = Class.new(Thread){ + def self.new(interp, &blk) + super(interp){|ip| Thread.current[:callback_ip] = ip; blk.call} + end + + @table = TkUtil.untrust(Hash.new{|h,k| h[k] = TkUtil.untrust([])}) + def self.table + @table + end + } + + def self._ip_id_ + __getip._ip_id_ + end + def _ip_id_ + # for RemoteTkIp + '' + end + + def self.__getip + current = Thread.current + if current.kind_of?(@@CALLBACK_SUBTHREAD) + return current[:callback_ip] + end + if TclTkLib.mainloop_thread? != false && current[:callback_ip] + return current[:callback_ip] + end + if current.group == ThreadGroup::Default + @@DEFAULT_MASTER + else + ip = @@IP_TABLE[current.group] + unless ip + fail SecurityError, + "cannot call Tk methods on #{Thread.current.inspect}" + end + ip + end + end +end + + +# aliases of constructor +class << MultiTkIp + alias __new new + private :__new + + def new_master(safe=nil, keys={}, &blk) + if MultiTkIp::WITH_RUBY_VM + #### TODO !!!!!! + fail RuntimeError, + 'sorry, still not support multiple master-interpreters on RubyVM' + end + + if safe.kind_of?(Hash) + keys = safe + elsif safe.kind_of?(Integer) + raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash) + if !keys.key?(:safe) && !keys.key?('safe') + keys[:safe] = safe + end + elsif safe == nil + # do nothing + else + raise ArgumentError, "unexpected argument(s)" + end + + ip = __new(__getip, nil, keys) + #ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? + if block_given? + #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)} + #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; yield}.call)} + ip._proc_on_safelevel(&blk).call(ip.safe_level) + end + ip + end + + alias new new_master + + def new_slave(safe=nil, keys={}, &blk) + if safe.kind_of?(Hash) + keys = safe + elsif safe.kind_of?(Integer) + raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash) + if !keys.key?(:safe) && !keys.key?('safe') + keys[:safe] = safe + end + elsif safe == nil + # do nothing + else + raise ArgumentError, "unexpected argument(s)" + end + + ip = __new(__getip, false, keys) + # ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? + if block_given? + #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)} + #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; yield}.call)} + ip._proc_on_safelevel(&blk).call(ip.safe_level) + end + ip + end + alias new_trusted_slave new_slave + + def new_safe_slave(safe=4, keys={}, &blk) + if safe.kind_of?(Hash) + keys = safe + elsif safe.kind_of?(Integer) + raise ArgumentError, "unexpected argument(s)" unless keys.kind_of?(Hash) + if !keys.key?(:safe) && !keys.key?('safe') + keys[:safe] = safe + end + else + raise ArgumentError, "unexpected argument(s)" + end + + ip = __new(__getip, true, keys) + # ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call) if block_given? + if block_given? + #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; Proc.new}.call)} + #Thread.new{ip.eval_proc(proc{$SAFE=ip.safe_level; yield}.call)} + ip._proc_on_safelevel(&blk).call(ip.safe_level) + end + ip + end + alias new_safeTk new_safe_slave +end + + +# get info +class MultiTkIp + def inspect + s = self.to_s.chop! + if self.manipulable? + if master? + if @interp.deleted? + s << ':deleted-master' + else + s << ':master' + end + else + if @interp.deleted? + s << ':deleted-slave' + elsif @interp.safe? + s << ':safe-slave' + else + s << ':trusted-slave' + end + end + end + s << '>' + end + + def master? + if @ip_name + false + else + true + end + end + def self.master? + __getip.master? + end + + def slave? + not master? + end + def self.slave? + not self.master? + end + + def alive? + raise SecurityError, "no permission to manipulate" unless self.manipulable? + begin + return false unless @cmd_receiver.alive? + return false if @interp.deleted? + return false if @interp._invoke('interp', 'exists', '') == '0' + rescue Exception + return false + end + true + end + def self.alive? + __getip.alive? + end + + def path + @ip_name || '' + end + def self.path + __getip.path + end + def ip_name + @ip_name || '' + end + def self.ip_name + __getip.ip_name + end + def to_eval + @ip_name || '' + end + def self.to_eval + __getip.to_eval + end + + def slaves(all = false) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp','slaves').split.map!{|name| + if @slave_ip_tbl.key?(name) + @slave_ip_tbl[name] + elsif all + name + else + nil + end + }.compact! + end + def self.slaves(all = false) + __getip.slaves(all) + end + + def manipulable? + return true if (Thread.current.group == ThreadGroup::Default) + ip = MultiTkIp.__getip + (ip == self) || ip._is_master_of?(@interp) + end + def self.manipulable? + true + end + + def _is_master_of?(tcltkip_obj) + tcltkip_obj.slave_of?(@interp) + end + protected :_is_master_of? +end + + +# instance methods to treat tables +class MultiTkIp + def _tk_cmd_tbl + tbl = {} + MultiTkIp.tk_cmd_tbl.each{|id, ent| tbl[id] = ent if ent.ip == self } + tbl + end + + def _tk_windows + @tk_windows + end + + def _tk_table_list + @tk_table_list + end + + def _add_new_tables + (@@TK_TABLE_LIST.size - @tk_table_list.size).times{ + @tk_table_list << TkUtil.untrust({}) + } + end + + def _init_ip_env(script) + self.eval_proc{script.call(self)} + end + + def _add_tk_procs(name, args, body) + return if slave? + @interp._invoke('proc', name, args, body) if args && body + @interp._invoke('interp', 'slaves').split.each{|slave| + @interp._invoke('interp', 'alias', slave, name, '', name) + } + end + + def _remove_tk_procs(*names) + return if slave? + names.each{|name| + name = name.to_s + + return if @interp.deleted? + @interp._invoke('rename', name, '') + + return if @interp.deleted? + @interp._invoke('interp', 'slaves').split.each{|slave| + return if @interp.deleted? + @interp._invoke('interp', 'alias', slave, name, '') rescue nil + } + } + end + + def _init_ip_internal(init_ip_env, add_tk_procs) + #init_ip_env.each{|script| self.eval_proc{script.call(self)}} + init_ip_env.each{|script| self._init_ip_env(script)} + add_tk_procs.each{|name, args, body| + if master? + @interp._invoke('proc', name, args, body) if args && body + else + @set_alias_proc.call(name) + end + } + end +end + + +# class methods to treat tables +class MultiTkIp + def self.tk_cmd_tbl + @@TK_CMD_TBL + end + def self.tk_windows + __getip._tk_windows + end + def self.tk_object_table(id) + __getip._tk_table_list[id] + end + def self.create_table + if __getip.slave? + begin + raise SecurityError, "slave-IP has no permission creating a new table" + rescue SecurityError => e + #p e.backtrace + # Is called on a Ruby/Tk library? + caller_info = e.backtrace[1] + if caller_info =~ %r{^#{MultiTkIp::BASE_DIR}/(tk|tkextlib)/[^:]+\.rb:} + # Probably, caller is a Ruby/Tk library --> allow creating + else + raise e + end + end + end + + id = @@TK_TABLE_LIST.size + obj = Object.new + @@TK_TABLE_LIST << obj + obj.instance_variable_set(:@id, id) + obj.instance_variable_set(:@mutex, Mutex.new) + obj.instance_eval{ + def self.mutex + @mutex + end + def self.method_missing(m, *args) + MultiTkIp.tk_object_table(@id).__send__(m, *args) + end + } + obj.freeze + @@IP_TABLE.each{|tg, ip| ip._add_new_tables } + return obj + end + + def self.init_ip_env(script = Proc.new) + @@INIT_IP_ENV << script + if __getip.slave? + begin + raise SecurityError, "slave-IP has no permission initializing IP env" + rescue SecurityError => e + #p e.backtrace + # Is called on a Ruby/Tk library? + caller_info = e.backtrace[1] + if caller_info =~ %r{^#{MultiTkIp::BASE_DIR}/(tk|tkextlib)/[^:]+\.rb:} + # Probably, caller is a Ruby/Tk library --> allow creating + else + raise e + end + end + end + + # @@IP_TABLE.each{|tg, ip| + # ip._init_ip_env(script) + # } + @@DEFAULT_MASTER.__init_ip_env__(@@IP_TABLE, script) + end + + def self.add_tk_procs(name, args=nil, body=nil) + if name.kind_of?(Array) # => an array of [name, args, body] + name.each{|param| self.add_tk_procs(*param)} + else + name = name.to_s + @@ADD_TK_PROCS << [name, args, body] + @@IP_TABLE.each{|tg, ip| + ip._add_tk_procs(name, args, body) + } + end + end + + def self.remove_tk_procs(*names) + names.each{|name| + name = name.to_s + @@ADD_TK_PROCS.delete_if{|elem| + elem.kind_of?(Array) && elem[0].to_s == name + } + } + @@IP_TABLE.each{|tg, ip| + ip._remove_tk_procs(*names) + } + end + + def self.init_ip_internal + __getip._init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS) + end +end + +# for callback operation +class MultiTkIp + def self.cb_entry_class + @@CB_ENTRY_CLASS + end + def self.get_cb_entry(cmd) + @@CB_ENTRY_CLASS.new(__getip, cmd).freeze + end + +=begin + def cb_eval(cmd, *args) + #self.eval_callback{ TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *args)) } + #ret = self.eval_callback{ TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *args)) } + ret = self.eval_callback(*args){|safe, *params| + $SAFE=safe if $SAFE < safe + TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *params)) + } + if ret.kind_of?(Exception) + raise ret + end + ret + end +=end + def cb_eval(cmd, *args) + self.eval_callback(*args, + &_proc_on_safelevel{|*params| + TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *params)) + }) + end +=begin + def cb_eval(cmd, *args) + self.eval_callback(*args){|safe, *params| + $SAFE=safe if $SAFE < safe + # TkUtil.eval_cmd(cmd, *params) + TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *params)) + } + end +=end +=begin + def cb_eval(cmd, *args) + @callback_status[0] ||= TkVariable.new + @callback_status[1] ||= TkVariable.new + st, val = @callback_status + th = Thread.new{ + self.eval_callback(*args){|safe, *params| + #p [status, val, safe, *params] + $SAFE=safe if $SAFE < safe + begin + TkComm._get_eval_string(TkUtil.eval_cmd(cmd, *params)) + rescue TkCallbackContinue + st.value = 4 + rescue TkCallbackBreak + st.value = 3 + rescue TkCallbackReturn + st.value = 2 + rescue Exception => e + val.value = e.message + st.value = 1 + else + st.value = 0 + end + } + } + begin + st.wait + status = st.numeric + retval = val.value + rescue => e + fail e + end + + if status == 1 + fail RuntimeError, retval + elsif status == 2 + fail TkCallbackReturn, "Tk callback returns 'return' status" + elsif status == 3 + fail TkCallbackBreak, "Tk callback returns 'break' status" + elsif status == 4 + fail TkCallbackContinue, "Tk callback returns 'continue' status" + else + '' + end + end +=end + +end + +# pseudo-toplevel operation support +class MultiTkIp + # instance method + def __pseudo_toplevel + ip = MultiTkIp.__getip + (ip == @@DEFAULT_MASTER || ip == self) && + self.__pseudo_toplevel_evaluable? && @pseudo_toplevel[1] + end + + def __pseudo_toplevel=(m) + unless (Thread.current.group == ThreadGroup::Default && + MultiTkIp.__getip == @@DEFAULT_MASTER) + fail SecurityError, "no permission to manipulate" + end + + # if m.kind_of?(Module) && m.respond_to?(:pseudo_toplevel_evaluable?) + if m.respond_to?(:pseudo_toplevel_evaluable?) + @pseudo_toplevel[0] = true + @pseudo_toplevel[1] = m + else + fail ArgumentError, 'fail to set pseudo-toplevel' + end + self + end + + def __pseudo_toplevel_evaluable? + begin + @pseudo_toplevel[0] && @pseudo_toplevel[1].pseudo_toplevel_evaluable? + rescue Exception + false + end + end + + def __pseudo_toplevel_evaluable=(mode) + unless (Thread.current.group == ThreadGroup::Default && + MultiTkIp.__getip == @@DEFAULT_MASTER) + fail SecurityError, "no permission to manipulate" + end + + @pseudo_toplevel[0] = (mode)? true: false + end +end + + +################################################ +# use pseudo-toplevel feature of MultiTkIp ? +if (!defined?(Use_PseudoToplevel_Feature_of_MultiTkIp) || + Use_PseudoToplevel_Feature_of_MultiTkIp) + module MultiTkIp_PseudoToplevel_Evaluable + #def pseudo_toplevel_eval(body = Proc.new) + # Thread.current[:TOPLEVEL] = self + # begin + # body.call + # ensure + # Thread.current[:TOPLEVEL] = nil + # end + #end + + def pseudo_toplevel_evaluable? + @pseudo_toplevel_evaluable + end + + def pseudo_toplevel_evaluable=(mode) + @pseudo_toplevel_evaluable = (mode)? true: false + end + + def self.extended(mod) + mod.__send__(:extend_object, mod) + mod.instance_variable_set('@pseudo_toplevel_evaluable', true) + end + end + + class Object + alias __method_missing_alias_for_MultiTkIp__ method_missing + private :__method_missing_alias_for_MultiTkIp__ + + def method_missing(id, *args) + begin + has_top = (top = MultiTkIp.__getip.__pseudo_toplevel) && + top.respond_to?(:pseudo_toplevel_evaluable?) && + top.pseudo_toplevel_evaluable? && + top.respond_to?(id) + rescue Exception => e + has_top = false + end + + if has_top + top.__send__(id, *args) + else + __method_missing_alias_for_MultiTkIp__(id, *args) + end + end + end +else + # dummy + module MultiTkIp_PseudoToplevel_Evaluable + def pseudo_toplevel_evaluable? + false + end + end +end + + +################################################ +# evaluate a procedure on the proper interpreter +class MultiTkIp + # instance & class method + def _proc_on_safelevel(cmd=nil) # require a block for eval + if cmd + if cmd.kind_of?(Method) + _proc_on_safelevel{|*args| cmd.call(*args)} + else + _proc_on_safelevel(&cmd) + end + else + #Proc.new{|safe, *args| $SAFE=safe if $SAFE < safe; yield(*args)} + Proc.new{|safe, *args| + # avoid security error on Exception objects + untrust_proc = proc{|err| + begin + err.untrust if err.respond_to?(:untrust) + rescue SecurityError + end + err + } + $SAFE=safe if $SAFE < safe; + begin + yield(*args) + rescue Exception => e + fail untrust_proc.call(e) + end + } + end + end + def MultiTkIp._proc_on_safelevel(cmd=nil, &blk) + MultiTkIp.__getip._proc_on_safelevel(cmd, &blk) + end + + def _proc_on_current_safelevel(cmd=nil, &blk) # require a block for eval + safe = $SAFE + cmd = _proc_on_safelevel(cmd, &blk) + Proc.new{|*args| cmd.call(safe, *args)} + end + def MultiTkIp._proc_on_current_safelevel(cmd=nil, &blk) + MultiTkIp.__getip._proc_on_current_safelevel(cmd, &blk) + end + + ###################################### + # instance method + def eval_proc_core(req_val, cmd, *args) + # check + raise SecurityError, "no permission to manipulate" unless self.manipulable? + unless cmd.kind_of?(Proc) || cmd.kind_of?(Method) + raise RuntimeError, "A Proc/Method object is expected for the 'cmd' argument" + end + + # on IP thread + if @cmd_receiver == Thread.current || + (!req_val && TclTkLib.mainloop_thread? != false) # callback + begin + ret = cmd.call(safe_level, *args) + rescue SystemExit => e + # exit IP + warn("Warning: "+ e.inspect + " on " + self.inspect) if $DEBUG + begin + self._eval_without_enc('exit') + rescue Exception => e + end + self.delete + ret = nil + rescue Exception => e + if $DEBUG + warn("Warning: " + e.class.inspect + + ((e.message.length > 0)? ' "' + e.message + '"': '') + + " on " + self.inspect) + end +=begin + begin + bt = _toUTF8(e.backtrace.join("\n")) + bt.instance_variable_set(:@encoding, 'utf-8') + rescue Exception + bt = e.backtrace.join("\n") + end + begin + @interp._set_global_var('errorInfo', bt) + rescue Exception + end +=end + ret = e + end + return ret + end + + # send cmd to the proc-queue + unless req_val + begin + @cmd_queue.enq([nil, cmd, *args]) + rescue Exception => e + # ignore + if $DEBUG + warn("Warning: " + e.class.inspect + + ((e.message.length > 0)? ' "' + e.message + '"': '') + + " on " + self.inspect) + end + return e + end + return nil + end + + # send and get return value by exception + begin + @cmd_queue.enq([Thread.current, cmd, *args]) + Thread.stop + rescue MultiTkIp_OK => ret + # return value + return ret.value + rescue SystemExit => e + # exit IP + warn("Warning: " + e.inspect + " on " + self.inspect) if $DEBUG + begin + self._eval_without_enc('exit') + rescue Exception + end + if !self.deleted? && !safe? && allow_ruby_exit? + self.delete + fail e + else + self.delete + end + rescue Exception => e + if $DEBUG + warn("Warning: " + e.class.inspect + + ((e.message.length > 0)? ' "' + e.message + '"': '') + + " on " + self.inspect) + end + return e + end + return nil + end + private :eval_proc_core + +if false && WITH_RUBY_VM ### Ruby 1.9 + # Not stable, so disable this feature + def eval_callback(*args) + if block_given? + cmd = Proc.new + else + cmd = args.shift + end + begin + if @@CALLBACK_SUBTHREAD.table[self].index(Thread.current) + last_th = nil + else + last_th = @@CALLBACK_SUBTHREAD.table[self][-1] + end + @@CALLBACK_SUBTHREAD.new(self){ + @@CALLBACK_SUBTHREAD.table[self] << Thread.current + begin + last_th.join if last_th + eval_proc_core(false, cmd, *args) + rescue Exception=>e + e + ensure + @@CALLBACK_SUBTHREAD.table[self].delete(Thread.current) + end + } + end + end +else ### Ruby 1.8 + def eval_callback(*args) + if block_given? + cmd = Proc.new + else + cmd = args.shift + end + begin + eval_proc_core(false, cmd, *args) + rescue Exception=>e + e + ensure + end + end +end + + def eval_proc(*args, &blk) + if block_given? + cmd = _proc_on_safelevel(&blk) + else + unless (cmd = args.shift) + fail ArgumentError, "A Proc or Method object is expected for 1st argument" + end + cmd = _proc_on_safelevel(&cmd) + end + if TclTkLib.mainloop_thread? == true + # call from eventloop + current = Thread.current + backup_ip = current[:callback_ip] + current[:callback_ip] = self + begin + eval_proc_core(false, cmd, *args) + ensure + current[:callback_ip] = backup_ip + end + else + eval_proc_core(true, + proc{|safe, *params| + Thread.new{cmd.call(safe, *params)}.value + }, + *args) + end + end +=begin + def eval_proc(*args) + # The scope of the eval-block of 'eval_proc' method is different from + # the external. If you want to pass local values to the eval-block, + # use arguments of eval_proc method. They are passed to block-arguments. + if block_given? + cmd = Proc.new + else + unless (cmd = args.shift) + fail ArgumentError, "A Proc or Method object is expected for 1st argument" + end + end + if TclTkLib.mainloop_thread? == true + # call from eventloop + current = Thread.current + backup_ip = current[:callback_ip] + current[:callback_ip] = self + begin + eval_proc_core(false, + proc{|safe, *params| + $SAFE=safe if $SAFE < safe + cmd.call(*params) + }, *args) + ensure + current[:callback_ip] = backup_ip + end + else + eval_proc_core(true, + proc{|safe, *params| + $SAFE=safe if $SAFE < safe + Thread.new(*params, &cmd).value + }, + *args) + end + end +=end + alias call eval_proc + + def bg_eval_proc(*args) + if block_given? + cmd = Proc.new + else + unless (cmd = args.shift) + fail ArgumentError, "A Proc or Method object is expected for 1st argument" + end + end + Thread.new{ + eval_proc(cmd, *args) +=begin + eval_proc_core(false, + proc{|safe, *params| + $SAFE=safe if $SAFE < safe + Thread.new(*params, &cmd).value + }, + safe_level, *args) +=end + } + end + alias background_eval_proc bg_eval_proc + alias thread_eval_proc bg_eval_proc + alias bg_call bg_eval_proc + alias background_call bg_eval_proc + + def eval_string(cmd, *eval_args) + # cmd string ==> proc + unless cmd.kind_of?(String) + raise RuntimeError, "A String object is expected for the 'cmd' argument" + end + + eval_proc_core(true, + proc{|safe| + Kernel.eval("$SAFE=#{safe} if $SAFE < #{safe};" << cmd, + *eval_args) + }) + end + alias eval_str eval_string + + def bg_eval_string(cmd, *eval_args) + # cmd string ==> proc + unless cmd.kind_of?(String) + raise RuntimeError, "A String object is expected for the 'cmd' argument" + end + Thread.new{ + eval_proc_core(true, + proc{|safe| + Kernel.eval("$SAFE=#{safe} if $SAFE < #{safe};" << cmd, + *eval_args) + }) + } + end + alias background_eval_string bg_eval_string + alias bg_eval_str bg_eval_string + alias background_eval_str bg_eval_string + + def eval(*args, &blk) + if block_given? + eval_proc(*args, &blk) + elsif args[0] + if args[0].respond_to?(:call) + eval_proc(*args) + else + eval_string(*args) + end + else + fail ArgumentError, "no argument to eval" + end + end + + def bg_eval(*args, &blk) + if block_given? + bg_eval_proc(*args, &blk) + elsif args[0] + if args[0].respond_to?(:call) + bg_eval_proc(*args) + else + bg_eval_string(*args) + end + else + fail ArgumentError, "no argument to eval" + end + end + alias background_eval bg_eval +end + +class << MultiTkIp + # class method + def eval_proc(*args, &blk) + # class ==> interp object + __getip.eval_proc(*args, &blk) + end + alias call eval_proc + + def bg_eval_proc(*args, &blk) + # class ==> interp object + __getip.bg_eval_proc(*args, &blk) + end + alias background_eval_proc bg_eval_proc + alias thread_eval_proc bg_eval_proc + alias bg_call bg_eval_proc + alias background_call bg_eval_proc + + def eval_string(cmd, *eval_args) + # class ==> interp object + __getip.eval_string(cmd, *eval_args) + end + alias eval_str eval_string + + def bg_eval_string(cmd, *eval_args) + # class ==> interp object + __getip.bg_eval_string(cmd, *eval_args) + end + alias background_eval_string bg_eval_string + alias bg_eval_str bg_eval_string + alias background_eval_str bg_eval_string + + def eval(*args, &blk) + # class ==> interp object + __getip.eval(*args, &blk) + end + def bg_eval(*args, &blk) + # class ==> interp object + __getip.bg_eval(*args, &blk) + end + alias background_eval bg_eval +end + + +# event loop +# all master/slave IPs are controled by only one event-loop +class MultiTkIp + def self.default_master? + __getip == @@DEFAULT_MASTER + end +end +class << MultiTkIp + def mainloop(check_root = true) + __getip.mainloop(check_root) + end + def mainloop_watchdog(check_root = true) + __getip.mainloop_watchdog(check_root) + end + def do_one_event(flag = TclTkLib::EventFlag::ALL) + __getip.do_one_event(flag) + end + def mainloop_abort_on_exception + # __getip.mainloop_abort_on_exception + TclTkLib.mainloop_abort_on_exception + end + def mainloop_abort_on_exception=(mode) + # __getip.mainloop_abort_on_exception=(mode) + TclTkLib.mainloop_abort_on_exception=(mode) + end + def set_eventloop_tick(tick) + __getip.set_eventloop_tick(tick) + end + def get_eventloop_tick + __getip.get_eventloop_tick + end + def set_no_event_wait(tick) + __getip.set_no_event_wait(tick) + end + def get_no_event_wait + __getip.get_no_event_wait + end + def set_eventloop_weight(loop_max, no_event_tick) + __getip.set_eventloop_weight(loop_max, no_event_tick) + end + def get_eventloop_weight + __getip.get_eventloop_weight + end +end + +# class methods to delegate to TclTkIp +class << MultiTkIp + def method_missing(id, *args) + __getip.__send__(id, *args) + end + + def make_safe + __getip.make_safe + end + + def safe? + __getip.safe? + end + + def safe_base? + begin + __getip.safe_base? + rescue + false + end + end + + def allow_ruby_exit? + __getip.allow_ruby_exit? + end + + def allow_ruby_exit= (mode) + __getip.allow_ruby_exit = mode + end + + def delete + __getip.delete + end + + def deleted? + __getip.deleted? + end + + def has_mainwindow? + __getip.has_mainwindow? + end + + def invalid_namespace? + __getip.invalid_namespace? + end + + def abort(msg = nil) + __getip.abort(msg) + end + + def exit(st = true) + __getip.exit(st) + end + + def exit!(st = false) + __getip.exit!(st) + end + + def restart(app_name = nil, keys = {}) + init_ip_internal + + __getip._invoke('set', 'argv0', app_name) if app_name + if keys.kind_of?(Hash) + __getip._invoke('set', 'argv', _keys2opts(keys)) + end + + __getip.restart + end + + def _eval(str) + __getip._eval(str) + end + + def _invoke(*args) + __getip._invoke(*args) + end + + def _eval_without_enc(str) + __getip._eval_without_enc(str) + end + + def _invoke_without_enc(*args) + __getip._invoke_without_enc(*args) + end + + def _eval_with_enc(str) + __getip._eval_with_enc(str) + end + + def _invoke_with_enc(*args) + __getip._invoke_with_enc(*args) + end + + def _toUTF8(str, encoding=nil) + __getip._toUTF8(str, encoding) + end + + def _fromUTF8(str, encoding=nil) + __getip._fromUTF8(str, encoding) + end + + def _thread_vwait(var) + __getip._thread_vwait(var) + end + + def _thread_tkwait(mode, target) + __getip._thread_tkwait(mode, target) + end + + def _return_value + __getip._return_value + end + + def _get_variable(var, flag) + __getip._get_variable(var, flag) + end + def _get_variable2(var, idx, flag) + __getip._get_variable2(var, idx, flag) + end + def _set_variable(var, value, flag) + __getip._set_variable(var, value, flag) + end + def _set_variable2(var, idx, value, flag) + __getip._set_variable2(var, idx, value, flag) + end + def _unset_variable(var, flag) + __getip._unset_variable(var, flag) + end + def _unset_variable2(var, idx, flag) + __getip._unset_variable2(var, idx, flag) + end + + def _get_global_var(var) + __getip._get_global_var(var) + end + def _get_global_var2(var, idx) + __getip._get_global_var2(var, idx) + end + def _set_global_var(var, value) + __getip._set_global_var(var, value) + end + def _set_global_var2(var, idx, value) + __getip._set_global_var2(var, idx, value) + end + def _unset_global_var(var) + __getip._unset_global_var(var) + end + def _unset_global_var2(var, idx) + __getip._unset_global_var2(var, idx) + end + + def _make_menu_embeddable(menu_path) + __getip._make_menu_embeddable(menu_path) + end + + def _split_tklist(str) + __getip._split_tklist(str) + end + def _merge_tklist(*args) + __getip._merge_tklist(*args) + end + def _conv_listelement(arg) + __getip._conv_listelement(arg) + end + + def _create_console + __getip._create_console + end +end + + +# wrap methods on TclTkLib : not permit calling TclTkLib module methods +class << TclTkLib + def mainloop(check_root = true) + MultiTkIp.mainloop(check_root) + end + def mainloop_watchdog(check_root = true) + MultiTkIp.mainloop_watchdog(check_root) + end + def do_one_event(flag = TclTkLib::EventFlag::ALL) + MultiTkIp.do_one_event(flag) + end + #def mainloop_abort_on_exception + # MultiTkIp.mainloop_abort_on_exception + #end + #def mainloop_abort_on_exception=(mode) + # MultiTkIp.mainloop_abort_on_exception=(mode) + #end + def set_eventloop_tick(tick) + MultiTkIp.set_eventloop_tick(tick) + end + def get_eventloop_tick + MultiTkIp.get_eventloop_tick + end + def set_no_event_wait(tick) + MultiTkIp.set_no_event_wait(tick) + end + def get_no_event_wait + MultiTkIp.get_no_event_wait + end + def set_eventloop_weight(loop_max, no_event_tick) + MultiTkIp.set_eventloop_weight(loop_max, no_event_tick) + end + def get_eventloop_weight + MultiTkIp.get_eventloop_weight + end + def restart(*args) + MultiTkIp.restart(*args) + end + + def _merge_tklist(*args) + MultiTkIp._merge_tklist(*args) + end + def _conv_listelement(arg) + MultiTkIp._conv_listelement(arg) + end +end + + +# depend on TclTkIp +class MultiTkIp +# def mainloop(check_root = true, restart_on_dead = true) + def mainloop(check_root = true, restart_on_dead = false) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + + if WITH_RUBY_VM ### Ruby 1.9 !!!!!!!!!!! + return @interp_thread.value if @interp_thread + end + + #return self if self.slave? + #return self if self != @@DEFAULT_MASTER + if self != @@DEFAULT_MASTER + if @wait_on_mainloop[0] + begin + @wait_on_mainloop[1] += 1 + if $SAFE >= 4 + _receiver_mainloop(check_root).join + else + @cmd_queue.enq([@system, 'call_mainloop', + Thread.current, check_root]) + Thread.stop + end + rescue MultiTkIp_OK => ret + # return value + if ret.value.kind_of?(Thread) + return ret.value.value + else + return ret.value + end + rescue SystemExit => e + # exit IP + warn("Warning: " + e.inspect + " on " + self.inspect) if $DEBUG + begin + self._eval_without_enc('exit') + rescue Exception + end + self.delete + rescue StandardError => e + if $DEBUG + warn("Warning: " + e.class.inspect + + ((e.message.length > 0)? ' "' + e.message + '"': '') + + " on " + self.inspect) + end + return e + rescue Exception => e + return e + ensure + @wait_on_mainloop[1] -= 1 + end + end + return + end + + unless restart_on_dead + @wait_on_mainloop[1] += 1 +=begin + begin + @interp.mainloop(check_root) + rescue StandardError => e + if $DEBUG + warn("Warning: " + e.class.inspect + + ((e.message.length > 0)? ' "' + e.message + '"': '') + + " on " + self.inspect) + end + end +=end + begin + @interp.mainloop(check_root) + ensure + @wait_on_mainloop[1] -= 1 + end + else + loop do + break unless self.alive? + if check_root + begin + break if TclTkLib.num_of_mainwindows == 0 + rescue StandardError + break + end + end + break if @interp.deleted? + begin + @wait_on_mainloop[1] += 1 + @interp.mainloop(check_root) + rescue StandardError => e + if TclTkLib.mainloop_abort_on_exception != nil + #STDERR.print("Warning: Tk mainloop receives ", $!.class.inspect, + # " exception (ignore) : ", $!.message, "\n"); + if $DEBUG + warn("Warning: Tk mainloop receives " << e.class.inspect << + " exception (ignore) : " << e.message); + end + end + #raise e + rescue Exception => e +=begin + if TclTkLib.mainloop_abort_on_exception != nil + #STDERR.print("Warning: Tk mainloop receives ", $!.class.inspect, + # " exception (ignore) : ", $!.message, "\n"); + if $DEBUG + warn("Warning: Tk mainloop receives " << e.class.inspect << + " exception (ignore) : " << e.message); + end + end +=end + raise e + ensure + @wait_on_mainloop[1] -= 1 + Thread.pass # avoid eventloop conflict + end + end + end + self + end + + def make_safe + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.make_safe + end + + def safe? + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.safe? + end + + def safe_base? + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @safe_base + end + + def allow_ruby_exit? + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.allow_ruby_exit? + end + + def allow_ruby_exit= (mode) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.allow_ruby_exit = mode + end + + def delete + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @slave_ip_tbl.each{|name, subip| + _destroy_slaves_of_slaveIP(subip) +=begin + begin + subip._invoke('destroy', '.') unless subip.deleted? + rescue Exception + end +=end + begin + # subip._eval_without_enc("foreach i [after info] {after cancel $i}") + unless subip.deleted? + after_ids = subip._eval_without_enc("after info") + subip._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") + end + rescue Exception + end + + # safe_base? + if @interp._eval_without_enc("catch {::safe::interpConfigure #{name}}") == '0' + begin + @interp._eval_without_enc("::safe::interpDelete #{name}") + rescue Exception + else + next if subip.deleted? + end + end + if subip.respond_to?(:safe_base?) && subip.safe_base? && + !subip.deleted? + # do 'exit' to call the delete_hook procedure + begin + subip._eval_without_enc('exit') + rescue Exception + end + else + begin + subip.delete unless subip.deleted? + rescue Exception + end + end + } + + begin + # @interp._eval_without_enc("foreach i [after info] {after cancel $i}") + after_ids = @interp._eval_without_enc("after info") + @interp._eval_without_enc("foreach i {#{after_ids}} {after cancel $i}") + rescue Exception + end + + begin + @interp._invoke('destroy', '.') unless @interp.deleted? + rescue Exception + end + + if @safe_base && !@interp.deleted? + # do 'exit' to call the delete_hook procedure + @interp._eval_without_enc('exit') + end + @interp.delete + self + end + + def deleted? + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.deleted? + end + + def has_mainwindow? + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.has_mainwindow? + end + + def invalid_namespace? + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.invalid_namespace? + end + + def abort(msg = nil) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + if master? && !safe? && allow_ruby_exit? + if msg + Kernel.abort(msg) + else + Kernel.abort + end + else + # ignore msg + delete + 1 + end + end + + def exit(st = true) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + if master? && !safe? && allow_ruby_exit? + Kernel.exit(st) + else + delete + st + end + end + + def exit!(st = false) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + if master? && !safe? && allow_ruby_exit? + Kernel.exit!(st) + else + delete + st + end + end + + def restart(app_name = nil, keys = {}) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + + _init_ip_internal(@@INIT_IP_ENV, @@ADD_TK_PROCS) + + @interp._invoke('set', 'argv0', app_name) if app_name + if keys.kind_of?(Hash) + @interp._invoke('set', 'argv', _keys2opts(keys)) + end + + @interp.restart + end + + def __eval(str) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.__eval(str) + end + + def __invoke(*args) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.__invoke(*args) + end + + def _eval(str) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._eval(str) + end + + def _invoke(*args) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke(*args) + end + + def _eval_without_enc(str) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._eval_without_enc(str) + end + + def _invoke_without_enc(*args) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke_without_enc(*args) + end + + def _eval_with_enc(str) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._eval_with_enc(str) + end + + def _invoke_with_enc(*args) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke_with_enc(*args) + end + + def _toUTF8(str, encoding=nil) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._toUTF8(str, encoding) + end + + def _fromUTF8(str, encoding=nil) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._fromUTF8(str, encoding) + end + + def _thread_vwait(var) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._thread_vwait(var) + end + + def _thread_tkwait(mode, target) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._thread_tkwait(mode, target) + end + + def _return_value + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._return_value + end + + def _get_variable(var, flag) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._get_variable(var, flag) + end + def _get_variable2(var, idx, flag) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._get_variable2(var, idx, flag) + end + def _set_variable(var, value, flag) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._set_variable(var, value, flag) + end + def _set_variable2(var, idx, value, flag) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._set_variable2(var, idx, value, flag) + end + def _unset_variable(var, flag) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._unset_variable(var, flag) + end + def _unset_variable2(var, idx, flag) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._unset_variable2(var, idx, flag) + end + + def _get_global_var(var) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._get_global_var(var) + end + def _get_global_var2(var, idx) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._get_global_var2(var, idx) + end + def _set_global_var(var, value) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._set_global_var(var, value) + end + def _set_global_var2(var, idx, value) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._set_global_var2(var, idx, value) + end + def _unset_global_var(var) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._unset_global_var(var) + end + def _unset_global_var2(var, idx) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._unset_global_var2(var, idx) + end + + def _make_menu_embeddable(menu_path) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._make_menu_embeddable(menu_path) + end + + def _split_tklist(str) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._split_tklist(str) + end + def _merge_tklist(*args) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._merge_tklist(*args) + end + def _conv_listelement(arg) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._conv_listelement(arg) + end +end + + +# interp command support +class MultiTkIp + def _lst2ary(str) + return [] if str == "" + idx = str.index('{') + while idx and idx > 0 and str[idx-1] == ?\\ + idx = str.index('{', idx+1) + end + return str.split unless idx + + list = str[0,idx].split + str = str[idx+1..-1] + i = -1 + brace = 1 + str.each_byte {|c| + c = c.chr + i += 1 + brace += 1 if c == '{' + brace -= 1 if c == '}' + break if brace == 0 + } + if i == 0 + list.push '' + elsif str[0, i] == ' ' + list.push ' ' + else + list.push str[0..i-1] + end + #list += _lst2ary(str[i+1..-1]) + list.concat(_lst2ary(str[i+1..-1])) + list + end + private :_lst2ary + + def _slavearg(slave) + if slave.kind_of?(MultiTkIp) + slave.path + elsif slave.kind_of?(String) + slave + else + slave.to_s + end + end + private :_slavearg + + def alias_info(slave, cmd_name) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + _lst2ary(@interp._invoke('interp', 'alias', _slavearg(slave), cmd_name)) + end + def self.alias_info(slave, cmd_name) + __getip.alias_info(slave, cmd_name) + end + + def alias_delete(slave, cmd_name) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'alias', _slavearg(slave), cmd_name, '') + self + end + def self.alias_delete(slave, cmd_name) + __getip.alias_delete(slave, cmd_name) + self + end + + def def_alias(slave, new_cmd, org_cmd, *args) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + ret = @interp._invoke('interp', 'alias', _slavearg(slave), new_cmd, + '', org_cmd, *args) + (ret == new_cmd)? self: nil + end + def self.def_alias(slave, new_cmd, org_cmd, *args) + ret = __getip.def_alias(slave, new_cmd, org_cmd, *args) + (ret == new_cmd)? self: nil + end + + def aliases(slave = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + _lst2ary(@interp._invoke('interp', 'aliases', _slavearg(slave))) + end + def self.aliases(slave = '') + __getip.aliases(slave) + end + + def delete_slaves(*args) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + slaves = args.collect{|s| _slavearg(s)} + @interp._invoke('interp', 'delete', *slaves) if slaves.size > 0 + self + end + def self.delete_slaves(*args) + __getip.delete_slaves(*args) + self + end + + def exist?(slave = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + ret = @interp._invoke('interp', 'exists', _slavearg(slave)) + (ret == '1')? true: false + end + def self.exist?(slave = '') + __getip.exist?(slave) + end + + def delete_cmd(slave, cmd) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + slave_invoke = @interp._invoke('list', 'rename', cmd, '') + @interp._invoke('interp', 'eval', _slavearg(slave), slave_invoke) + self + end + def self.delete_cmd(slave, cmd) + __getip.delete_cmd(slave, cmd) + self + end + + def expose_cmd(slave, cmd, aliasname = nil) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + if aliasname + @interp._invoke('interp', 'expose', _slavearg(slave), cmd, aliasname) + else + @interp._invoke('interp', 'expose', _slavearg(slave), cmd) + end + self + end + def self.expose_cmd(slave, cmd, aliasname = nil) + __getip.expose_cmd(slave, cmd, aliasname) + self + end + + def hide_cmd(slave, cmd, aliasname = nil) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + if aliasname + @interp._invoke('interp', 'hide', _slavearg(slave), cmd, aliasname) + else + @interp._invoke('interp', 'hide', _slavearg(slave), cmd) + end + self + end + def self.hide_cmd(slave, cmd, aliasname = nil) + __getip.hide_cmd(slave, cmd, aliasname) + self + end + + def hidden_cmds(slave = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + _lst2ary(@interp._invoke('interp', 'hidden', _slavearg(slave))) + end + def self.hidden_cmds(slave = '') + __getip.hidden_cmds(slave) + end + + def invoke_hidden(slave, cmd, *args) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + if args[-1].kind_of?(Hash) + keys = _symbolkey2str(args.pop) + else + keys = [] + end + keys << _slavearg(slave) + if Tk::TCL_MAJOR_VERSION > 8 || + (Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION >= 5) + keys << '--' + end + keys << cmd + keys.concat(args) + @interp._invoke('interp', 'invokehidden', *keys) + end + def self.invoke_hidden(slave, cmd, *args) + __getip.invoke_hidden(slave, cmd, *args) + end + + def invoke_hidden_on_global(slave, cmd, *args) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + if args[-1].kind_of?(Hash) + keys = _symbolkey2str(args.pop) + else + keys = [] + end + keys << _slavearg(slave) + keys << '-global' + if Tk::TCL_MAJOR_VERSION > 8 || + (Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION >= 5) + keys << '--' + end + keys << cmd + keys.concat(args) + @interp._invoke('interp', 'invokehidden', *keys) + end + def self.invoke_hidden_on_global(slave, cmd, *args) + __getip.invoke_hidden_on_global(slave, cmd, *args) + end + + def invoke_hidden_on_namespace(slave, ns, cmd, *args) + # for Tcl8.5 or later + raise SecurityError, "no permission to manipulate" unless self.manipulable? + if args[-1].kind_of?(Hash) + keys = _symbolkey2str(args.pop) + else + keys = [] + end + keys << _slavearg(slave) + keys << '-namespace' << TkComm._get_eval_string(ns) + keys << '--' << cmd + keys.concat(args) + @interp._invoke('interp', 'invokehidden', *keys) + end + def self.invoke_hidden_on_namespace(slave, ns, cmd, *args) + __getip.invoke_hidden_on_namespace(slave, ns, cmd, *args) + end + + def mark_trusted(slave = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'marktrusted', _slavearg(slave)) + self + end + def self.mark_trusted(slave = '') + __getip.mark_trusted(slave) + self + end + + def set_bgerror_handler(cmd = Proc.new, slave = nil, &b) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + + unless TkComm._callback_entry?(cmd) + if !slave && b + slave = cmd + cmd = Proc.new(&b) + end + end + slave = '' unless slave + + @interp._invoke('interp', 'bgerror', _slavearg(slave), cmd) + end + def self.bgerror(cmd = Proc.new, slave = nil, &b) + __getip.bgerror(cmd, slave, &b) + end + + def get_bgerror_handler(slave = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + procedure(@interp._invoke('interp', 'bgerror', _slavearg(slave))) + end + def self.bgerror(slave = '') + __getip.bgerror(slave) + end + + def set_limit(limit_type, slave = '', opts = {}) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'limit', _slavearg(slave), limit_type, opts) + end + def self.set_limit(limit_type, slave = '', opts = {}) + __getip.set_limit(limit_type, slave, opts) + end + + def get_limit(limit_type, slave = '', slot = nil) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + + if slot + num_or_str(@interp._invoke('interp', 'limit', _slavearg(slave), + limit_type, slot)) + else + l = @interp._split_tklist(@interp._invoke_without_enc('interp', 'limit', + _slavearg(slave), + limit_type)) + l.map!{|s| _fromUTF8(s)} + r = {} + until l.empty? + key = l.shift[1..-1] + val = l.shift + val = num_or_str(val) if val + r[key] = val + end + r + end + end + def self.get_limit(limit_type, slave = '', slot = nil) + __getip.get_limit(limit_type, slave, slot) + end + + def recursion_limit(slave = '', limit = None) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + number(@interp._invoke('interp', 'recursionlimit', + _slavearg(slave), limit)) + end + def self.recursion_limit(slave = '', limit = None) + __getip.recursion_limit(slave) + end + + def alias_target(aliascmd, slave = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'target', _slavearg(slave), aliascmd) + end + def self.alias_target(aliascmd, slave = '') + __getip.alias_target(aliascmd, slave) + end + + def share_stdin(dist, src = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'share', src, 'stdin', dist) + self + end + def self.share_stdin(dist, src = '') + __getip.share_stdin(dist, src) + self + end + + def share_stdout(dist, src = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'share', src, 'stdout', dist) + self + end + def self.share_stdout(dist, src = '') + __getip.share_stdout(dist, src) + self + end + + def share_stderr(dist, src = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'share', src, 'stderr', dist) + self + end + def self.share_stderr(dist, src = '') + __getip.share_stderr(dist, src) + self + end + + def transfer_stdin(dist, src = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'transfer', src, 'stdin', dist) + self + end + def self.transfer_stdin(dist, src = '') + __getip.transfer_stdin(dist, src) + self + end + + def transfer_stdout(dist, src = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'transfer', src, 'stdout', dist) + self + end + def self.transfer_stdout(dist, src = '') + __getip.transfer_stdout(dist, src) + self + end + + def transfer_stderr(dist, src = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'transfer', src, 'stderr', dist) + self + end + def self.transfer_stderr(dist, src = '') + __getip.transfer_stderr(dist, src) + self + end + + def share_stdio(dist, src = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'share', src, 'stdin', dist) + @interp._invoke('interp', 'share', src, 'stdout', dist) + @interp._invoke('interp', 'share', src, 'stderr', dist) + self + end + def self.share_stdio(dist, src = '') + __getip.share_stdio(dist, src) + self + end + + def transfer_stdio(dist, src = '') + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp._invoke('interp', 'transfer', src, 'stdin', dist) + @interp._invoke('interp', 'transfer', src, 'stdout', dist) + @interp._invoke('interp', 'transfer', src, 'stderr', dist) + self + end + def self.transfer_stdio(dist, src = '') + __getip.transfer_stdio(dist, src) + self + end +end + + +# Safe Base :: manipulating safe interpreter +class MultiTkIp + def safeip_configure(slot, value=None) + # use for '-noStatics' option ==> {statics=>false} + # for '-nestedLoadOk' option ==> {nested=>true} + if slot.kind_of?(Hash) + ip = MultiTkIp.__getip + ip._eval('::safe::interpConfigure ' + @ip_name + ' ' + _keys2opts(slot)) + else + ip._eval('::safe::interpConfigure ' + @ip_name + ' ' + + "-#{slot} #{_get_eval_string(value)}") + end + self + end + + def safeip_configinfo(slot = nil) + ip = MultiTkIp.__getip + ret = {} + if slot + conf = _lst2ary(ip._eval("::safe::interpConfigure " + + @ip_name + " -#{slot}")) + if conf[0] == '-deleteHook' +=begin + if conf[1] =~ /^rb_out\S* (c(_\d+_)?\d+)/ + ret[conf[0][1..-1]] = MultiTkIp._tk_cmd_tbl[$1] +=end + if conf[1] =~ /rb_out\S*(?:\s+(::\S*|[{](::.*)[}]|["](::.*)["]))? (c(_\d+_)?(\d+))/ + ret[conf[0][1..-1]] = MultiTkIp._tk_cmd_tbl[$4] + else + ret[conf[0][1..-1]] = conf[1] + end + else + ret[conf[0][1..-1]] = conf[1] + end + else + Hash[*_lst2ary(ip._eval("::safe::interpConfigure " + + @ip_name))].each{|k, v| + if k == '-deleteHook' +=begin + if v =~ /^rb_out\S* (c(_\d+_)?\d+)/ + ret[k[1..-1]] = MultiTkIp._tk_cmd_tbl[$1] +=end + if v =~ /rb_out\S*(?:\s+(::\S*|[{](::.*)[}]|["](::.*)["]))? (c(_\d+_)?(\d+))/ + ret[k[1..-1]] = MultiTkIp._tk_cmd_tbl[$4] + else + ret[k[1..-1]] = v + end + else + ret[k[1..-1]] = v + end + } + end + ret + end + + def safeip_delete + ip = MultiTkIp.__getip + ip._eval("::safe::interpDelete " + @ip_name) + end + + def safeip_add_to_access_path(dir) + ip = MultiTkIp.__getip + ip._eval("::safe::interpAddToAccessPath #{@ip_name} #{dir}") + end + + def safeip_find_in_access_path(dir) + ip = MultiTkIp.__getip + ip._eval("::safe::interpFindInAccessPath #{@ip_name} #{dir}") + end + + def safeip_set_log_cmd(cmd = Proc.new) + ip = MultiTkIp.__getip + ip._eval("::safe::setLogCmd #{@ip_name} #{_get_eval_string(cmd)}") + end +end + + +# encoding convert +class << MultiTkIp + def encoding_table + __getip.encoding_table + end + + def force_default_encoding=(mode) + __getip.force_default_encoding=(mode) + end + + def force_default_encoding? + __getip.force_default_encoding? + end + + def default_encoding=(enc) + __getip.default_encoding=(enc) + end + + def encoding=(enc) + __getip.encoding=(enc) + end + + def encoding_name + __getip.encoding_name + end + + def encoding_obj + __getip.encoding_obj + end + alias encoding encoding_name + alias default_encoding encoding_name + + def encoding_convertfrom(str, enc=None) + __getip.encoding_convertfrom(str, enc) + end + alias encoding_convert_from encoding_convertfrom + + def encoding_convertto(str, enc=None) + __getip.encoding_convertto(str, enc) + end + alias encoding_convert_to encoding_convertto +end +class MultiTkIp + def encoding_table + @interp.encoding_table + end + + def force_default_encoding=(mode) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.force_default_encoding = mode + end + def force_default_encoding? + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.force_default_encoding? + end + + def default_encoding=(enc) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.default_encoding = enc + end + + def encoding=(enc) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.encoding = enc + end + def encoding_name + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.encoding_name + end + def encoding_obj + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.encoding_obj + end + alias encoding encoding_name + alias default_encoding encoding_name + + def encoding_convertfrom(str, enc=None) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.encoding_convertfrom(str, enc) + end + alias encoding_convert_from encoding_convertfrom + + def encoding_convertto(str, enc=None) + raise SecurityError, "no permission to manipulate" unless self.manipulable? + @interp.encoding_convertto(str, enc) + end + alias encoding_convert_to encoding_convertto +end + + +# remove methods for security +=begin +class MultiTkIp + INTERP_THREAD = @@DEFAULT_MASTER.instance_variable_get('@interp_thread') + INTERP_MUTEX = INTERP_THREAD[:mutex] + INTERP_ROOT_CHECK = INTERP_THREAD[:root_check] + + # undef_method :instance_eval + undef_method :instance_variable_get + undef_method :instance_variable_set +end + +module TkCore + if MultiTkIp::WITH_RUBY_VM && + ! MultiTkIp::RUN_EVENTLOOP_ON_MAIN_THREAD ### check Ruby 1.9 !!!!!!! + INTERP_THREAD = MultiTkIp::INTERP_THREAD + INTERP_MUTEX = MultiTkIp::INTERP_MUTEX + INTERP_ROOT_CHECK = MultiTkIp::INTERP_ROOT_CHECK + end +end +class MultiTkIp + remove_const(:INTERP_THREAD) + remove_const(:INTERP_MUTEX) + remove_const(:INTERP_ROOT_CHECK) +end +=end +if MultiTkIp::WITH_RUBY_VM && + ! MultiTkIp::RUN_EVENTLOOP_ON_MAIN_THREAD ### check Ruby 1.9 !!!!!!! + class MultiTkIp + INTERP_THREAD = @@DEFAULT_MASTER.instance_variable_get('@interp_thread') + INTERP_THREAD_STATUS = INTERP_THREAD[:status] + INTERP_MUTEX = INTERP_THREAD[:mutex] + INTERP_ROOT_CHECK = INTERP_THREAD[:root_check] + end + module TkCore + INTERP_THREAD = MultiTkIp::INTERP_THREAD + INTERP_THREAD_STATUS = MultiTkIp::INTERP_THREAD_STATUS + INTERP_MUTEX = MultiTkIp::INTERP_MUTEX + INTERP_ROOT_CHECK = MultiTkIp::INTERP_ROOT_CHECK + end + class MultiTkIp + remove_const(:INTERP_THREAD) + remove_const(:INTERP_THREAD_STATUS) + remove_const(:INTERP_MUTEX) + remove_const(:INTERP_ROOT_CHECK) + end +end + +class MultiTkIp + # undef_method :instance_eval + undef_method :instance_variable_get + undef_method :instance_variable_set +end +# end of MultiTkIp definition + +# defend against modification +#MultiTkIp.freeze +#TclTkLib.freeze + +######################################## +# start Tk which depends on MultiTkIp +module TkCore + INTERP = MultiTkIp +end +require 'tk' diff --git a/ruby/lib/ruby/2.1.0/mutex_m.rb b/ruby/lib/ruby/2.1.0/mutex_m.rb new file mode 100644 index 0000000..6698cb5 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/mutex_m.rb @@ -0,0 +1,111 @@ +# +# mutex_m.rb - +# $Release Version: 3.0$ +# $Revision: 1.7 $ +# Original from mutex.rb +# by Keiju ISHITSUKA(keiju@ishitsuka.com) +# modified by matz +# patched by akira yamada +# +# -- + + +require 'thread' + +# = mutex_m.rb +# +# When 'mutex_m' is required, any object that extends or includes Mutex_m will +# be treated like a Mutex. +# +# Start by requiring the standard library Mutex_m: +# +# require "mutex_m.rb" +# +# From here you can extend an object with Mutex instance methods: +# +# obj = Object.new +# obj.extend Mutex_m +# +# Or mixin Mutex_m into your module to your class inherit Mutex instance +# methods. +# +# class Foo +# include Mutex_m +# # ... +# end +# obj = Foo.new +# # this obj can be handled like Mutex +# +module Mutex_m + def Mutex_m.define_aliases(cl) # :nodoc: + cl.module_eval %q{ + alias locked? mu_locked? + alias lock mu_lock + alias unlock mu_unlock + alias try_lock mu_try_lock + alias synchronize mu_synchronize + } + end + + def Mutex_m.append_features(cl) # :nodoc: + super + define_aliases(cl) unless cl.instance_of?(Module) + end + + def Mutex_m.extend_object(obj) # :nodoc: + super + obj.mu_extended + end + + def mu_extended # :nodoc: + unless (defined? locked? and + defined? lock and + defined? unlock and + defined? try_lock and + defined? synchronize) + Mutex_m.define_aliases(singleton_class) + end + mu_initialize + end + + # See Mutex#synchronize + def mu_synchronize(&block) + @_mutex.synchronize(&block) + end + + # See Mutex#locked? + def mu_locked? + @_mutex.locked? + end + + # See Mutex#try_lock + def mu_try_lock + @_mutex.try_lock + end + + # See Mutex#lock + def mu_lock + @_mutex.lock + end + + # See Mutex#unlock + def mu_unlock + @_mutex.unlock + end + + # See Mutex#sleep + def sleep(timeout = nil) + @_mutex.sleep(timeout) + end + + private + + def mu_initialize # :nodoc: + @_mutex = Mutex.new + end + + def initialize(*args) # :nodoc: + mu_initialize + super + end +end diff --git a/ruby/lib/ruby/2.1.0/net/ftp.rb b/ruby/lib/ruby/2.1.0/net/ftp.rb new file mode 100644 index 0000000..a57372a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/ftp.rb @@ -0,0 +1,1126 @@ +# +# = net/ftp.rb - FTP Client Library +# +# Written by Shugo Maeda . +# +# Documentation by Gavin Sinclair, sourced from "Programming Ruby" (Hunt/Thomas) +# and "Ruby In a Nutshell" (Matsumoto), used with permission. +# +# This library is distributed under the terms of the Ruby license. +# You can freely distribute/modify this library. +# +# It is included in the Ruby standard library. +# +# See the Net::FTP class for an overview. +# + +require "socket" +require "monitor" +require "net/protocol" + +module Net + + # :stopdoc: + class FTPError < StandardError; end + class FTPReplyError < FTPError; end + class FTPTempError < FTPError; end + class FTPPermError < FTPError; end + class FTPProtoError < FTPError; end + class FTPConnectionError < FTPError; end + # :startdoc: + + # + # This class implements the File Transfer Protocol. If you have used a + # command-line FTP program, and are familiar with the commands, you will be + # able to use this class easily. Some extra features are included to take + # advantage of Ruby's style and strengths. + # + # == Example + # + # require 'net/ftp' + # + # === Example 1 + # + # ftp = Net::FTP.new('example.com') + # ftp.login + # files = ftp.chdir('pub/lang/ruby/contrib') + # files = ftp.list('n*') + # ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024) + # ftp.close + # + # === Example 2 + # + # Net::FTP.open('example.com') do |ftp| + # ftp.login + # files = ftp.chdir('pub/lang/ruby/contrib') + # files = ftp.list('n*') + # ftp.getbinaryfile('nif.rb-0.91.gz', 'nif.gz', 1024) + # end + # + # == Major Methods + # + # The following are the methods most likely to be useful to users: + # - FTP.open + # - #getbinaryfile + # - #gettextfile + # - #putbinaryfile + # - #puttextfile + # - #chdir + # - #nlst + # - #size + # - #rename + # - #delete + # + class FTP + include MonitorMixin + + # :stopdoc: + FTP_PORT = 21 + CRLF = "\r\n" + DEFAULT_BLOCKSIZE = BufferedIO::BUFSIZE + # :startdoc: + + # When +true+, transfers are performed in binary mode. Default: +true+. + attr_reader :binary + + # When +true+, the connection is in passive mode. Default: +false+. + attr_accessor :passive + + # When +true+, all traffic to and from the server is written + # to +$stdout+. Default: +false+. + attr_accessor :debug_mode + + # Sets or retrieves the +resume+ status, which decides whether incomplete + # transfers are resumed or restarted. Default: +false+. + attr_accessor :resume + + # Number of seconds to wait for the connection to open. Any number + # may be used, including Floats for fractional seconds. If the FTP + # object cannot open a connection in this many seconds, it raises a + # Net::OpenTimeout exception. The default value is +nil+. + attr_accessor :open_timeout + + # Number of seconds to wait for one block to be read (via one read(2) + # call). Any number may be used, including Floats for fractional + # seconds. If the FTP object cannot read data in this many seconds, + # it raises a TimeoutError exception. The default value is 60 seconds. + attr_reader :read_timeout + + # Setter for the read_timeout attribute. + def read_timeout=(sec) + @sock.read_timeout = sec + @read_timeout = sec + end + + # The server's welcome message. + attr_reader :welcome + + # The server's last response code. + attr_reader :last_response_code + alias lastresp last_response_code + + # The server's last response. + attr_reader :last_response + + # + # A synonym for FTP.new, but with a mandatory host parameter. + # + # If a block is given, it is passed the +FTP+ object, which will be closed + # when the block finishes, or when an exception is raised. + # + def FTP.open(host, user = nil, passwd = nil, acct = nil) + if block_given? + ftp = new(host, user, passwd, acct) + begin + yield ftp + ensure + ftp.close + end + else + new(host, user, passwd, acct) + end + end + + # + # Creates and returns a new +FTP+ object. If a +host+ is given, a connection + # is made. Additionally, if the +user+ is given, the given user name, + # password, and (optionally) account are used to log in. See #login. + # + def initialize(host = nil, user = nil, passwd = nil, acct = nil) + super() + @binary = true + @passive = false + @debug_mode = false + @resume = false + @sock = NullSocket.new + @logged_in = false + @open_timeout = nil + @read_timeout = 60 + if host + connect(host) + if user + login(user, passwd, acct) + end + end + end + + # A setter to toggle transfers in binary mode. + # +newmode+ is either +true+ or +false+ + def binary=(newmode) + if newmode != @binary + @binary = newmode + send_type_command if @logged_in + end + end + + # Sends a command to destination host, with the current binary sendmode + # type. + # + # If binary mode is +true+, then "TYPE I" (image) is sent, otherwise "TYPE + # A" (ascii) is sent. + def send_type_command # :nodoc: + if @binary + voidcmd("TYPE I") + else + voidcmd("TYPE A") + end + end + private :send_type_command + + # Toggles transfers in binary mode and yields to a block. + # This preserves your current binary send mode, but allows a temporary + # transaction with binary sendmode of +newmode+. + # + # +newmode+ is either +true+ or +false+ + def with_binary(newmode) # :nodoc: + oldmode = binary + self.binary = newmode + begin + yield + ensure + self.binary = oldmode + end + end + private :with_binary + + # Obsolete + def return_code # :nodoc: + $stderr.puts("warning: Net::FTP#return_code is obsolete and do nothing") + return "\n" + end + + # Obsolete + def return_code=(s) # :nodoc: + $stderr.puts("warning: Net::FTP#return_code= is obsolete and do nothing") + end + + # Constructs a socket with +host+ and +port+. + # + # If SOCKSSocket is defined and the environment (ENV) defines + # SOCKS_SERVER, then a SOCKSSocket is returned, else a TCPSocket is + # returned. + def open_socket(host, port) # :nodoc: + return Timeout.timeout(@open_timeout, Net::OpenTimeout) { + if defined? SOCKSSocket and ENV["SOCKS_SERVER"] + @passive = true + sock = SOCKSSocket.open(host, port) + else + sock = TCPSocket.open(host, port) + end + io = BufferedSocket.new(sock) + io.read_timeout = @read_timeout + io + } + end + private :open_socket + + # + # Establishes an FTP connection to host, optionally overriding the default + # port. If the environment variable +SOCKS_SERVER+ is set, sets up the + # connection through a SOCKS proxy. Raises an exception (typically + # Errno::ECONNREFUSED) if the connection cannot be established. + # + def connect(host, port = FTP_PORT) + if @debug_mode + print "connect: ", host, ", ", port, "\n" + end + synchronize do + @sock = open_socket(host, port) + voidresp + end + end + + # + # Set the socket used to connect to the FTP server. + # + # May raise FTPReplyError if +get_greeting+ is false. + def set_socket(sock, get_greeting = true) + synchronize do + @sock = sock + if get_greeting + voidresp + end + end + end + + # If string +s+ includes the PASS command (password), then the contents of + # the password are cleaned from the string using "*" + def sanitize(s) # :nodoc: + if s =~ /^PASS /i + return s[0, 5] + "*" * (s.length - 5) + else + return s + end + end + private :sanitize + + # Ensures that +line+ has a control return / line feed (CRLF) and writes + # it to the socket. + def putline(line) # :nodoc: + if @debug_mode + print "put: ", sanitize(line), "\n" + end + line = line + CRLF + @sock.write(line) + end + private :putline + + # Reads a line from the sock. If EOF, then it will raise EOFError + def getline # :nodoc: + line = @sock.readline # if get EOF, raise EOFError + line.sub!(/(\r\n|\n|\r)\z/n, "") + if @debug_mode + print "get: ", sanitize(line), "\n" + end + return line + end + private :getline + + # Receive a section of lines until the response code's match. + def getmultiline # :nodoc: + line = getline + buff = line + if line[3] == ?- + code = line[0, 3] + begin + line = getline + buff << "\n" << line + end until line[0, 3] == code and line[3] != ?- + end + return buff << "\n" + end + private :getmultiline + + # Receives a response from the destination host. + # + # Returns the response code or raises FTPTempError, FTPPermError, or + # FTPProtoError + def getresp # :nodoc: + @last_response = getmultiline + @last_response_code = @last_response[0, 3] + case @last_response_code + when /\A[123]/ + return @last_response + when /\A4/ + raise FTPTempError, @last_response + when /\A5/ + raise FTPPermError, @last_response + else + raise FTPProtoError, @last_response + end + end + private :getresp + + # Receives a response. + # + # Raises FTPReplyError if the first position of the response code is not + # equal 2. + def voidresp # :nodoc: + resp = getresp + if resp[0] != ?2 + raise FTPReplyError, resp + end + end + private :voidresp + + # + # Sends a command and returns the response. + # + def sendcmd(cmd) + synchronize do + putline(cmd) + return getresp + end + end + + # + # Sends a command and expect a response beginning with '2'. + # + def voidcmd(cmd) + synchronize do + putline(cmd) + voidresp + end + end + + # Constructs and send the appropriate PORT (or EPRT) command + def sendport(host, port) # :nodoc: + af = (@sock.peeraddr)[0] + if af == "AF_INET" + cmd = "PORT " + (host.split(".") + port.divmod(256)).join(",") + elsif af == "AF_INET6" + cmd = sprintf("EPRT |2|%s|%d|", host, port) + else + raise FTPProtoError, host + end + voidcmd(cmd) + end + private :sendport + + # Constructs a TCPServer socket, and sends it the PORT command + # + # Returns the constructed TCPServer socket + def makeport # :nodoc: + sock = TCPServer.open(@sock.addr[3], 0) + port = sock.addr[1] + host = sock.addr[3] + sendport(host, port) + return sock + end + private :makeport + + # sends the appropriate command to enable a passive connection + def makepasv # :nodoc: + if @sock.peeraddr[0] == "AF_INET" + host, port = parse227(sendcmd("PASV")) + else + host, port = parse229(sendcmd("EPSV")) + # host, port = parse228(sendcmd("LPSV")) + end + return host, port + end + private :makepasv + + # Constructs a connection for transferring data + def transfercmd(cmd, rest_offset = nil) # :nodoc: + if @passive + host, port = makepasv + conn = open_socket(host, port) + if @resume and rest_offset + resp = sendcmd("REST " + rest_offset.to_s) + if resp[0] != ?3 + raise FTPReplyError, resp + end + end + resp = sendcmd(cmd) + # skip 2XX for some ftp servers + resp = getresp if resp[0] == ?2 + if resp[0] != ?1 + raise FTPReplyError, resp + end + else + sock = makeport + begin + if @resume and rest_offset + resp = sendcmd("REST " + rest_offset.to_s) + if resp[0] != ?3 + raise FTPReplyError, resp + end + end + resp = sendcmd(cmd) + # skip 2XX for some ftp servers + resp = getresp if resp[0] == ?2 + if resp[0] != ?1 + raise FTPReplyError, resp + end + conn = BufferedSocket.new(sock.accept) + conn.read_timeout = @read_timeout + sock.shutdown(Socket::SHUT_WR) rescue nil + sock.read rescue nil + ensure + sock.close + end + end + return conn + end + private :transfercmd + + # + # Logs in to the remote host. The session must have been previously + # connected. If +user+ is the string "anonymous" and the +password+ is + # +nil+, a password of user@host is synthesized. If the +acct+ + # parameter is not +nil+, an FTP ACCT command is sent following the + # successful login. Raises an exception on error (typically + # Net::FTPPermError). + # + def login(user = "anonymous", passwd = nil, acct = nil) + if user == "anonymous" and passwd == nil + passwd = "anonymous@" + end + + resp = "" + synchronize do + resp = sendcmd('USER ' + user) + if resp[0] == ?3 + raise FTPReplyError, resp if passwd.nil? + resp = sendcmd('PASS ' + passwd) + end + if resp[0] == ?3 + raise FTPReplyError, resp if acct.nil? + resp = sendcmd('ACCT ' + acct) + end + end + if resp[0] != ?2 + raise FTPReplyError, resp + end + @welcome = resp + send_type_command + @logged_in = true + end + + # + # Puts the connection into binary (image) mode, issues the given command, + # and fetches the data returned, passing it to the associated block in + # chunks of +blocksize+ characters. Note that +cmd+ is a server command + # (such as "RETR myfile"). + # + def retrbinary(cmd, blocksize, rest_offset = nil) # :yield: data + synchronize do + with_binary(true) do + begin + conn = transfercmd(cmd, rest_offset) + loop do + data = conn.read(blocksize) + break if data == nil + yield(data) + end + conn.shutdown(Socket::SHUT_WR) + conn.read_timeout = 1 + conn.read + ensure + conn.close if conn + end + voidresp + end + end + end + + # + # Puts the connection into ASCII (text) mode, issues the given command, and + # passes the resulting data, one line at a time, to the associated block. If + # no block is given, prints the lines. Note that +cmd+ is a server command + # (such as "RETR myfile"). + # + def retrlines(cmd) # :yield: line + synchronize do + with_binary(false) do + begin + conn = transfercmd(cmd) + loop do + line = conn.gets + break if line == nil + yield(line.sub(/\r?\n\z/, ""), !line.match(/\n\z/).nil?) + end + conn.shutdown(Socket::SHUT_WR) + conn.read_timeout = 1 + conn.read + ensure + conn.close if conn + end + voidresp + end + end + end + + # + # Puts the connection into binary (image) mode, issues the given server-side + # command (such as "STOR myfile"), and sends the contents of the file named + # +file+ to the server. If the optional block is given, it also passes it + # the data, in chunks of +blocksize+ characters. + # + def storbinary(cmd, file, blocksize, rest_offset = nil) # :yield: data + if rest_offset + file.seek(rest_offset, IO::SEEK_SET) + end + synchronize do + with_binary(true) do + conn = transfercmd(cmd) + loop do + buf = file.read(blocksize) + break if buf == nil + conn.write(buf) + yield(buf) if block_given? + end + conn.close + voidresp + end + end + rescue Errno::EPIPE + # EPIPE, in this case, means that the data connection was unexpectedly + # terminated. Rather than just raising EPIPE to the caller, check the + # response on the control connection. If getresp doesn't raise a more + # appropriate exception, re-raise the original exception. + getresp + raise + end + + # + # Puts the connection into ASCII (text) mode, issues the given server-side + # command (such as "STOR myfile"), and sends the contents of the file + # named +file+ to the server, one line at a time. If the optional block is + # given, it also passes it the lines. + # + def storlines(cmd, file) # :yield: line + synchronize do + with_binary(false) do + conn = transfercmd(cmd) + loop do + buf = file.gets + break if buf == nil + if buf[-2, 2] != CRLF + buf = buf.chomp + CRLF + end + conn.write(buf) + yield(buf) if block_given? + end + conn.close + voidresp + end + end + rescue Errno::EPIPE + # EPIPE, in this case, means that the data connection was unexpectedly + # terminated. Rather than just raising EPIPE to the caller, check the + # response on the control connection. If getresp doesn't raise a more + # appropriate exception, re-raise the original exception. + getresp + raise + end + + # + # Retrieves +remotefile+ in binary mode, storing the result in +localfile+. + # If +localfile+ is nil, returns retrieved data. + # If a block is supplied, it is passed the retrieved data in +blocksize+ + # chunks. + # + def getbinaryfile(remotefile, localfile = File.basename(remotefile), + blocksize = DEFAULT_BLOCKSIZE) # :yield: data + result = nil + if localfile + if @resume + rest_offset = File.size?(localfile) + f = open(localfile, "a") + else + rest_offset = nil + f = open(localfile, "w") + end + elsif !block_given? + result = "" + end + begin + f.binmode if localfile + retrbinary("RETR " + remotefile.to_s, blocksize, rest_offset) do |data| + f.write(data) if localfile + yield(data) if block_given? + result.concat(data) if result + end + return result + ensure + f.close if localfile + end + end + + # + # Retrieves +remotefile+ in ASCII (text) mode, storing the result in + # +localfile+. + # If +localfile+ is nil, returns retrieved data. + # If a block is supplied, it is passed the retrieved data one + # line at a time. + # + def gettextfile(remotefile, localfile = File.basename(remotefile)) # :yield: line + result = nil + if localfile + f = open(localfile, "w") + elsif !block_given? + result = "" + end + begin + retrlines("RETR " + remotefile) do |line, newline| + l = newline ? line + "\n" : line + f.print(l) if localfile + yield(line, newline) if block_given? + result.concat(l) if result + end + return result + ensure + f.close if localfile + end + end + + # + # Retrieves +remotefile+ in whatever mode the session is set (text or + # binary). See #gettextfile and #getbinaryfile. + # + def get(remotefile, localfile = File.basename(remotefile), + blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data + if @binary + getbinaryfile(remotefile, localfile, blocksize, &block) + else + gettextfile(remotefile, localfile, &block) + end + end + + # + # Transfers +localfile+ to the server in binary mode, storing the result in + # +remotefile+. If a block is supplied, calls it, passing in the transmitted + # data in +blocksize+ chunks. + # + def putbinaryfile(localfile, remotefile = File.basename(localfile), + blocksize = DEFAULT_BLOCKSIZE, &block) # :yield: data + if @resume + begin + rest_offset = size(remotefile) + rescue Net::FTPPermError + rest_offset = nil + end + else + rest_offset = nil + end + f = open(localfile) + begin + f.binmode + if rest_offset + storbinary("APPE " + remotefile, f, blocksize, rest_offset, &block) + else + storbinary("STOR " + remotefile, f, blocksize, rest_offset, &block) + end + ensure + f.close + end + end + + # + # Transfers +localfile+ to the server in ASCII (text) mode, storing the result + # in +remotefile+. If callback or an associated block is supplied, calls it, + # passing in the transmitted data one line at a time. + # + def puttextfile(localfile, remotefile = File.basename(localfile), &block) # :yield: line + f = open(localfile) + begin + storlines("STOR " + remotefile, f, &block) + ensure + f.close + end + end + + # + # Transfers +localfile+ to the server in whatever mode the session is set + # (text or binary). See #puttextfile and #putbinaryfile. + # + def put(localfile, remotefile = File.basename(localfile), + blocksize = DEFAULT_BLOCKSIZE, &block) + if @binary + putbinaryfile(localfile, remotefile, blocksize, &block) + else + puttextfile(localfile, remotefile, &block) + end + end + + # + # Sends the ACCT command. + # + # This is a less common FTP command, to send account + # information if the destination host requires it. + # + def acct(account) + cmd = "ACCT " + account + voidcmd(cmd) + end + + # + # Returns an array of filenames in the remote directory. + # + def nlst(dir = nil) + cmd = "NLST" + if dir + cmd = cmd + " " + dir + end + files = [] + retrlines(cmd) do |line| + files.push(line) + end + return files + end + + # + # Returns an array of file information in the directory (the output is like + # `ls -l`). If a block is given, it iterates through the listing. + # + def list(*args, &block) # :yield: line + cmd = "LIST" + args.each do |arg| + cmd = cmd + " " + arg.to_s + end + if block + retrlines(cmd, &block) + else + lines = [] + retrlines(cmd) do |line| + lines << line + end + return lines + end + end + alias ls list + alias dir list + + # + # Renames a file on the server. + # + def rename(fromname, toname) + resp = sendcmd("RNFR " + fromname) + if resp[0] != ?3 + raise FTPReplyError, resp + end + voidcmd("RNTO " + toname) + end + + # + # Deletes a file on the server. + # + def delete(filename) + resp = sendcmd("DELE " + filename) + if resp[0, 3] == "250" + return + elsif resp[0] == ?5 + raise FTPPermError, resp + else + raise FTPReplyError, resp + end + end + + # + # Changes the (remote) directory. + # + def chdir(dirname) + if dirname == ".." + begin + voidcmd("CDUP") + return + rescue FTPPermError => e + if e.message[0, 3] != "500" + raise e + end + end + end + cmd = "CWD " + dirname + voidcmd(cmd) + end + + # + # Returns the size of the given (remote) filename. + # + def size(filename) + with_binary(true) do + resp = sendcmd("SIZE " + filename) + if resp[0, 3] != "213" + raise FTPReplyError, resp + end + return resp[3..-1].strip.to_i + end + end + + MDTM_REGEXP = /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ # :nodoc: + + # + # Returns the last modification time of the (remote) file. If +local+ is + # +true+, it is returned as a local time, otherwise it's a UTC time. + # + def mtime(filename, local = false) + str = mdtm(filename) + ary = str.scan(MDTM_REGEXP)[0].collect {|i| i.to_i} + return local ? Time.local(*ary) : Time.gm(*ary) + end + + # + # Creates a remote directory. + # + def mkdir(dirname) + resp = sendcmd("MKD " + dirname) + return parse257(resp) + end + + # + # Removes a remote directory. + # + def rmdir(dirname) + voidcmd("RMD " + dirname) + end + + # + # Returns the current remote directory. + # + def pwd + resp = sendcmd("PWD") + return parse257(resp) + end + alias getdir pwd + + # + # Returns system information. + # + def system + resp = sendcmd("SYST") + if resp[0, 3] != "215" + raise FTPReplyError, resp + end + return resp[4 .. -1] + end + + # + # Aborts the previous command (ABOR command). + # + def abort + line = "ABOR" + CRLF + print "put: ABOR\n" if @debug_mode + @sock.send(line, Socket::MSG_OOB) + resp = getmultiline + unless ["426", "226", "225"].include?(resp[0, 3]) + raise FTPProtoError, resp + end + return resp + end + + # + # Returns the status (STAT command). + # + def status + line = "STAT" + CRLF + print "put: STAT\n" if @debug_mode + @sock.send(line, Socket::MSG_OOB) + return getresp + end + + # + # Returns the raw last modification time of the (remote) file in the format + # "YYYYMMDDhhmmss" (MDTM command). + # + # Use +mtime+ if you want a parsed Time instance. + # + def mdtm(filename) + resp = sendcmd("MDTM " + filename) + if resp[0, 3] == "213" + return resp[3 .. -1].strip + end + end + + # + # Issues the HELP command. + # + def help(arg = nil) + cmd = "HELP" + if arg + cmd = cmd + " " + arg + end + sendcmd(cmd) + end + + # + # Exits the FTP session. + # + def quit + voidcmd("QUIT") + end + + # + # Issues a NOOP command. + # + # Does nothing except return a response. + # + def noop + voidcmd("NOOP") + end + + # + # Issues a SITE command. + # + def site(arg) + cmd = "SITE " + arg + voidcmd(cmd) + end + + # + # Closes the connection. Further operations are impossible until you open + # a new connection with #connect. + # + def close + if @sock and not @sock.closed? + begin + @sock.shutdown(Socket::SHUT_WR) rescue nil + orig, self.read_timeout = self.read_timeout, 3 + @sock.read rescue nil + ensure + @sock.close + self.read_timeout = orig + end + end + end + + # + # Returns +true+ iff the connection is closed. + # + def closed? + @sock == nil or @sock.closed? + end + + # handler for response code 227 + # (Entering Passive Mode (h1,h2,h3,h4,p1,p2)) + # + # Returns host and port. + def parse227(resp) # :nodoc: + if resp[0, 3] != "227" + raise FTPReplyError, resp + end + if m = /\((?\d+(,\d+){3}),(?\d+,\d+)\)/.match(resp) + return parse_pasv_ipv4_host(m["host"]), parse_pasv_port(m["port"]) + else + raise FTPProtoError, resp + end + end + private :parse227 + + # handler for response code 228 + # (Entering Long Passive Mode) + # + # Returns host and port. + def parse228(resp) # :nodoc: + if resp[0, 3] != "228" + raise FTPReplyError, resp + end + if m = /\(4,4,(?\d+(,\d+){3}),2,(?\d+,\d+)\)/.match(resp) + return parse_pasv_ipv4_host(m["host"]), parse_pasv_port(m["port"]) + elsif m = /\(6,16,(?\d+(,(\d+)){15}),2,(?\d+,\d+)\)/.match(resp) + return parse_pasv_ipv6_host(m["host"]), parse_pasv_port(m["port"]) + else + raise FTPProtoError, resp + end + end + private :parse228 + + def parse_pasv_ipv4_host(s) + return s.tr(",", ".") + end + private :parse_pasv_ipv4_host + + def parse_pasv_ipv6_host(s) + return s.split(/,/).map { |i| + "%02x" % i.to_i + }.each_slice(2).map(&:join).join(":") + end + private :parse_pasv_ipv6_host + + def parse_pasv_port(s) + return s.split(/,/).map(&:to_i).inject { |x, y| + (x << 8) + y + } + end + private :parse_pasv_port + + # handler for response code 229 + # (Extended Passive Mode Entered) + # + # Returns host and port. + def parse229(resp) # :nodoc: + if resp[0, 3] != "229" + raise FTPReplyError, resp + end + if m = /\((?[!-~])\k\k(?\d+)\k\)/.match(resp) + return @sock.peeraddr[3], m["port"].to_i + else + raise FTPProtoError, resp + end + end + private :parse229 + + # handler for response code 257 + # ("PATHNAME" created) + # + # Returns host and port. + def parse257(resp) # :nodoc: + if resp[0, 3] != "257" + raise FTPReplyError, resp + end + if resp[3, 2] != ' "' + return "" + end + dirname = "" + i = 5 + n = resp.length + while i < n + c = resp[i, 1] + i = i + 1 + if c == '"' + if i > n or resp[i, 1] != '"' + break + end + i = i + 1 + end + dirname = dirname + c + end + return dirname + end + private :parse257 + + # :stopdoc: + class NullSocket + def read_timeout=(sec) + end + + def close + end + + def method_missing(mid, *args) + raise FTPConnectionError, "not connected" + end + end + + class BufferedSocket < BufferedIO + [:addr, :peeraddr, :send, :shutdown].each do |method| + define_method(method) { |*args| + @io.__send__(method, *args) + } + end + + def read(len = nil) + if len + s = super(len, "", true) + return s.empty? ? nil : s + else + result = "" + while s = super(DEFAULT_BLOCKSIZE, "", true) + break if s.empty? + result << s + end + return result + end + end + + def gets + line = readuntil("\n", true) + return line.empty? ? nil : line + end + + def readline + line = gets + if line.nil? + raise EOFError, "end of file reached" + end + return line + end + end + # :startdoc: + end +end + + +# Documentation comments: +# - sourced from pickaxe and nutshell, with improvements (hopefully) diff --git a/ruby/lib/ruby/2.1.0/net/http.rb b/ruby/lib/ruby/2.1.0/net/http.rb new file mode 100644 index 0000000..ae68393 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/http.rb @@ -0,0 +1,1556 @@ +# +# = net/http.rb +# +# Copyright (c) 1999-2007 Yukihiro Matsumoto +# Copyright (c) 1999-2007 Minero Aoki +# Copyright (c) 2001 GOTOU Yuuzou +# +# Written and maintained by Minero Aoki . +# HTTPS support added by GOTOU Yuuzou . +# +# This file is derived from "http-access.rb". +# +# Documented by Minero Aoki; converted to RDoc by William Webber. +# +# This program is free software. You can re-distribute and/or +# modify this program under the same terms of ruby itself --- +# Ruby Distribution License or GNU General Public License. +# +# See Net::HTTP for an overview and examples. +# + +require 'net/protocol' +require 'uri' + +module Net #:nodoc: + autoload :OpenSSL, 'openssl' + + # :stopdoc: + class HTTPBadResponse < StandardError; end + class HTTPHeaderSyntaxError < StandardError; end + # :startdoc: + + # == An HTTP client API for Ruby. + # + # Net::HTTP provides a rich library which can be used to build HTTP + # user-agents. For more details about HTTP see + # [RFC2616](http://www.ietf.org/rfc/rfc2616.txt) + # + # Net::HTTP is designed to work closely with URI. URI::HTTP#host, + # URI::HTTP#port and URI::HTTP#request_uri are designed to work with + # Net::HTTP. + # + # If you are only performing a few GET requests you should try OpenURI. + # + # == Simple Examples + # + # All examples assume you have loaded Net::HTTP with: + # + # require 'net/http' + # + # This will also require 'uri' so you don't need to require it separately. + # + # The Net::HTTP methods in the following section do not persist + # connections. They are not recommended if you are performing many HTTP + # requests. + # + # === GET + # + # Net::HTTP.get('example.com', '/index.html') # => String + # + # === GET by URI + # + # uri = URI('http://example.com/index.html?count=10') + # Net::HTTP.get(uri) # => String + # + # === GET with Dynamic Parameters + # + # uri = URI('http://example.com/index.html') + # params = { :limit => 10, :page => 3 } + # uri.query = URI.encode_www_form(params) + # + # res = Net::HTTP.get_response(uri) + # puts res.body if res.is_a?(Net::HTTPSuccess) + # + # === POST + # + # uri = URI('http://www.example.com/search.cgi') + # res = Net::HTTP.post_form(uri, 'q' => 'ruby', 'max' => '50') + # puts res.body + # + # === POST with Multiple Values + # + # uri = URI('http://www.example.com/search.cgi') + # res = Net::HTTP.post_form(uri, 'q' => ['ruby', 'perl'], 'max' => '50') + # puts res.body + # + # == How to use Net::HTTP + # + # The following example code can be used as the basis of a HTTP user-agent + # which can perform a variety of request types using persistent + # connections. + # + # uri = URI('http://example.com/some_path?query=string') + # + # Net::HTTP.start(uri.host, uri.port) do |http| + # request = Net::HTTP::Get.new uri + # + # response = http.request request # Net::HTTPResponse object + # end + # + # Net::HTTP::start immediately creates a connection to an HTTP server which + # is kept open for the duration of the block. The connection will remain + # open for multiple requests in the block if the server indicates it + # supports persistent connections. + # + # The request types Net::HTTP supports are listed below in the section "HTTP + # Request Classes". + # + # If you wish to re-use a connection across multiple HTTP requests without + # automatically closing it you can use ::new instead of ::start. #request + # will automatically open a connection to the server if one is not currently + # open. You can manually close the connection with #finish. + # + # For all the Net::HTTP request objects and shortcut request methods you may + # supply either a String for the request path or a URI from which Net::HTTP + # will extract the request path. + # + # === Response Data + # + # uri = URI('http://example.com/index.html') + # res = Net::HTTP.get_response(uri) + # + # # Headers + # res['Set-Cookie'] # => String + # res.get_fields('set-cookie') # => Array + # res.to_hash['set-cookie'] # => Array + # puts "Headers: #{res.to_hash.inspect}" + # + # # Status + # puts res.code # => '200' + # puts res.message # => 'OK' + # puts res.class.name # => 'HTTPOK' + # + # # Body + # puts res.body if res.response_body_permitted? + # + # === Following Redirection + # + # Each Net::HTTPResponse object belongs to a class for its response code. + # + # For example, all 2XX responses are instances of a Net::HTTPSuccess + # subclass, a 3XX response is an instance of a Net::HTTPRedirection + # subclass and a 200 response is an instance of the Net::HTTPOK class. For + # details of response classes, see the section "HTTP Response Classes" + # below. + # + # Using a case statement you can handle various types of responses properly: + # + # def fetch(uri_str, limit = 10) + # # You should choose a better exception. + # raise ArgumentError, 'too many HTTP redirects' if limit == 0 + # + # response = Net::HTTP.get_response(URI(uri_str)) + # + # case response + # when Net::HTTPSuccess then + # response + # when Net::HTTPRedirection then + # location = response['location'] + # warn "redirected to #{location}" + # fetch(location, limit - 1) + # else + # response.value + # end + # end + # + # print fetch('http://www.ruby-lang.org') + # + # === POST + # + # A POST can be made using the Net::HTTP::Post request class. This example + # creates a urlencoded POST body: + # + # uri = URI('http://www.example.com/todo.cgi') + # req = Net::HTTP::Post.new(uri) + # req.set_form_data('from' => '2005-01-01', 'to' => '2005-03-31') + # + # res = Net::HTTP.start(uri.hostname, uri.port) do |http| + # http.request(req) + # end + # + # case res + # when Net::HTTPSuccess, Net::HTTPRedirection + # # OK + # else + # res.value + # end + # + # At this time Net::HTTP does not support multipart/form-data. To send + # multipart/form-data use Net::HTTPRequest#body= and + # Net::HTTPRequest#content_type=: + # + # req = Net::HTTP::Post.new(uri) + # req.body = multipart_data + # req.content_type = 'multipart/form-data' + # + # Other requests that can contain a body such as PUT can be created in the + # same way using the corresponding request class (Net::HTTP::Put). + # + # === Setting Headers + # + # The following example performs a conditional GET using the + # If-Modified-Since header. If the files has not been modified since the + # time in the header a Not Modified response will be returned. See RFC 2616 + # section 9.3 for further details. + # + # uri = URI('http://example.com/cached_response') + # file = File.stat 'cached_response' + # + # req = Net::HTTP::Get.new(uri) + # req['If-Modified-Since'] = file.mtime.rfc2822 + # + # res = Net::HTTP.start(uri.hostname, uri.port) {|http| + # http.request(req) + # } + # + # open 'cached_response', 'w' do |io| + # io.write res.body + # end if res.is_a?(Net::HTTPSuccess) + # + # === Basic Authentication + # + # Basic authentication is performed according to + # [RFC2617](http://www.ietf.org/rfc/rfc2617.txt) + # + # uri = URI('http://example.com/index.html?key=value') + # + # req = Net::HTTP::Get.new(uri) + # req.basic_auth 'user', 'pass' + # + # res = Net::HTTP.start(uri.hostname, uri.port) {|http| + # http.request(req) + # } + # puts res.body + # + # === Streaming Response Bodies + # + # By default Net::HTTP reads an entire response into memory. If you are + # handling large files or wish to implement a progress bar you can instead + # stream the body directly to an IO. + # + # uri = URI('http://example.com/large_file') + # + # Net::HTTP.start(uri.host, uri.port) do |http| + # request = Net::HTTP::Get.new uri + # + # http.request request do |response| + # open 'large_file', 'w' do |io| + # response.read_body do |chunk| + # io.write chunk + # end + # end + # end + # end + # + # === HTTPS + # + # HTTPS is enabled for an HTTP connection by Net::HTTP#use_ssl=. + # + # uri = URI('https://secure.example.com/some_path?query=string') + # + # Net::HTTP.start(uri.host, uri.port, + # :use_ssl => uri.scheme == 'https') do |http| + # request = Net::HTTP::Get.new uri + # + # response = http.request request # Net::HTTPResponse object + # end + # + # In previous versions of Ruby you would need to require 'net/https' to use + # HTTPS. This is no longer true. + # + # === Proxies + # + # Net::HTTP will automatically create a proxy from the +http_proxy+ + # environment variable if it is present. To disable use of +http_proxy+, + # pass +nil+ for the proxy address. + # + # You may also create a custom proxy: + # + # proxy_addr = 'your.proxy.host' + # proxy_port = 8080 + # + # Net::HTTP.new('example.com', nil, proxy_addr, proxy_port).start { |http| + # # always proxy via your.proxy.addr:8080 + # } + # + # See Net::HTTP.new for further details and examples such as proxies that + # require a username and password. + # + # === Compression + # + # Net::HTTP automatically adds Accept-Encoding for compression of response + # bodies and automatically decompresses gzip and deflate responses unless a + # Range header was sent. + # + # Compression can be disabled through the Accept-Encoding: identity header. + # + # == HTTP Request Classes + # + # Here is the HTTP request class hierarchy. + # + # * Net::HTTPRequest + # * Net::HTTP::Get + # * Net::HTTP::Head + # * Net::HTTP::Post + # * Net::HTTP::Patch + # * Net::HTTP::Put + # * Net::HTTP::Proppatch + # * Net::HTTP::Lock + # * Net::HTTP::Unlock + # * Net::HTTP::Options + # * Net::HTTP::Propfind + # * Net::HTTP::Delete + # * Net::HTTP::Move + # * Net::HTTP::Copy + # * Net::HTTP::Mkcol + # * Net::HTTP::Trace + # + # == HTTP Response Classes + # + # Here is HTTP response class hierarchy. All classes are defined in Net + # module and are subclasses of Net::HTTPResponse. + # + # HTTPUnknownResponse:: For unhandled HTTP extensions + # HTTPInformation:: 1xx + # HTTPContinue:: 100 + # HTTPSwitchProtocol:: 101 + # HTTPSuccess:: 2xx + # HTTPOK:: 200 + # HTTPCreated:: 201 + # HTTPAccepted:: 202 + # HTTPNonAuthoritativeInformation:: 203 + # HTTPNoContent:: 204 + # HTTPResetContent:: 205 + # HTTPPartialContent:: 206 + # HTTPMultiStatus:: 207 + # HTTPIMUsed:: 226 + # HTTPRedirection:: 3xx + # HTTPMultipleChoices:: 300 + # HTTPMovedPermanently:: 301 + # HTTPFound:: 302 + # HTTPSeeOther:: 303 + # HTTPNotModified:: 304 + # HTTPUseProxy:: 305 + # HTTPTemporaryRedirect:: 307 + # HTTPClientError:: 4xx + # HTTPBadRequest:: 400 + # HTTPUnauthorized:: 401 + # HTTPPaymentRequired:: 402 + # HTTPForbidden:: 403 + # HTTPNotFound:: 404 + # HTTPMethodNotAllowed:: 405 + # HTTPNotAcceptable:: 406 + # HTTPProxyAuthenticationRequired:: 407 + # HTTPRequestTimeOut:: 408 + # HTTPConflict:: 409 + # HTTPGone:: 410 + # HTTPLengthRequired:: 411 + # HTTPPreconditionFailed:: 412 + # HTTPRequestEntityTooLarge:: 413 + # HTTPRequestURITooLong:: 414 + # HTTPUnsupportedMediaType:: 415 + # HTTPRequestedRangeNotSatisfiable:: 416 + # HTTPExpectationFailed:: 417 + # HTTPUnprocessableEntity:: 422 + # HTTPLocked:: 423 + # HTTPFailedDependency:: 424 + # HTTPUpgradeRequired:: 426 + # HTTPPreconditionRequired:: 428 + # HTTPTooManyRequests:: 429 + # HTTPRequestHeaderFieldsTooLarge:: 431 + # HTTPServerError:: 5xx + # HTTPInternalServerError:: 500 + # HTTPNotImplemented:: 501 + # HTTPBadGateway:: 502 + # HTTPServiceUnavailable:: 503 + # HTTPGatewayTimeOut:: 504 + # HTTPVersionNotSupported:: 505 + # HTTPInsufficientStorage:: 507 + # HTTPNetworkAuthenticationRequired:: 511 + # + # There is also the Net::HTTPBadResponse exception which is raised when + # there is a protocol error. + # + class HTTP < Protocol + + # :stopdoc: + Revision = %q$Revision: 44079 $.split[1] + HTTPVersion = '1.1' + begin + require 'zlib' + require 'stringio' #for our purposes (unpacking gzip) lump these together + HAVE_ZLIB=true + rescue LoadError + HAVE_ZLIB=false + end + # :startdoc: + + # Turns on net/http 1.2 (Ruby 1.8) features. + # Defaults to ON in Ruby 1.8 or later. + def HTTP.version_1_2 + true + end + + # Returns true if net/http is in version 1.2 mode. + # Defaults to true. + def HTTP.version_1_2? + true + end + + def HTTP.version_1_1? #:nodoc: + false + end + + class << HTTP + alias is_version_1_1? version_1_1? #:nodoc: + alias is_version_1_2? version_1_2? #:nodoc: + end + + # + # short cut methods + # + + # + # Gets the body text from the target and outputs it to $stdout. The + # target can either be specified as + # (+uri+), or as (+host+, +path+, +port+ = 80); so: + # + # Net::HTTP.get_print URI('http://www.example.com/index.html') + # + # or: + # + # Net::HTTP.get_print 'www.example.com', '/index.html' + # + def HTTP.get_print(uri_or_host, path = nil, port = nil) + get_response(uri_or_host, path, port) {|res| + res.read_body do |chunk| + $stdout.print chunk + end + } + nil + end + + # Sends a GET request to the target and returns the HTTP response + # as a string. The target can either be specified as + # (+uri+), or as (+host+, +path+, +port+ = 80); so: + # + # print Net::HTTP.get(URI('http://www.example.com/index.html')) + # + # or: + # + # print Net::HTTP.get('www.example.com', '/index.html') + # + def HTTP.get(uri_or_host, path = nil, port = nil) + get_response(uri_or_host, path, port).body + end + + # Sends a GET request to the target and returns the HTTP response + # as a Net::HTTPResponse object. The target can either be specified as + # (+uri+), or as (+host+, +path+, +port+ = 80); so: + # + # res = Net::HTTP.get_response(URI('http://www.example.com/index.html')) + # print res.body + # + # or: + # + # res = Net::HTTP.get_response('www.example.com', '/index.html') + # print res.body + # + def HTTP.get_response(uri_or_host, path = nil, port = nil, &block) + if path + host = uri_or_host + new(host, port || HTTP.default_port).start {|http| + return http.request_get(path, &block) + } + else + uri = uri_or_host + start(uri.hostname, uri.port, + :use_ssl => uri.scheme == 'https') {|http| + return http.request_get(uri, &block) + } + end + end + + # Posts HTML form data to the specified URI object. + # The form data must be provided as a Hash mapping from String to String. + # Example: + # + # { "cmd" => "search", "q" => "ruby", "max" => "50" } + # + # This method also does Basic Authentication iff +url+.user exists. + # But userinfo for authentication is deprecated (RFC3986). + # So this feature will be removed. + # + # Example: + # + # require 'net/http' + # require 'uri' + # + # Net::HTTP.post_form URI('http://www.example.com/search.cgi'), + # { "q" => "ruby", "max" => "50" } + # + def HTTP.post_form(url, params) + req = Post.new(url) + req.form_data = params + req.basic_auth url.user, url.password if url.user + start(url.hostname, url.port, + :use_ssl => url.scheme == 'https' ) {|http| + http.request(req) + } + end + + # + # HTTP session management + # + + # The default port to use for HTTP requests; defaults to 80. + def HTTP.default_port + http_default_port() + end + + # The default port to use for HTTP requests; defaults to 80. + def HTTP.http_default_port + 80 + end + + # The default port to use for HTTPS requests; defaults to 443. + def HTTP.https_default_port + 443 + end + + def HTTP.socket_type #:nodoc: obsolete + BufferedIO + end + + # :call-seq: + # HTTP.start(address, port, p_addr, p_port, p_user, p_pass, &block) + # HTTP.start(address, port=nil, p_addr=nil, p_port=nil, p_user=nil, p_pass=nil, opt, &block) + # + # Creates a new Net::HTTP object, then additionally opens the TCP + # connection and HTTP session. + # + # Arguments are the following: + # _address_ :: hostname or IP address of the server + # _port_ :: port of the server + # _p_addr_ :: address of proxy + # _p_port_ :: port of proxy + # _p_user_ :: user of proxy + # _p_pass_ :: pass of proxy + # _opt_ :: optional hash + # + # _opt_ sets following values by its accessor. + # The keys are ca_file, ca_path, cert, cert_store, ciphers, + # close_on_empty_response, key, open_timeout, read_timeout, ssl_timeout, + # ssl_version, use_ssl, verify_callback, verify_depth and verify_mode. + # If you set :use_ssl as true, you can use https and default value of + # verify_mode is set as OpenSSL::SSL::VERIFY_PEER. + # + # If the optional block is given, the newly + # created Net::HTTP object is passed to it and closed when the + # block finishes. In this case, the return value of this method + # is the return value of the block. If no block is given, the + # return value of this method is the newly created Net::HTTP object + # itself, and the caller is responsible for closing it upon completion + # using the finish() method. + def HTTP.start(address, *arg, &block) # :yield: +http+ + arg.pop if opt = Hash.try_convert(arg[-1]) + port, p_addr, p_port, p_user, p_pass = *arg + port = https_default_port if !port && opt && opt[:use_ssl] + http = new(address, port, p_addr, p_port, p_user, p_pass) + + if opt + if opt[:use_ssl] + opt = {verify_mode: OpenSSL::SSL::VERIFY_PEER}.update(opt) + end + http.methods.grep(/\A(\w+)=\z/) do |meth| + key = $1.to_sym + opt.key?(key) or next + http.__send__(meth, opt[key]) + end + end + + http.start(&block) + end + + class << HTTP + alias newobj new # :nodoc: + end + + # Creates a new Net::HTTP object without opening a TCP connection or + # HTTP session. + # + # The +address+ should be a DNS hostname or IP address, the +port+ is the + # port the server operates on. If no +port+ is given the default port for + # HTTP or HTTPS is used. + # + # If none of the +p_+ arguments are given, the proxy host and port are + # taken from the +http_proxy+ environment variable (or its uppercase + # equivalent) if present. If the proxy requires authentication you must + # supply it by hand. See URI::Generic#find_proxy for details of proxy + # detection from the environment. To disable proxy detection set +p_addr+ + # to nil. + # + # If you are connecting to a custom proxy, +p_addr+ the DNS name or IP + # address of the proxy host, +p_port+ the port to use to access the proxy, + # and +p_user+ and +p_pass+ the username and password if authorization is + # required to use the proxy. + # + def HTTP.new(address, port = nil, p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) + http = super address, port + + if proxy_class? then # from Net::HTTP::Proxy() + http.proxy_from_env = @proxy_from_env + http.proxy_address = @proxy_address + http.proxy_port = @proxy_port + http.proxy_user = @proxy_user + http.proxy_pass = @proxy_pass + elsif p_addr == :ENV then + http.proxy_from_env = true + else + http.proxy_address = p_addr + http.proxy_port = p_port || default_port + http.proxy_user = p_user + http.proxy_pass = p_pass + end + + http + end + + # Creates a new Net::HTTP object for the specified server address, + # without opening the TCP connection or initializing the HTTP session. + # The +address+ should be a DNS hostname or IP address. + def initialize(address, port = nil) + @address = address + @port = (port || HTTP.default_port) + @local_host = nil + @local_port = nil + @curr_http_version = HTTPVersion + @keep_alive_timeout = 2 + @last_communicated = nil + @close_on_empty_response = false + @socket = nil + @started = false + @open_timeout = nil + @read_timeout = 60 + @continue_timeout = nil + @debug_output = nil + + @proxy_from_env = false + @proxy_uri = nil + @proxy_address = nil + @proxy_port = nil + @proxy_user = nil + @proxy_pass = nil + + @use_ssl = false + @ssl_context = nil + @ssl_session = nil + @enable_post_connection_check = true + @sspi_enabled = false + SSL_IVNAMES.each do |ivname| + instance_variable_set ivname, nil + end + end + + def inspect + "#<#{self.class} #{@address}:#{@port} open=#{started?}>" + end + + # *WARNING* This method opens a serious security hole. + # Never use this method in production code. + # + # Sets an output stream for debugging. + # + # http = Net::HTTP.new(hostname) + # http.set_debug_output $stderr + # http.start { .... } + # + def set_debug_output(output) + warn 'Net::HTTP#set_debug_output called after HTTP started' if started? + @debug_output = output + end + + # The DNS host name or IP address to connect to. + attr_reader :address + + # The port number to connect to. + attr_reader :port + + # The local host used to estabilish the connection. + attr_accessor :local_host + + # The local port used to estabilish the connection. + attr_accessor :local_port + + attr_writer :proxy_from_env + attr_writer :proxy_address + attr_writer :proxy_port + attr_writer :proxy_user + attr_writer :proxy_pass + + # Number of seconds to wait for the connection to open. Any number + # may be used, including Floats for fractional seconds. If the HTTP + # object cannot open a connection in this many seconds, it raises a + # Net::OpenTimeout exception. The default value is +nil+. + attr_accessor :open_timeout + + # Number of seconds to wait for one block to be read (via one read(2) + # call). Any number may be used, including Floats for fractional + # seconds. If the HTTP object cannot read data in this many seconds, + # it raises a Net::ReadTimeout exception. The default value is 60 seconds. + attr_reader :read_timeout + + # Setter for the read_timeout attribute. + def read_timeout=(sec) + @socket.read_timeout = sec if @socket + @read_timeout = sec + end + + # Seconds to wait for 100 Continue response. If the HTTP object does not + # receive a response in this many seconds it sends the request body. The + # default value is +nil+. + attr_reader :continue_timeout + + # Setter for the continue_timeout attribute. + def continue_timeout=(sec) + @socket.continue_timeout = sec if @socket + @continue_timeout = sec + end + + # Seconds to reuse the connection of the previous request. + # If the idle time is less than this Keep-Alive Timeout, + # Net::HTTP reuses the TCP/IP socket used by the previous communication. + # The default value is 2 seconds. + attr_accessor :keep_alive_timeout + + # Returns true if the HTTP session has been started. + def started? + @started + end + + alias active? started? #:nodoc: obsolete + + attr_accessor :close_on_empty_response + + # Returns true if SSL/TLS is being used with HTTP. + def use_ssl? + @use_ssl + end + + # Turn on/off SSL. + # This flag must be set before starting session. + # If you change use_ssl value after session started, + # a Net::HTTP object raises IOError. + def use_ssl=(flag) + flag = flag ? true : false + if started? and @use_ssl != flag + raise IOError, "use_ssl value changed, but session already started" + end + @use_ssl = flag + end + + SSL_IVNAMES = [ + :@ca_file, + :@ca_path, + :@cert, + :@cert_store, + :@ciphers, + :@key, + :@ssl_timeout, + :@ssl_version, + :@verify_callback, + :@verify_depth, + :@verify_mode, + ] + SSL_ATTRIBUTES = [ + :ca_file, + :ca_path, + :cert, + :cert_store, + :ciphers, + :key, + :ssl_timeout, + :ssl_version, + :verify_callback, + :verify_depth, + :verify_mode, + ] + + # Sets path of a CA certification file in PEM format. + # + # The file can contain several CA certificates. + attr_accessor :ca_file + + # Sets path of a CA certification directory containing certifications in + # PEM format. + attr_accessor :ca_path + + # Sets an OpenSSL::X509::Certificate object as client certificate. + # (This method is appeared in Michal Rokos's OpenSSL extension). + attr_accessor :cert + + # Sets the X509::Store to verify peer certificate. + attr_accessor :cert_store + + # Sets the available ciphers. See OpenSSL::SSL::SSLContext#ciphers= + attr_accessor :ciphers + + # Sets an OpenSSL::PKey::RSA or OpenSSL::PKey::DSA object. + # (This method is appeared in Michal Rokos's OpenSSL extension.) + attr_accessor :key + + # Sets the SSL timeout seconds. + attr_accessor :ssl_timeout + + # Sets the SSL version. See OpenSSL::SSL::SSLContext#ssl_version= + attr_accessor :ssl_version + + # Sets the verify callback for the server certification verification. + attr_accessor :verify_callback + + # Sets the maximum depth for the certificate chain verification. + attr_accessor :verify_depth + + # Sets the flags for server the certification verification at beginning of + # SSL/TLS session. + # + # OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER are acceptable. + attr_accessor :verify_mode + + # Returns the X.509 certificates the server presented. + def peer_cert + if not use_ssl? or not @socket + return nil + end + @socket.io.peer_cert + end + + # Opens a TCP connection and HTTP session. + # + # When this method is called with a block, it passes the Net::HTTP + # object to the block, and closes the TCP connection and HTTP session + # after the block has been executed. + # + # When called with a block, it returns the return value of the + # block; otherwise, it returns self. + # + def start # :yield: http + raise IOError, 'HTTP session already opened' if @started + if block_given? + begin + do_start + return yield(self) + ensure + do_finish + end + end + do_start + self + end + + def do_start + connect + @started = true + end + private :do_start + + def connect + if proxy? then + conn_address = proxy_address + conn_port = proxy_port + else + conn_address = address + conn_port = port + end + + D "opening connection to #{conn_address}:#{conn_port}..." + s = Timeout.timeout(@open_timeout, Net::OpenTimeout) { + TCPSocket.open(conn_address, conn_port, @local_host, @local_port) + } + s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) + D "opened" + if use_ssl? + ssl_parameters = Hash.new + iv_list = instance_variables + SSL_IVNAMES.each_with_index do |ivname, i| + if iv_list.include?(ivname) and + value = instance_variable_get(ivname) + ssl_parameters[SSL_ATTRIBUTES[i]] = value if value + end + end + @ssl_context = OpenSSL::SSL::SSLContext.new + @ssl_context.set_params(ssl_parameters) + D "starting SSL for #{conn_address}:#{conn_port}..." + s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) + s.sync_close = true + D "SSL established" + end + @socket = BufferedIO.new(s) + @socket.read_timeout = @read_timeout + @socket.continue_timeout = @continue_timeout + @socket.debug_output = @debug_output + if use_ssl? + begin + if proxy? + buf = "CONNECT #{@address}:#{@port} HTTP/#{HTTPVersion}\r\n" + buf << "Host: #{@address}:#{@port}\r\n" + if proxy_user + credential = ["#{proxy_user}:#{proxy_pass}"].pack('m') + credential.delete!("\r\n") + buf << "Proxy-Authorization: Basic #{credential}\r\n" + end + buf << "\r\n" + @socket.write(buf) + HTTPResponse.read_new(@socket).value + end + s.session = @ssl_session if @ssl_session + # Server Name Indication (SNI) RFC 3546 + s.hostname = @address if s.respond_to? :hostname= + Timeout.timeout(@open_timeout, Net::OpenTimeout) { s.connect } + if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE + s.post_connection_check(@address) + end + @ssl_session = s.session + rescue => exception + D "Conn close because of connect error #{exception}" + @socket.close if @socket and not @socket.closed? + raise exception + end + end + on_connect + end + private :connect + + def on_connect + end + private :on_connect + + # Finishes the HTTP session and closes the TCP connection. + # Raises IOError if the session has not been started. + def finish + raise IOError, 'HTTP session not yet started' unless started? + do_finish + end + + def do_finish + @started = false + @socket.close if @socket and not @socket.closed? + @socket = nil + end + private :do_finish + + # + # proxy + # + + public + + # no proxy + @is_proxy_class = false + @proxy_from_env = false + @proxy_addr = nil + @proxy_port = nil + @proxy_user = nil + @proxy_pass = nil + + # Creates an HTTP proxy class which behaves like Net::HTTP, but + # performs all access via the specified proxy. + # + # This class is obsolete. You may pass these same parameters directly to + # Net::HTTP.new. See Net::HTTP.new for details of the arguments. + def HTTP.Proxy(p_addr = :ENV, p_port = nil, p_user = nil, p_pass = nil) + return self unless p_addr + + Class.new(self) { + @is_proxy_class = true + + if p_addr == :ENV then + @proxy_from_env = true + @proxy_address = nil + @proxy_port = nil + else + @proxy_from_env = false + @proxy_address = p_addr + @proxy_port = p_port || default_port + end + + @proxy_user = p_user + @proxy_pass = p_pass + } + end + + class << HTTP + # returns true if self is a class which was created by HTTP::Proxy. + def proxy_class? + defined?(@is_proxy_class) ? @is_proxy_class : false + end + + # Address of proxy host. If Net::HTTP does not use a proxy, nil. + attr_reader :proxy_address + + # Port number of proxy host. If Net::HTTP does not use a proxy, nil. + attr_reader :proxy_port + + # User name for accessing proxy. If Net::HTTP does not use a proxy, nil. + attr_reader :proxy_user + + # User password for accessing proxy. If Net::HTTP does not use a proxy, + # nil. + attr_reader :proxy_pass + end + + # True if requests for this connection will be proxied + def proxy? + !!if @proxy_from_env then + proxy_uri + else + @proxy_address + end + end + + # True if the proxy for this connection is determined from the environment + def proxy_from_env? + @proxy_from_env + end + + # The proxy URI determined from the environment for this connection. + def proxy_uri # :nodoc: + @proxy_uri ||= URI("http://#{address}:#{port}").find_proxy + end + + # The address of the proxy server, if one is configured. + def proxy_address + if @proxy_from_env then + proxy_uri && proxy_uri.hostname + else + @proxy_address + end + end + + # The port of the proxy server, if one is configured. + def proxy_port + if @proxy_from_env then + proxy_uri && proxy_uri.port + else + @proxy_port + end + end + + # The proxy username, if one is configured + def proxy_user + @proxy_user + end + + # The proxy password, if one is configured + def proxy_pass + @proxy_pass + end + + alias proxyaddr proxy_address #:nodoc: obsolete + alias proxyport proxy_port #:nodoc: obsolete + + private + + # without proxy, obsolete + + def conn_address # :nodoc: + address() + end + + def conn_port # :nodoc: + port() + end + + def edit_path(path) + if proxy? and not use_ssl? then + "http://#{addr_port}#{path}" + else + path + end + end + + # + # HTTP operations + # + + public + + # Retrieves data from +path+ on the connected-to host which may be an + # absolute path String or a URI to extract the path from. + # + # +initheader+ must be a Hash like { 'Accept' => '*/*', ... }, + # and it defaults to an empty hash. + # If +initheader+ doesn't have the key 'accept-encoding', then + # a value of "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" is used, + # so that gzip compression is used in preference to deflate + # compression, which is used in preference to no compression. + # Ruby doesn't have libraries to support the compress (Lempel-Ziv) + # compression, so that is not supported. The intent of this is + # to reduce bandwidth by default. If this routine sets up + # compression, then it does the decompression also, removing + # the header as well to prevent confusion. Otherwise + # it leaves the body as it found it. + # + # This method returns a Net::HTTPResponse object. + # + # If called with a block, yields each fragment of the + # entity body in turn as a string as it is read from + # the socket. Note that in this case, the returned response + # object will *not* contain a (meaningful) body. + # + # +dest+ argument is obsolete. + # It still works but you must not use it. + # + # This method never raises an exception. + # + # response = http.get('/index.html') + # + # # using block + # File.open('result.txt', 'w') {|f| + # http.get('/~foo/') do |str| + # f.write str + # end + # } + # + def get(path, initheader = {}, dest = nil, &block) # :yield: +body_segment+ + res = nil + request(Get.new(path, initheader)) {|r| + r.read_body dest, &block + res = r + } + res + end + + # Gets only the header from +path+ on the connected-to host. + # +header+ is a Hash like { 'Accept' => '*/*', ... }. + # + # This method returns a Net::HTTPResponse object. + # + # This method never raises an exception. + # + # response = nil + # Net::HTTP.start('some.www.server', 80) {|http| + # response = http.head('/index.html') + # } + # p response['content-type'] + # + def head(path, initheader = nil) + request(Head.new(path, initheader)) + end + + # Posts +data+ (must be a String) to +path+. +header+ must be a Hash + # like { 'Accept' => '*/*', ... }. + # + # This method returns a Net::HTTPResponse object. + # + # If called with a block, yields each fragment of the + # entity body in turn as a string as it is read from + # the socket. Note that in this case, the returned response + # object will *not* contain a (meaningful) body. + # + # +dest+ argument is obsolete. + # It still works but you must not use it. + # + # This method never raises exception. + # + # response = http.post('/cgi-bin/search.rb', 'query=foo') + # + # # using block + # File.open('result.txt', 'w') {|f| + # http.post('/cgi-bin/search.rb', 'query=foo') do |str| + # f.write str + # end + # } + # + # You should set Content-Type: header field for POST. + # If no Content-Type: field given, this method uses + # "application/x-www-form-urlencoded" by default. + # + def post(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+ + send_entity(path, data, initheader, dest, Post, &block) + end + + # Sends a PATCH request to the +path+ and gets a response, + # as an HTTPResponse object. + def patch(path, data, initheader = nil, dest = nil, &block) # :yield: +body_segment+ + send_entity(path, data, initheader, dest, Patch, &block) + end + + def put(path, data, initheader = nil) #:nodoc: + request(Put.new(path, initheader), data) + end + + # Sends a PROPPATCH request to the +path+ and gets a response, + # as an HTTPResponse object. + def proppatch(path, body, initheader = nil) + request(Proppatch.new(path, initheader), body) + end + + # Sends a LOCK request to the +path+ and gets a response, + # as an HTTPResponse object. + def lock(path, body, initheader = nil) + request(Lock.new(path, initheader), body) + end + + # Sends a UNLOCK request to the +path+ and gets a response, + # as an HTTPResponse object. + def unlock(path, body, initheader = nil) + request(Unlock.new(path, initheader), body) + end + + # Sends a OPTIONS request to the +path+ and gets a response, + # as an HTTPResponse object. + def options(path, initheader = nil) + request(Options.new(path, initheader)) + end + + # Sends a PROPFIND request to the +path+ and gets a response, + # as an HTTPResponse object. + def propfind(path, body = nil, initheader = {'Depth' => '0'}) + request(Propfind.new(path, initheader), body) + end + + # Sends a DELETE request to the +path+ and gets a response, + # as an HTTPResponse object. + def delete(path, initheader = {'Depth' => 'Infinity'}) + request(Delete.new(path, initheader)) + end + + # Sends a MOVE request to the +path+ and gets a response, + # as an HTTPResponse object. + def move(path, initheader = nil) + request(Move.new(path, initheader)) + end + + # Sends a COPY request to the +path+ and gets a response, + # as an HTTPResponse object. + def copy(path, initheader = nil) + request(Copy.new(path, initheader)) + end + + # Sends a MKCOL request to the +path+ and gets a response, + # as an HTTPResponse object. + def mkcol(path, body = nil, initheader = nil) + request(Mkcol.new(path, initheader), body) + end + + # Sends a TRACE request to the +path+ and gets a response, + # as an HTTPResponse object. + def trace(path, initheader = nil) + request(Trace.new(path, initheader)) + end + + # Sends a GET request to the +path+. + # Returns the response as a Net::HTTPResponse object. + # + # When called with a block, passes an HTTPResponse object to the block. + # The body of the response will not have been read yet; + # the block can process it using HTTPResponse#read_body, + # if desired. + # + # Returns the response. + # + # This method never raises Net::* exceptions. + # + # response = http.request_get('/index.html') + # # The entity body is already read in this case. + # p response['content-type'] + # puts response.body + # + # # Using a block + # http.request_get('/index.html') {|response| + # p response['content-type'] + # response.read_body do |str| # read body now + # print str + # end + # } + # + def request_get(path, initheader = nil, &block) # :yield: +response+ + request(Get.new(path, initheader), &block) + end + + # Sends a HEAD request to the +path+ and returns the response + # as a Net::HTTPResponse object. + # + # Returns the response. + # + # This method never raises Net::* exceptions. + # + # response = http.request_head('/index.html') + # p response['content-type'] + # + def request_head(path, initheader = nil, &block) + request(Head.new(path, initheader), &block) + end + + # Sends a POST request to the +path+. + # + # Returns the response as a Net::HTTPResponse object. + # + # When called with a block, the block is passed an HTTPResponse + # object. The body of that response will not have been read yet; + # the block can process it using HTTPResponse#read_body, if desired. + # + # Returns the response. + # + # This method never raises Net::* exceptions. + # + # # example + # response = http.request_post('/cgi-bin/nice.rb', 'datadatadata...') + # p response.status + # puts response.body # body is already read in this case + # + # # using block + # http.request_post('/cgi-bin/nice.rb', 'datadatadata...') {|response| + # p response.status + # p response['content-type'] + # response.read_body do |str| # read body now + # print str + # end + # } + # + def request_post(path, data, initheader = nil, &block) # :yield: +response+ + request Post.new(path, initheader), data, &block + end + + def request_put(path, data, initheader = nil, &block) #:nodoc: + request Put.new(path, initheader), data, &block + end + + alias get2 request_get #:nodoc: obsolete + alias head2 request_head #:nodoc: obsolete + alias post2 request_post #:nodoc: obsolete + alias put2 request_put #:nodoc: obsolete + + + # Sends an HTTP request to the HTTP server. + # Also sends a DATA string if +data+ is given. + # + # Returns a Net::HTTPResponse object. + # + # This method never raises Net::* exceptions. + # + # response = http.send_request('GET', '/index.html') + # puts response.body + # + def send_request(name, path, data = nil, header = nil) + r = HTTPGenericRequest.new(name,(data ? true : false),true,path,header) + request r, data + end + + # Sends an HTTPRequest object +req+ to the HTTP server. + # + # If +req+ is a Net::HTTP::Post or Net::HTTP::Put request containing + # data, the data is also sent. Providing data for a Net::HTTP::Head or + # Net::HTTP::Get request results in an ArgumentError. + # + # Returns an HTTPResponse object. + # + # When called with a block, passes an HTTPResponse object to the block. + # The body of the response will not have been read yet; + # the block can process it using HTTPResponse#read_body, + # if desired. + # + # This method never raises Net::* exceptions. + # + def request(req, body = nil, &block) # :yield: +response+ + unless started? + start { + req['connection'] ||= 'close' + return request(req, body, &block) + } + end + if proxy_user() + req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl? + end + req.set_body_internal body + res = transport_request(req, &block) + if sspi_auth?(res) + sspi_auth(req) + res = transport_request(req, &block) + end + res + end + + private + + # Executes a request which uses a representation + # and returns its body. + def send_entity(path, data, initheader, dest, type, &block) + res = nil + request(type.new(path, initheader), data) {|r| + r.read_body dest, &block + res = r + } + res + end + + IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/ # :nodoc: + + def transport_request(req) + count = 0 + begin + begin_transport req + res = catch(:response) { + req.exec @socket, @curr_http_version, edit_path(req.path) + begin + res = HTTPResponse.read_new(@socket) + res.decode_content = req.decode_content + end while res.kind_of?(HTTPContinue) + + res.uri = req.uri + + res.reading_body(@socket, req.response_body_permitted?) { + yield res if block_given? + } + res + } + rescue Net::OpenTimeout + raise + rescue Net::ReadTimeout, IOError, EOFError, + Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE, + # avoid a dependency on OpenSSL + defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError, + Timeout::Error => exception + if count == 0 && IDEMPOTENT_METHODS_.include?(req.method) + count += 1 + @socket.close if @socket and not @socket.closed? + D "Conn close because of error #{exception}, and retry" + retry + end + D "Conn close because of error #{exception}" + @socket.close if @socket and not @socket.closed? + raise + end + + end_transport req, res + res + rescue => exception + D "Conn close because of error #{exception}" + @socket.close if @socket and not @socket.closed? + raise exception + end + + def begin_transport(req) + if @socket.closed? + connect + elsif @last_communicated && @last_communicated + @keep_alive_timeout < Time.now + D 'Conn close because of keep_alive_timeout' + @socket.close + connect + end + + if not req.response_body_permitted? and @close_on_empty_response + req['connection'] ||= 'close' + end + + host = req['host'] || address + host = $1 if host =~ /(.*):\d+$/ + req.update_uri host, port, use_ssl? + + req['host'] ||= addr_port() + end + + def end_transport(req, res) + @curr_http_version = res.http_version + @last_communicated = nil + if @socket.closed? + D 'Conn socket closed' + elsif not res.body and @close_on_empty_response + D 'Conn close' + @socket.close + elsif keep_alive?(req, res) + D 'Conn keep-alive' + @last_communicated = Time.now + else + D 'Conn close' + @socket.close + end + end + + def keep_alive?(req, res) + return false if req.connection_close? + if @curr_http_version <= '1.0' + res.connection_keep_alive? + else # HTTP/1.1 or later + not res.connection_close? + end + end + + def sspi_auth?(res) + return false unless @sspi_enabled + if res.kind_of?(HTTPProxyAuthenticationRequired) and + proxy? and res["Proxy-Authenticate"].include?("Negotiate") + begin + require 'win32/sspi' + true + rescue LoadError + false + end + else + false + end + end + + def sspi_auth(req) + n = Win32::SSPI::NegotiateAuth.new + req["Proxy-Authorization"] = "Negotiate #{n.get_initial_token}" + # Some versions of ISA will close the connection if this isn't present. + req["Connection"] = "Keep-Alive" + req["Proxy-Connection"] = "Keep-Alive" + res = transport_request(req) + authphrase = res["Proxy-Authenticate"] or return res + req["Proxy-Authorization"] = "Negotiate #{n.complete_authentication(authphrase)}" + rescue => err + raise HTTPAuthenticationError.new('HTTP authentication failed', err) + end + + # + # utils + # + + private + + def addr_port + if use_ssl? + address() + (port == HTTP.https_default_port ? '' : ":#{port()}") + else + address() + (port == HTTP.http_default_port ? '' : ":#{port()}") + end + end + + def D(msg) + return unless @debug_output + @debug_output << msg + @debug_output << "\n" + end + end + +end + +require 'net/http/exceptions' + +require 'net/http/header' + +require 'net/http/generic_request' +require 'net/http/request' +require 'net/http/requests' + +require 'net/http/response' +require 'net/http/responses' + +require 'net/http/proxy_delta' + +require 'net/http/backward' + diff --git a/ruby/lib/ruby/2.1.0/net/http/backward.rb b/ruby/lib/ruby/2.1.0/net/http/backward.rb new file mode 100644 index 0000000..faf47b8 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/http/backward.rb @@ -0,0 +1,25 @@ +# for backward compatibility + +# :enddoc: + +class Net::HTTP + ProxyMod = ProxyDelta +end + +module Net + HTTPSession = Net::HTTP +end + +module Net::NetPrivate + HTTPRequest = ::Net::HTTPRequest +end + +Net::HTTPInformationCode = Net::HTTPInformation +Net::HTTPSuccessCode = Net::HTTPSuccess +Net::HTTPRedirectionCode = Net::HTTPRedirection +Net::HTTPRetriableCode = Net::HTTPRedirection +Net::HTTPClientErrorCode = Net::HTTPClientError +Net::HTTPFatalErrorCode = Net::HTTPClientError +Net::HTTPServerErrorCode = Net::HTTPServerError +Net::HTTPResponceReceiver = Net::HTTPResponse + diff --git a/ruby/lib/ruby/2.1.0/net/http/exceptions.rb b/ruby/lib/ruby/2.1.0/net/http/exceptions.rb new file mode 100644 index 0000000..6c5d81c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/http/exceptions.rb @@ -0,0 +1,25 @@ +# Net::HTTP exception class. +# You cannot use Net::HTTPExceptions directly; instead, you must use +# its subclasses. +module Net::HTTPExceptions + def initialize(msg, res) #:nodoc: + super msg + @response = res + end + attr_reader :response + alias data response #:nodoc: obsolete +end +class Net::HTTPError < Net::ProtocolError + include Net::HTTPExceptions +end +class Net::HTTPRetriableError < Net::ProtoRetriableError + include Net::HTTPExceptions +end +class Net::HTTPServerException < Net::ProtoServerError + # We cannot use the name "HTTPServerError", it is the name of the response. + include Net::HTTPExceptions +end +class Net::HTTPFatalError < Net::ProtoFatalError + include Net::HTTPExceptions +end + diff --git a/ruby/lib/ruby/2.1.0/net/http/generic_request.rb b/ruby/lib/ruby/2.1.0/net/http/generic_request.rb new file mode 100644 index 0000000..b51034c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/http/generic_request.rb @@ -0,0 +1,329 @@ +# HTTPGenericRequest is the parent of the HTTPRequest class. +# Do not use this directly; use a subclass of HTTPRequest. +# +# Mixes in the HTTPHeader module to provide easier access to HTTP headers. +# +class Net::HTTPGenericRequest + + include Net::HTTPHeader + + def initialize(m, reqbody, resbody, uri_or_path, initheader = nil) + @method = m + @request_has_body = reqbody + @response_has_body = resbody + + if URI === uri_or_path then + @uri = uri_or_path.dup + host = @uri.hostname + host += ":#{@uri.port}" if @uri.port != @uri.class::DEFAULT_PORT + path = uri_or_path.request_uri + else + @uri = nil + host = nil + path = uri_or_path + end + + raise ArgumentError, "no HTTP request path given" unless path + raise ArgumentError, "HTTP request path is empty" if path.empty? + @path = path + + @decode_content = false + + if @response_has_body and Net::HTTP::HAVE_ZLIB then + if !initheader || + !initheader.keys.any? { |k| + %w[accept-encoding range].include? k.downcase + } then + @decode_content = true + initheader = initheader ? initheader.dup : {} + initheader["accept-encoding"] = + "gzip;q=1.0,deflate;q=0.6,identity;q=0.3" + end + end + + initialize_http_header initheader + self['Accept'] ||= '*/*' + self['User-Agent'] ||= 'Ruby' + self['Host'] ||= host + @body = nil + @body_stream = nil + @body_data = nil + end + + attr_reader :method + attr_reader :path + attr_reader :uri + + # Automatically set to false if the user sets the Accept-Encoding header. + # This indicates they wish to handle Content-encoding in responses + # themselves. + attr_reader :decode_content + + def inspect + "\#<#{self.class} #{@method}>" + end + + ## + # Don't automatically decode response content-encoding if the user indicates + # they want to handle it. + + def []=(key, val) # :nodoc: + @decode_content = false if key.downcase == 'accept-encoding' + + super key, val + end + + def request_body_permitted? + @request_has_body + end + + def response_body_permitted? + @response_has_body + end + + def body_exist? + warn "Net::HTTPRequest#body_exist? is obsolete; use response_body_permitted?" if $VERBOSE + response_body_permitted? + end + + attr_reader :body + + def body=(str) + @body = str + @body_stream = nil + @body_data = nil + str + end + + attr_reader :body_stream + + def body_stream=(input) + @body = nil + @body_stream = input + @body_data = nil + input + end + + def set_body_internal(str) #:nodoc: internal use only + raise ArgumentError, "both of body argument and HTTPRequest#body set" if str and (@body or @body_stream) + self.body = str if str + if @body.nil? && @body_stream.nil? && @body_data.nil? && request_body_permitted? + self.body = '' + end + end + + # + # write + # + + def exec(sock, ver, path) #:nodoc: internal use only + if @uri + if @uri.port == @uri.default_port + # [Bug #7650] Amazon ECS API and GFE/1.3 disallow extra default port number + self['host'] = @uri.host + else + self['host'] = "#{@uri.host}:#{@uri.port}" + end + end + + if @body + send_request_with_body sock, ver, path, @body + elsif @body_stream + send_request_with_body_stream sock, ver, path, @body_stream + elsif @body_data + send_request_with_body_data sock, ver, path, @body_data + else + write_header sock, ver, path + end + end + + def update_uri(host, port, ssl) # :nodoc: internal use only + return unless @uri + + @uri.host ||= host + @uri.port = port + + scheme = ssl ? 'https' : 'http' + + # convert the class of the URI + unless scheme == @uri.scheme then + new_uri = @uri.to_s.sub(/^https?/, scheme) + @uri = URI new_uri + end + + @uri + end + + private + + class Chunker #:nodoc: + def initialize(sock) + @sock = sock + @prev = nil + end + + def write(buf) + # avoid memcpy() of buf, buf can huge and eat memory bandwidth + @sock.write("#{buf.bytesize.to_s(16)}\r\n") + rv = @sock.write(buf) + @sock.write("\r\n") + rv + end + + def finish + @sock.write("0\r\n\r\n") + end + end + + def send_request_with_body(sock, ver, path, body) + self.content_length = body.bytesize + delete 'Transfer-Encoding' + supply_default_content_type + write_header sock, ver, path + wait_for_continue sock, ver if sock.continue_timeout + sock.write body + end + + def send_request_with_body_stream(sock, ver, path, f) + unless content_length() or chunked? + raise ArgumentError, + "Content-Length not given and Transfer-Encoding is not `chunked'" + end + supply_default_content_type + write_header sock, ver, path + wait_for_continue sock, ver if sock.continue_timeout + if chunked? + chunker = Chunker.new(sock) + IO.copy_stream(f, chunker) + chunker.finish + else + # copy_stream can sendfile() to sock.io unless we use SSL. + # If sock.io is an SSLSocket, copy_stream will hit SSL_write() + IO.copy_stream(f, sock.io) + end + end + + def send_request_with_body_data(sock, ver, path, params) + if /\Amultipart\/form-data\z/i !~ self.content_type + self.content_type = 'application/x-www-form-urlencoded' + return send_request_with_body(sock, ver, path, URI.encode_www_form(params)) + end + + opt = @form_option.dup + require 'securerandom' unless defined?(SecureRandom) + opt[:boundary] ||= SecureRandom.urlsafe_base64(40) + self.set_content_type(self.content_type, boundary: opt[:boundary]) + if chunked? + write_header sock, ver, path + encode_multipart_form_data(sock, params, opt) + else + require 'tempfile' + file = Tempfile.new('multipart') + file.binmode + encode_multipart_form_data(file, params, opt) + file.rewind + self.content_length = file.size + write_header sock, ver, path + IO.copy_stream(file, sock) + file.close(true) + end + end + + def encode_multipart_form_data(out, params, opt) + charset = opt[:charset] + boundary = opt[:boundary] + require 'securerandom' unless defined?(SecureRandom) + boundary ||= SecureRandom.urlsafe_base64(40) + chunked_p = chunked? + + buf = '' + params.each do |key, value, h={}| + key = quote_string(key, charset) + filename = + h.key?(:filename) ? h[:filename] : + value.respond_to?(:to_path) ? File.basename(value.to_path) : + nil + + buf << "--#{boundary}\r\n" + if filename + filename = quote_string(filename, charset) + type = h[:content_type] || 'application/octet-stream' + buf << "Content-Disposition: form-data; " \ + "name=\"#{key}\"; filename=\"#{filename}\"\r\n" \ + "Content-Type: #{type}\r\n\r\n" + if !out.respond_to?(:write) || !value.respond_to?(:read) + # if +out+ is not an IO or +value+ is not an IO + buf << (value.respond_to?(:read) ? value.read : value) + elsif value.respond_to?(:size) && chunked_p + # if +out+ is an IO and +value+ is a File, use IO.copy_stream + flush_buffer(out, buf, chunked_p) + out << "%x\r\n" % value.size if chunked_p + IO.copy_stream(value, out) + out << "\r\n" if chunked_p + else + # +out+ is an IO, and +value+ is not a File but an IO + flush_buffer(out, buf, chunked_p) + 1 while flush_buffer(out, value.read(4096), chunked_p) + end + else + # non-file field: + # HTML5 says, "The parts of the generated multipart/form-data + # resource that correspond to non-file fields must not have a + # Content-Type header specified." + buf << "Content-Disposition: form-data; name=\"#{key}\"\r\n\r\n" + buf << (value.respond_to?(:read) ? value.read : value) + end + buf << "\r\n" + end + buf << "--#{boundary}--\r\n" + flush_buffer(out, buf, chunked_p) + out << "0\r\n\r\n" if chunked_p + end + + def quote_string(str, charset) + str = str.encode(charset, fallback:->(c){'&#%d;'%c.encode("UTF-8").ord}) if charset + str.gsub(/[\\"]/, '\\\\\&') + end + + def flush_buffer(out, buf, chunked_p) + return unless buf + out << "%x\r\n"%buf.bytesize if chunked_p + out << buf + out << "\r\n" if chunked_p + buf.clear + end + + def supply_default_content_type + return if content_type() + warn 'net/http: warning: Content-Type did not set; using application/x-www-form-urlencoded' if $VERBOSE + set_content_type 'application/x-www-form-urlencoded' + end + + ## + # Waits up to the continue timeout for a response from the server provided + # we're speaking HTTP 1.1 and are expecting a 100-continue response. + + def wait_for_continue(sock, ver) + if ver >= '1.1' and @header['expect'] and + @header['expect'].include?('100-continue') + if IO.select([sock.io], nil, nil, sock.continue_timeout) + res = Net::HTTPResponse.read_new(sock) + unless res.kind_of?(Net::HTTPContinue) + res.decode_content = @decode_content + throw :response, res + end + end + end + end + + def write_header(sock, ver, path) + buf = "#{@method} #{path} HTTP/#{ver}\r\n" + each_capitalized do |k,v| + buf << "#{k}: #{v}\r\n" + end + buf << "\r\n" + sock.write buf + end + +end + diff --git a/ruby/lib/ruby/2.1.0/net/http/header.rb b/ruby/lib/ruby/2.1.0/net/http/header.rb new file mode 100644 index 0000000..912419d --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/http/header.rb @@ -0,0 +1,452 @@ +# The HTTPHeader module defines methods for reading and writing +# HTTP headers. +# +# It is used as a mixin by other classes, to provide hash-like +# access to HTTP header values. Unlike raw hash access, HTTPHeader +# provides access via case-insensitive keys. It also provides +# methods for accessing commonly-used HTTP header values in more +# convenient formats. +# +module Net::HTTPHeader + + def initialize_http_header(initheader) + @header = {} + return unless initheader + initheader.each do |key, value| + warn "net/http: warning: duplicated HTTP header: #{key}" if key?(key) and $VERBOSE + @header[key.downcase] = [value.strip] + end + end + + def size #:nodoc: obsolete + @header.size + end + + alias length size #:nodoc: obsolete + + # Returns the header field corresponding to the case-insensitive key. + # For example, a key of "Content-Type" might return "text/html" + def [](key) + a = @header[key.downcase] or return nil + a.join(', ') + end + + # Sets the header field corresponding to the case-insensitive key. + def []=(key, val) + unless val + @header.delete key.downcase + return val + end + @header[key.downcase] = [val] + end + + # [Ruby 1.8.3] + # Adds a value to a named header field, instead of replacing its value. + # Second argument +val+ must be a String. + # See also #[]=, #[] and #get_fields. + # + # request.add_field 'X-My-Header', 'a' + # p request['X-My-Header'] #=> "a" + # p request.get_fields('X-My-Header') #=> ["a"] + # request.add_field 'X-My-Header', 'b' + # p request['X-My-Header'] #=> "a, b" + # p request.get_fields('X-My-Header') #=> ["a", "b"] + # request.add_field 'X-My-Header', 'c' + # p request['X-My-Header'] #=> "a, b, c" + # p request.get_fields('X-My-Header') #=> ["a", "b", "c"] + # + def add_field(key, val) + if @header.key?(key.downcase) + @header[key.downcase].push val + else + @header[key.downcase] = [val] + end + end + + # [Ruby 1.8.3] + # Returns an array of header field strings corresponding to the + # case-insensitive +key+. This method allows you to get duplicated + # header fields without any processing. See also #[]. + # + # p response.get_fields('Set-Cookie') + # #=> ["session=al98axx; expires=Fri, 31-Dec-1999 23:58:23", + # "query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23"] + # p response['Set-Cookie'] + # #=> "session=al98axx; expires=Fri, 31-Dec-1999 23:58:23, query=rubyscript; expires=Fri, 31-Dec-1999 23:58:23" + # + def get_fields(key) + return nil unless @header[key.downcase] + @header[key.downcase].dup + end + + # Returns the header field corresponding to the case-insensitive key. + # Returns the default value +args+, or the result of the block, or + # raises an IndexError if there's no header field named +key+ + # See Hash#fetch + def fetch(key, *args, &block) #:yield: +key+ + a = @header.fetch(key.downcase, *args, &block) + a.kind_of?(Array) ? a.join(', ') : a + end + + # Iterates through the header names and values, passing in the name + # and value to the code block supplied. + # + # Example: + # + # response.header.each_header {|key,value| puts "#{key} = #{value}" } + # + def each_header #:yield: +key+, +value+ + block_given? or return enum_for(__method__) + @header.each do |k,va| + yield k, va.join(', ') + end + end + + alias each each_header + + # Iterates through the header names in the header, passing + # each header name to the code block. + def each_name(&block) #:yield: +key+ + block_given? or return enum_for(__method__) + @header.each_key(&block) + end + + alias each_key each_name + + # Iterates through the header names in the header, passing + # capitalized header names to the code block. + # + # Note that header names are capitalized systematically; + # capitalization may not match that used by the remote HTTP + # server in its response. + def each_capitalized_name #:yield: +key+ + block_given? or return enum_for(__method__) + @header.each_key do |k| + yield capitalize(k) + end + end + + # Iterates through header values, passing each value to the + # code block. + def each_value #:yield: +value+ + block_given? or return enum_for(__method__) + @header.each_value do |va| + yield va.join(', ') + end + end + + # Removes a header field, specified by case-insensitive key. + def delete(key) + @header.delete(key.downcase) + end + + # true if +key+ header exists. + def key?(key) + @header.key?(key.downcase) + end + + # Returns a Hash consisting of header names and array of values. + # e.g. + # {"cache-control" => ["private"], + # "content-type" => ["text/html"], + # "date" => ["Wed, 22 Jun 2005 22:11:50 GMT"]} + def to_hash + @header.dup + end + + # As for #each_header, except the keys are provided in capitalized form. + # + # Note that header names are capitalized systematically; + # capitalization may not match that used by the remote HTTP + # server in its response. + def each_capitalized + block_given? or return enum_for(__method__) + @header.each do |k,v| + yield capitalize(k), v.join(', ') + end + end + + alias canonical_each each_capitalized + + def capitalize(name) + name.split(/-/).map {|s| s.capitalize }.join('-') + end + private :capitalize + + # Returns an Array of Range objects which represent the Range: + # HTTP header field, or +nil+ if there is no such header. + def range + return nil unless @header['range'] + + value = self['Range'] + # byte-range-set = *( "," OWS ) ( byte-range-spec / suffix-byte-range-spec ) + # *( OWS "," [ OWS ( byte-range-spec / suffix-byte-range-spec ) ] ) + # corrected collected ABNF + # http://tools.ietf.org/html/draft-ietf-httpbis-p5-range-19#section-5.4.1 + # http://tools.ietf.org/html/draft-ietf-httpbis-p5-range-19#appendix-C + # http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-19#section-3.2.5 + unless /\Abytes=((?:,[ \t]*)*(?:\d+-\d*|-\d+)(?:[ \t]*,(?:[ \t]*\d+-\d*|-\d+)?)*)\z/ =~ value + raise Net::HTTPHeaderSyntaxError, "invalid syntax for byte-ranges-specifier: '#{value}'" + end + + byte_range_set = $1 + result = byte_range_set.split(/,/).map {|spec| + m = /(\d+)?\s*-\s*(\d+)?/i.match(spec) or + raise Net::HTTPHeaderSyntaxError, "invalid byte-range-spec: '#{spec}'" + d1 = m[1].to_i + d2 = m[2].to_i + if m[1] and m[2] + if d1 > d2 + raise Net::HTTPHeaderSyntaxError, "last-byte-pos MUST greater than or equal to first-byte-pos but '#{spec}'" + end + d1..d2 + elsif m[1] + d1..-1 + elsif m[2] + -d2..-1 + else + raise Net::HTTPHeaderSyntaxError, 'range is not specified' + end + } + # if result.empty? + # byte-range-set must include at least one byte-range-spec or suffix-byte-range-spec + # but above regexp already denies it. + if result.size == 1 && result[0].begin == 0 && result[0].end == -1 + raise Net::HTTPHeaderSyntaxError, 'only one suffix-byte-range-spec with zero suffix-length' + end + result + end + + # Sets the HTTP Range: header. + # Accepts either a Range object as a single argument, + # or a beginning index and a length from that index. + # Example: + # + # req.range = (0..1023) + # req.set_range 0, 1023 + # + def set_range(r, e = nil) + unless r + @header.delete 'range' + return r + end + r = (r...r+e) if e + case r + when Numeric + n = r.to_i + rangestr = (n > 0 ? "0-#{n-1}" : "-#{-n}") + when Range + first = r.first + last = r.end + last -= 1 if r.exclude_end? + if last == -1 + rangestr = (first > 0 ? "#{first}-" : "-#{-first}") + else + raise Net::HTTPHeaderSyntaxError, 'range.first is negative' if first < 0 + raise Net::HTTPHeaderSyntaxError, 'range.last is negative' if last < 0 + raise Net::HTTPHeaderSyntaxError, 'must be .first < .last' if first > last + rangestr = "#{first}-#{last}" + end + else + raise TypeError, 'Range/Integer is required' + end + @header['range'] = ["bytes=#{rangestr}"] + r + end + + alias range= set_range + + # Returns an Integer object which represents the HTTP Content-Length: + # header field, or +nil+ if that field was not provided. + def content_length + return nil unless key?('Content-Length') + len = self['Content-Length'].slice(/\d+/) or + raise Net::HTTPHeaderSyntaxError, 'wrong Content-Length format' + len.to_i + end + + def content_length=(len) + unless len + @header.delete 'content-length' + return nil + end + @header['content-length'] = [len.to_i.to_s] + end + + # Returns "true" if the "transfer-encoding" header is present and + # set to "chunked". This is an HTTP/1.1 feature, allowing the + # the content to be sent in "chunks" without at the outset + # stating the entire content length. + def chunked? + return false unless @header['transfer-encoding'] + field = self['Transfer-Encoding'] + (/(?:\A|[^\-\w])chunked(?![\-\w])/i =~ field) ? true : false + end + + # Returns a Range object which represents the value of the Content-Range: + # header field. + # For a partial entity body, this indicates where this fragment + # fits inside the full entity body, as range of byte offsets. + def content_range + return nil unless @header['content-range'] + m = %ri.match(self['Content-Range']) or + raise Net::HTTPHeaderSyntaxError, 'wrong Content-Range format' + m[1].to_i .. m[2].to_i + end + + # The length of the range represented in Content-Range: header. + def range_length + r = content_range() or return nil + r.end - r.begin + 1 + end + + # Returns a content type string such as "text/html". + # This method returns nil if Content-Type: header field does not exist. + def content_type + return nil unless main_type() + if sub_type() + then "#{main_type()}/#{sub_type()}" + else main_type() + end + end + + # Returns a content type string such as "text". + # This method returns nil if Content-Type: header field does not exist. + def main_type + return nil unless @header['content-type'] + self['Content-Type'].split(';').first.to_s.split('/')[0].to_s.strip + end + + # Returns a content type string such as "html". + # This method returns nil if Content-Type: header field does not exist + # or sub-type is not given (e.g. "Content-Type: text"). + def sub_type + return nil unless @header['content-type'] + _, sub = *self['Content-Type'].split(';').first.to_s.split('/') + return nil unless sub + sub.strip + end + + # Any parameters specified for the content type, returned as a Hash. + # For example, a header of Content-Type: text/html; charset=EUC-JP + # would result in type_params returning {'charset' => 'EUC-JP'} + def type_params + result = {} + list = self['Content-Type'].to_s.split(';') + list.shift + list.each do |param| + k, v = *param.split('=', 2) + result[k.strip] = v.strip + end + result + end + + # Sets the content type in an HTTP header. + # The +type+ should be a full HTTP content type, e.g. "text/html". + # The +params+ are an optional Hash of parameters to add after the + # content type, e.g. {'charset' => 'iso-8859-1'} + def set_content_type(type, params = {}) + @header['content-type'] = [type + params.map{|k,v|"; #{k}=#{v}"}.join('')] + end + + alias content_type= set_content_type + + # Set header fields and a body from HTML form data. + # +params+ should be an Array of Arrays or + # a Hash containing HTML form data. + # Optional argument +sep+ means data record separator. + # + # Values are URL encoded as necessary and the content-type is set to + # application/x-www-form-urlencoded + # + # Example: + # http.form_data = {"q" => "ruby", "lang" => "en"} + # http.form_data = {"q" => ["ruby", "perl"], "lang" => "en"} + # http.set_form_data({"q" => "ruby", "lang" => "en"}, ';') + # + def set_form_data(params, sep = '&') + query = URI.encode_www_form(params) + query.gsub!(/&/, sep) if sep != '&' + self.body = query + self.content_type = 'application/x-www-form-urlencoded' + end + + alias form_data= set_form_data + + # Set a HTML form data set. + # +params+ is the form data set; it is an Array of Arrays or a Hash + # +enctype is the type to encode the form data set. + # It is application/x-www-form-urlencoded or multipart/form-data. + # +formpot+ is an optional hash to specify the detail. + # + # boundary:: the boundary of the multipart message + # charset:: the charset of the message. All names and the values of + # non-file fields are encoded as the charset. + # + # Each item of params is an array and contains following items: + # +name+:: the name of the field + # +value+:: the value of the field, it should be a String or a File + # +opt+:: an optional hash to specify additional information + # + # Each item is a file field or a normal field. + # If +value+ is a File object or the +opt+ have a filename key, + # the item is treated as a file field. + # + # If Transfer-Encoding is set as chunked, this send the request in + # chunked encoding. Because chunked encoding is HTTP/1.1 feature, + # you must confirm the server to support HTTP/1.1 before sending it. + # + # Example: + # http.set_form([["q", "ruby"], ["lang", "en"]]) + # + # See also RFC 2388, RFC 2616, HTML 4.01, and HTML5 + # + def set_form(params, enctype='application/x-www-form-urlencoded', formopt={}) + @body_data = params + @body = nil + @body_stream = nil + @form_option = formopt + case enctype + when /\Aapplication\/x-www-form-urlencoded\z/i, + /\Amultipart\/form-data\z/i + self.content_type = enctype + else + raise ArgumentError, "invalid enctype: #{enctype}" + end + end + + # Set the Authorization: header for "Basic" authorization. + def basic_auth(account, password) + @header['authorization'] = [basic_encode(account, password)] + end + + # Set Proxy-Authorization: header for "Basic" authorization. + def proxy_basic_auth(account, password) + @header['proxy-authorization'] = [basic_encode(account, password)] + end + + def basic_encode(account, password) + 'Basic ' + ["#{account}:#{password}"].pack('m').delete("\r\n") + end + private :basic_encode + + def connection_close? + tokens(@header['connection']).include?('close') or + tokens(@header['proxy-connection']).include?('close') + end + + def connection_keep_alive? + tokens(@header['connection']).include?('keep-alive') or + tokens(@header['proxy-connection']).include?('keep-alive') + end + + def tokens(vals) + return [] unless vals + vals.map {|v| v.split(',') }.flatten\ + .reject {|str| str.strip.empty? }\ + .map {|tok| tok.strip.downcase } + end + private :tokens + +end + diff --git a/ruby/lib/ruby/2.1.0/net/http/proxy_delta.rb b/ruby/lib/ruby/2.1.0/net/http/proxy_delta.rb new file mode 100644 index 0000000..b16c9f1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/http/proxy_delta.rb @@ -0,0 +1,16 @@ +module Net::HTTP::ProxyDelta #:nodoc: internal use only + private + + def conn_address + proxy_address() + end + + def conn_port + proxy_port() + end + + def edit_path(path) + use_ssl? ? path : "http://#{addr_port()}#{path}" + end +end + diff --git a/ruby/lib/ruby/2.1.0/net/http/request.rb b/ruby/lib/ruby/2.1.0/net/http/request.rb new file mode 100644 index 0000000..e8b0f48 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/http/request.rb @@ -0,0 +1,20 @@ +# HTTP request class. +# This class wraps together the request header and the request path. +# You cannot use this class directly. Instead, you should use one of its +# subclasses: Net::HTTP::Get, Net::HTTP::Post, Net::HTTP::Head. +# +class Net::HTTPRequest < Net::HTTPGenericRequest + # Creates an HTTP request object for +path+. + # + # +initheader+ are the default headers to use. Net::HTTP adds + # Accept-Encoding to enable compression of the response body unless + # Accept-Encoding or Range are supplied in +initheader+. + + def initialize(path, initheader = nil) + super self.class::METHOD, + self.class::REQUEST_HAS_BODY, + self.class::RESPONSE_HAS_BODY, + path, initheader + end +end + diff --git a/ruby/lib/ruby/2.1.0/net/http/requests.rb b/ruby/lib/ruby/2.1.0/net/http/requests.rb new file mode 100644 index 0000000..ef719ea --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/http/requests.rb @@ -0,0 +1,122 @@ +# +# HTTP/1.1 methods --- RFC2616 +# + +# See Net::HTTPGenericRequest for attributes and methods. +# See Net::HTTP for usage examples. +class Net::HTTP::Get < Net::HTTPRequest + METHOD = 'GET' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true +end + +# See Net::HTTPGenericRequest for attributes and methods. +# See Net::HTTP for usage examples. +class Net::HTTP::Head < Net::HTTPRequest + METHOD = 'HEAD' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = false +end + +# See Net::HTTPGenericRequest for attributes and methods. +# See Net::HTTP for usage examples. +class Net::HTTP::Post < Net::HTTPRequest + METHOD = 'POST' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true +end + +# See Net::HTTPGenericRequest for attributes and methods. +# See Net::HTTP for usage examples. +class Net::HTTP::Put < Net::HTTPRequest + METHOD = 'PUT' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true +end + +# See Net::HTTPGenericRequest for attributes and methods. +# See Net::HTTP for usage examples. +class Net::HTTP::Delete < Net::HTTPRequest + METHOD = 'DELETE' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true +end + +# See Net::HTTPGenericRequest for attributes and methods. +class Net::HTTP::Options < Net::HTTPRequest + METHOD = 'OPTIONS' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = false +end + +# See Net::HTTPGenericRequest for attributes and methods. +class Net::HTTP::Trace < Net::HTTPRequest + METHOD = 'TRACE' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true +end + +# +# PATCH method --- RFC5789 +# + +# See Net::HTTPGenericRequest for attributes and methods. +class Net::HTTP::Patch < Net::HTTPRequest + METHOD = 'PATCH' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true +end + +# +# WebDAV methods --- RFC2518 +# + +# See Net::HTTPGenericRequest for attributes and methods. +class Net::HTTP::Propfind < Net::HTTPRequest + METHOD = 'PROPFIND' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true +end + +# See Net::HTTPGenericRequest for attributes and methods. +class Net::HTTP::Proppatch < Net::HTTPRequest + METHOD = 'PROPPATCH' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true +end + +# See Net::HTTPGenericRequest for attributes and methods. +class Net::HTTP::Mkcol < Net::HTTPRequest + METHOD = 'MKCOL' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true +end + +# See Net::HTTPGenericRequest for attributes and methods. +class Net::HTTP::Copy < Net::HTTPRequest + METHOD = 'COPY' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true +end + +# See Net::HTTPGenericRequest for attributes and methods. +class Net::HTTP::Move < Net::HTTPRequest + METHOD = 'MOVE' + REQUEST_HAS_BODY = false + RESPONSE_HAS_BODY = true +end + +# See Net::HTTPGenericRequest for attributes and methods. +class Net::HTTP::Lock < Net::HTTPRequest + METHOD = 'LOCK' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true +end + +# See Net::HTTPGenericRequest for attributes and methods. +class Net::HTTP::Unlock < Net::HTTPRequest + METHOD = 'UNLOCK' + REQUEST_HAS_BODY = true + RESPONSE_HAS_BODY = true +end + diff --git a/ruby/lib/ruby/2.1.0/net/http/response.rb b/ruby/lib/ruby/2.1.0/net/http/response.rb new file mode 100644 index 0000000..da3e4b4 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/http/response.rb @@ -0,0 +1,405 @@ +# HTTP response class. +# +# This class wraps together the response header and the response body (the +# entity requested). +# +# It mixes in the HTTPHeader module, which provides access to response +# header values both via hash-like methods and via individual readers. +# +# Note that each possible HTTP response code defines its own +# HTTPResponse subclass. These are listed below. +# +# All classes are defined under the Net module. Indentation indicates +# inheritance. For a list of the classes see Net::HTTP. +# +# +class Net::HTTPResponse + class << self + # true if the response has a body. + def body_permitted? + self::HAS_BODY + end + + def exception_type # :nodoc: internal use only + self::EXCEPTION_TYPE + end + + def read_new(sock) #:nodoc: internal use only + httpv, code, msg = read_status_line(sock) + res = response_class(code).new(httpv, code, msg) + each_response_header(sock) do |k,v| + res.add_field k, v + end + res + end + + private + + def read_status_line(sock) + str = sock.readline + m = /\AHTTP(?:\/(\d+\.\d+))?\s+(\d\d\d)\s*(.*)\z/in.match(str) or + raise Net::HTTPBadResponse, "wrong status line: #{str.dump}" + m.captures + end + + def response_class(code) + CODE_TO_OBJ[code] or + CODE_CLASS_TO_OBJ[code[0,1]] or + Net::HTTPUnknownResponse + end + + def each_response_header(sock) + key = value = nil + while true + line = sock.readuntil("\n", true).sub(/\s+\z/, '') + break if line.empty? + if line[0] == ?\s or line[0] == ?\t and value + value << ' ' unless value.empty? + value << line.strip + else + yield key, value if key + key, value = line.strip.split(/\s*:\s*/, 2) + raise Net::HTTPBadResponse, 'wrong header line format' if value.nil? + end + end + yield key, value if key + end + end + + # next is to fix bug in RDoc, where the private inside class << self + # spills out. + public + + include Net::HTTPHeader + + def initialize(httpv, code, msg) #:nodoc: internal use only + @http_version = httpv + @code = code + @message = msg + initialize_http_header nil + @body = nil + @read = false + @uri = nil + @decode_content = false + end + + # The HTTP version supported by the server. + attr_reader :http_version + + # The HTTP result code string. For example, '302'. You can also + # determine the response type by examining which response subclass + # the response object is an instance of. + attr_reader :code + + # The HTTP result message sent by the server. For example, 'Not Found'. + attr_reader :message + alias msg message # :nodoc: obsolete + + # The URI used to fetch this response. The response URI is only available + # if a URI was used to create the request. + attr_reader :uri + + # Set to true automatically when the request did not contain an + # Accept-Encoding header from the user. + attr_accessor :decode_content + + def inspect + "#<#{self.class} #{@code} #{@message} readbody=#{@read}>" + end + + # + # response <-> exception relationship + # + + def code_type #:nodoc: + self.class + end + + def error! #:nodoc: + raise error_type().new(@code + ' ' + @message.dump, self) + end + + def error_type #:nodoc: + self.class::EXCEPTION_TYPE + end + + # Raises an HTTP error if the response is not 2xx (success). + def value + error! unless self.kind_of?(Net::HTTPSuccess) + end + + def uri= uri # :nodoc: + @uri = uri.dup if uri + end + + # + # header (for backward compatibility only; DO NOT USE) + # + + def response #:nodoc: + warn "#{caller(1)[0]}: warning: Net::HTTPResponse#response is obsolete" if $VERBOSE + self + end + + def header #:nodoc: + warn "#{caller(1)[0]}: warning: Net::HTTPResponse#header is obsolete" if $VERBOSE + self + end + + def read_header #:nodoc: + warn "#{caller(1)[0]}: warning: Net::HTTPResponse#read_header is obsolete" if $VERBOSE + self + end + + # + # body + # + + def reading_body(sock, reqmethodallowbody) #:nodoc: internal use only + @socket = sock + @body_exist = reqmethodallowbody && self.class.body_permitted? + begin + yield + self.body # ensure to read body + ensure + @socket = nil + end + end + + # Gets the entity body returned by the remote HTTP server. + # + # If a block is given, the body is passed to the block, and + # the body is provided in fragments, as it is read in from the socket. + # + # Calling this method a second or subsequent time for the same + # HTTPResponse object will return the value already read. + # + # http.request_get('/index.html') {|res| + # puts res.read_body + # } + # + # http.request_get('/index.html') {|res| + # p res.read_body.object_id # 538149362 + # p res.read_body.object_id # 538149362 + # } + # + # # using iterator + # http.request_get('/index.html') {|res| + # res.read_body do |segment| + # print segment + # end + # } + # + def read_body(dest = nil, &block) + if @read + raise IOError, "#{self.class}\#read_body called twice" if dest or block + return @body + end + to = procdest(dest, block) + stream_check + if @body_exist + read_body_0 to + @body = to + else + @body = nil + end + @read = true + + @body + end + + # Returns the full entity body. + # + # Calling this method a second or subsequent time will return the + # string already read. + # + # http.request_get('/index.html') {|res| + # puts res.body + # } + # + # http.request_get('/index.html') {|res| + # p res.body.object_id # 538149362 + # p res.body.object_id # 538149362 + # } + # + def body + read_body() + end + + # Because it may be necessary to modify the body, Eg, decompression + # this method facilitates that. + def body=(value) + @body = value + end + + alias entity body #:nodoc: obsolete + + private + + ## + # Checks for a supported Content-Encoding header and yields an Inflate + # wrapper for this response's socket when zlib is present. If the + # Content-Encoding is unsupported or zlib is missing the plain socket is + # yielded. + # + # If a Content-Range header is present a plain socket is yielded as the + # bytes in the range may not be a complete deflate block. + + def inflater # :nodoc: + return yield @socket unless Net::HTTP::HAVE_ZLIB + return yield @socket unless @decode_content + return yield @socket if self['content-range'] + + case self['content-encoding'] + when 'deflate', 'gzip', 'x-gzip' then + self.delete 'content-encoding' + + inflate_body_io = Inflater.new(@socket) + + begin + yield inflate_body_io + ensure + inflate_body_io.finish + end + when 'none', 'identity' then + self.delete 'content-encoding' + + yield @socket + else + yield @socket + end + end + + def read_body_0(dest) + inflater do |inflate_body_io| + if chunked? + read_chunked dest, inflate_body_io + return + end + + @socket = inflate_body_io + + clen = content_length() + if clen + @socket.read clen, dest, true # ignore EOF + return + end + clen = range_length() + if clen + @socket.read clen, dest + return + end + @socket.read_all dest + end + end + + ## + # read_chunked reads from +@socket+ for chunk-size, chunk-extension, CRLF, + # etc. and +chunk_data_io+ for chunk-data which may be deflate or gzip + # encoded. + # + # See RFC 2616 section 3.6.1 for definitions + + def read_chunked(dest, chunk_data_io) # :nodoc: + len = nil + total = 0 + while true + line = @socket.readline + hexlen = line.slice(/[0-9a-fA-F]+/) or + raise Net::HTTPBadResponse, "wrong chunk size line: #{line}" + len = hexlen.hex + break if len == 0 + begin + chunk_data_io.read len, dest + ensure + total += len + @socket.read 2 # \r\n + end + end + until @socket.readline.empty? + # none + end + end + + def stream_check + raise IOError, 'attempt to read body out of block' if @socket.closed? + end + + def procdest(dest, block) + raise ArgumentError, 'both arg and block given for HTTP method' if + dest and block + if block + Net::ReadAdapter.new(block) + else + dest || '' + end + end + + ## + # Inflater is a wrapper around Net::BufferedIO that transparently inflates + # zlib and gzip streams. + + class Inflater # :nodoc: + + ## + # Creates a new Inflater wrapping +socket+ + + def initialize socket + @socket = socket + # zlib with automatic gzip detection + @inflate = Zlib::Inflate.new(32 + Zlib::MAX_WBITS) + end + + ## + # Finishes the inflate stream. + + def finish + @inflate.finish + end + + ## + # Returns a Net::ReadAdapter that inflates each read chunk into +dest+. + # + # This allows a large response body to be inflated without storing the + # entire body in memory. + + def inflate_adapter(dest) + block = proc do |compressed_chunk| + @inflate.inflate(compressed_chunk) do |chunk| + dest << chunk + end + end + + Net::ReadAdapter.new(block) + end + + ## + # Reads +clen+ bytes from the socket, inflates them, then writes them to + # +dest+. +ignore_eof+ is passed down to Net::BufferedIO#read + # + # Unlike Net::BufferedIO#read, this method returns more than +clen+ bytes. + # At this time there is no way for a user of Net::HTTPResponse to read a + # specific number of bytes from the HTTP response body, so this internal + # API does not return the same number of bytes as were requested. + # + # See https://bugs.ruby-lang.org/issues/6492 for further discussion. + + def read clen, dest, ignore_eof = false + temp_dest = inflate_adapter(dest) + + @socket.read clen, temp_dest, ignore_eof + end + + ## + # Reads the rest of the socket, inflates it, then writes it to +dest+. + + def read_all dest + temp_dest = inflate_adapter(dest) + + @socket.read_all temp_dest + end + + end + +end + diff --git a/ruby/lib/ruby/2.1.0/net/http/responses.rb b/ruby/lib/ruby/2.1.0/net/http/responses.rb new file mode 100644 index 0000000..bc7642c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/http/responses.rb @@ -0,0 +1,271 @@ +# :stopdoc: +class Net::HTTPUnknownResponse < Net::HTTPResponse + HAS_BODY = true + EXCEPTION_TYPE = Net::HTTPError +end +class Net::HTTPInformation < Net::HTTPResponse # 1xx + HAS_BODY = false + EXCEPTION_TYPE = Net::HTTPError +end +class Net::HTTPSuccess < Net::HTTPResponse # 2xx + HAS_BODY = true + EXCEPTION_TYPE = Net::HTTPError +end +class Net::HTTPRedirection < Net::HTTPResponse # 3xx + HAS_BODY = true + EXCEPTION_TYPE = Net::HTTPRetriableError +end +class Net::HTTPClientError < Net::HTTPResponse # 4xx + HAS_BODY = true + EXCEPTION_TYPE = Net::HTTPServerException # for backward compatibility +end +class Net::HTTPServerError < Net::HTTPResponse # 5xx + HAS_BODY = true + EXCEPTION_TYPE = Net::HTTPFatalError # for backward compatibility +end + +class Net::HTTPContinue < Net::HTTPInformation # 100 + HAS_BODY = false +end +class Net::HTTPSwitchProtocol < Net::HTTPInformation # 101 + HAS_BODY = false +end +# 102 - RFC 2518; removed in RFC 4918 + +class Net::HTTPOK < Net::HTTPSuccess # 200 + HAS_BODY = true +end +class Net::HTTPCreated < Net::HTTPSuccess # 201 + HAS_BODY = true +end +class Net::HTTPAccepted < Net::HTTPSuccess # 202 + HAS_BODY = true +end +class Net::HTTPNonAuthoritativeInformation < Net::HTTPSuccess # 203 + HAS_BODY = true +end +class Net::HTTPNoContent < Net::HTTPSuccess # 204 + HAS_BODY = false +end +class Net::HTTPResetContent < Net::HTTPSuccess # 205 + HAS_BODY = false +end +class Net::HTTPPartialContent < Net::HTTPSuccess # 206 + HAS_BODY = true +end +class Net::HTTPMultiStatus < Net::HTTPSuccess # 207 - RFC 4918 + HAS_BODY = true +end +# 208 Already Reported - RFC 5842; experimental +class Net::HTTPIMUsed < Net::HTTPSuccess # 226 - RFC 3229 + HAS_BODY = true +end + +class Net::HTTPMultipleChoices < Net::HTTPRedirection # 300 + HAS_BODY = true +end +Net::HTTPMultipleChoice = Net::HTTPMultipleChoices +class Net::HTTPMovedPermanently < Net::HTTPRedirection # 301 + HAS_BODY = true +end +class Net::HTTPFound < Net::HTTPRedirection # 302 + HAS_BODY = true +end +Net::HTTPMovedTemporarily = Net::HTTPFound +class Net::HTTPSeeOther < Net::HTTPRedirection # 303 + HAS_BODY = true +end +class Net::HTTPNotModified < Net::HTTPRedirection # 304 + HAS_BODY = false +end +class Net::HTTPUseProxy < Net::HTTPRedirection # 305 + HAS_BODY = false +end +# 306 Switch Proxy - no longer unused +class Net::HTTPTemporaryRedirect < Net::HTTPRedirection # 307 + HAS_BODY = true +end +# 308 Permanent Redirect - in draft + +class Net::HTTPBadRequest < Net::HTTPClientError # 400 + HAS_BODY = true +end +class Net::HTTPUnauthorized < Net::HTTPClientError # 401 + HAS_BODY = true +end +class Net::HTTPPaymentRequired < Net::HTTPClientError # 402 + HAS_BODY = true +end +class Net::HTTPForbidden < Net::HTTPClientError # 403 + HAS_BODY = true +end +class Net::HTTPNotFound < Net::HTTPClientError # 404 + HAS_BODY = true +end +class Net::HTTPMethodNotAllowed < Net::HTTPClientError # 405 + HAS_BODY = true +end +class Net::HTTPNotAcceptable < Net::HTTPClientError # 406 + HAS_BODY = true +end +class Net::HTTPProxyAuthenticationRequired < Net::HTTPClientError # 407 + HAS_BODY = true +end +class Net::HTTPRequestTimeOut < Net::HTTPClientError # 408 + HAS_BODY = true +end +class Net::HTTPConflict < Net::HTTPClientError # 409 + HAS_BODY = true +end +class Net::HTTPGone < Net::HTTPClientError # 410 + HAS_BODY = true +end +class Net::HTTPLengthRequired < Net::HTTPClientError # 411 + HAS_BODY = true +end +class Net::HTTPPreconditionFailed < Net::HTTPClientError # 412 + HAS_BODY = true +end +class Net::HTTPRequestEntityTooLarge < Net::HTTPClientError # 413 + HAS_BODY = true +end +class Net::HTTPRequestURITooLong < Net::HTTPClientError # 414 + HAS_BODY = true +end +Net::HTTPRequestURITooLarge = Net::HTTPRequestURITooLong +class Net::HTTPUnsupportedMediaType < Net::HTTPClientError # 415 + HAS_BODY = true +end +class Net::HTTPRequestedRangeNotSatisfiable < Net::HTTPClientError # 416 + HAS_BODY = true +end +class Net::HTTPExpectationFailed < Net::HTTPClientError # 417 + HAS_BODY = true +end +# 418 I'm a teapot - RFC 2324; a joke RFC +# 420 Enhance Your Calm - Twitter +class Net::HTTPUnprocessableEntity < Net::HTTPClientError # 422 - RFC 4918 + HAS_BODY = true +end +class Net::HTTPLocked < Net::HTTPClientError # 423 - RFC 4918 + HAS_BODY = true +end +class Net::HTTPFailedDependency < Net::HTTPClientError # 424 - RFC 4918 + HAS_BODY = true +end +# 425 Unordered Collection - existed only in draft +class Net::HTTPUpgradeRequired < Net::HTTPClientError # 426 - RFC 2817 + HAS_BODY = true +end +class Net::HTTPPreconditionRequired < Net::HTTPClientError # 428 - RFC 6585 + HAS_BODY = true +end +class Net::HTTPTooManyRequests < Net::HTTPClientError # 429 - RFC 6585 + HAS_BODY = true +end +class Net::HTTPRequestHeaderFieldsTooLarge < Net::HTTPClientError # 431 - RFC 6585 + HAS_BODY = true +end +# 444 No Response - Nginx +# 449 Retry With - Microsoft +# 450 Blocked by Windows Parental Controls - Microsoft +# 499 Client Closed Request - Nginx + +class Net::HTTPInternalServerError < Net::HTTPServerError # 500 + HAS_BODY = true +end +class Net::HTTPNotImplemented < Net::HTTPServerError # 501 + HAS_BODY = true +end +class Net::HTTPBadGateway < Net::HTTPServerError # 502 + HAS_BODY = true +end +class Net::HTTPServiceUnavailable < Net::HTTPServerError # 503 + HAS_BODY = true +end +class Net::HTTPGatewayTimeOut < Net::HTTPServerError # 504 + HAS_BODY = true +end +class Net::HTTPVersionNotSupported < Net::HTTPServerError # 505 + HAS_BODY = true +end +# 506 Variant Also Negotiates - RFC 2295; experimental +class Net::HTTPInsufficientStorage < Net::HTTPServerError # 507 - RFC 4918 + HAS_BODY = true +end +# 508 Loop Detected - RFC 5842; experimental +# 509 Bandwidth Limit Exceeded - Apache bw/limited extension +# 510 Not Extended - RFC 2774; experimental +class Net::HTTPNetworkAuthenticationRequired < Net::HTTPServerError # 511 - RFC 6585 + HAS_BODY = true +end + +class Net::HTTPResponse + CODE_CLASS_TO_OBJ = { + '1' => Net::HTTPInformation, + '2' => Net::HTTPSuccess, + '3' => Net::HTTPRedirection, + '4' => Net::HTTPClientError, + '5' => Net::HTTPServerError + } + CODE_TO_OBJ = { + '100' => Net::HTTPContinue, + '101' => Net::HTTPSwitchProtocol, + + '200' => Net::HTTPOK, + '201' => Net::HTTPCreated, + '202' => Net::HTTPAccepted, + '203' => Net::HTTPNonAuthoritativeInformation, + '204' => Net::HTTPNoContent, + '205' => Net::HTTPResetContent, + '206' => Net::HTTPPartialContent, + '207' => Net::HTTPMultiStatus, + '226' => Net::HTTPIMUsed, + + '300' => Net::HTTPMultipleChoices, + '301' => Net::HTTPMovedPermanently, + '302' => Net::HTTPFound, + '303' => Net::HTTPSeeOther, + '304' => Net::HTTPNotModified, + '305' => Net::HTTPUseProxy, + '307' => Net::HTTPTemporaryRedirect, + + '400' => Net::HTTPBadRequest, + '401' => Net::HTTPUnauthorized, + '402' => Net::HTTPPaymentRequired, + '403' => Net::HTTPForbidden, + '404' => Net::HTTPNotFound, + '405' => Net::HTTPMethodNotAllowed, + '406' => Net::HTTPNotAcceptable, + '407' => Net::HTTPProxyAuthenticationRequired, + '408' => Net::HTTPRequestTimeOut, + '409' => Net::HTTPConflict, + '410' => Net::HTTPGone, + '411' => Net::HTTPLengthRequired, + '412' => Net::HTTPPreconditionFailed, + '413' => Net::HTTPRequestEntityTooLarge, + '414' => Net::HTTPRequestURITooLong, + '415' => Net::HTTPUnsupportedMediaType, + '416' => Net::HTTPRequestedRangeNotSatisfiable, + '417' => Net::HTTPExpectationFailed, + '422' => Net::HTTPUnprocessableEntity, + '423' => Net::HTTPLocked, + '424' => Net::HTTPFailedDependency, + '426' => Net::HTTPUpgradeRequired, + '428' => Net::HTTPPreconditionRequired, + '429' => Net::HTTPTooManyRequests, + '431' => Net::HTTPRequestHeaderFieldsTooLarge, + + '500' => Net::HTTPInternalServerError, + '501' => Net::HTTPNotImplemented, + '502' => Net::HTTPBadGateway, + '503' => Net::HTTPServiceUnavailable, + '504' => Net::HTTPGatewayTimeOut, + '505' => Net::HTTPVersionNotSupported, + '507' => Net::HTTPInsufficientStorage, + '511' => Net::HTTPNetworkAuthenticationRequired, + } +end + +# :startdoc: + diff --git a/ruby/lib/ruby/2.1.0/net/https.rb b/ruby/lib/ruby/2.1.0/net/https.rb new file mode 100644 index 0000000..d36f820 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/https.rb @@ -0,0 +1,22 @@ +=begin + += net/https -- SSL/TLS enhancement for Net::HTTP. + + This file has been merged with net/http. There is no longer any need to + require 'net/https' to use HTTPS. + + See Net::HTTP for details on how to make HTTPS connections. + +== Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU Yuuzou + All rights reserved. + +== Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + +=end + +require 'net/http' +require 'openssl' diff --git a/ruby/lib/ruby/2.1.0/net/imap.rb b/ruby/lib/ruby/2.1.0/net/imap.rb new file mode 100644 index 0000000..1bb0b81 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/imap.rb @@ -0,0 +1,3770 @@ +# +# = net/imap.rb +# +# Copyright (C) 2000 Shugo Maeda +# +# This library is distributed under the terms of the Ruby license. +# You can freely distribute/modify this library. +# +# Documentation: Shugo Maeda, with RDoc conversion and overview by William +# Webber. +# +# See Net::IMAP for documentation. +# + + +require "socket" +require "monitor" +require "digest/md5" +require "strscan" +begin + require "openssl" +rescue LoadError +end + +module Net + + # + # Net::IMAP implements Internet Message Access Protocol (IMAP) client + # functionality. The protocol is described in [IMAP]. + # + # == IMAP Overview + # + # An IMAP client connects to a server, and then authenticates + # itself using either #authenticate() or #login(). Having + # authenticated itself, there is a range of commands + # available to it. Most work with mailboxes, which may be + # arranged in an hierarchical namespace, and each of which + # contains zero or more messages. How this is implemented on + # the server is implementation-dependent; on a UNIX server, it + # will frequently be implemented as a files in mailbox format + # within a hierarchy of directories. + # + # To work on the messages within a mailbox, the client must + # first select that mailbox, using either #select() or (for + # read-only access) #examine(). Once the client has successfully + # selected a mailbox, they enter _selected_ state, and that + # mailbox becomes the _current_ mailbox, on which mail-item + # related commands implicitly operate. + # + # Messages have two sorts of identifiers: message sequence + # numbers, and UIDs. + # + # Message sequence numbers number messages within a mail box + # from 1 up to the number of items in the mail box. If new + # message arrives during a session, it receives a sequence + # number equal to the new size of the mail box. If messages + # are expunged from the mailbox, remaining messages have their + # sequence numbers "shuffled down" to fill the gaps. + # + # UIDs, on the other hand, are permanently guaranteed not to + # identify another message within the same mailbox, even if + # the existing message is deleted. UIDs are required to + # be assigned in ascending (but not necessarily sequential) + # order within a mailbox; this means that if a non-IMAP client + # rearranges the order of mailitems within a mailbox, the + # UIDs have to be reassigned. An IMAP client cannot thus + # rearrange message orders. + # + # == Examples of Usage + # + # === List sender and subject of all recent messages in the default mailbox + # + # imap = Net::IMAP.new('mail.example.com') + # imap.authenticate('LOGIN', 'joe_user', 'joes_password') + # imap.examine('INBOX') + # imap.search(["RECENT"]).each do |message_id| + # envelope = imap.fetch(message_id, "ENVELOPE")[0].attr["ENVELOPE"] + # puts "#{envelope.from[0].name}: \t#{envelope.subject}" + # end + # + # === Move all messages from April 2003 from "Mail/sent-mail" to "Mail/sent-apr03" + # + # imap = Net::IMAP.new('mail.example.com') + # imap.authenticate('LOGIN', 'joe_user', 'joes_password') + # imap.select('Mail/sent-mail') + # if not imap.list('Mail/', 'sent-apr03') + # imap.create('Mail/sent-apr03') + # end + # imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id| + # imap.copy(message_id, "Mail/sent-apr03") + # imap.store(message_id, "+FLAGS", [:Deleted]) + # end + # imap.expunge + # + # == Thread Safety + # + # Net::IMAP supports concurrent threads. For example, + # + # imap = Net::IMAP.new("imap.foo.net", "imap2") + # imap.authenticate("cram-md5", "bar", "password") + # imap.select("inbox") + # fetch_thread = Thread.start { imap.fetch(1..-1, "UID") } + # search_result = imap.search(["BODY", "hello"]) + # fetch_result = fetch_thread.value + # imap.disconnect + # + # This script invokes the FETCH command and the SEARCH command concurrently. + # + # == Errors + # + # An IMAP server can send three different types of responses to indicate + # failure: + # + # NO:: the attempted command could not be successfully completed. For + # instance, the username/password used for logging in are incorrect; + # the selected mailbox does not exists; etc. + # + # BAD:: the request from the client does not follow the server's + # understanding of the IMAP protocol. This includes attempting + # commands from the wrong client state; for instance, attempting + # to perform a SEARCH command without having SELECTed a current + # mailbox. It can also signal an internal server + # failure (such as a disk crash) has occurred. + # + # BYE:: the server is saying goodbye. This can be part of a normal + # logout sequence, and can be used as part of a login sequence + # to indicate that the server is (for some reason) unwilling + # to accept our connection. As a response to any other command, + # it indicates either that the server is shutting down, or that + # the server is timing out the client connection due to inactivity. + # + # These three error response are represented by the errors + # Net::IMAP::NoResponseError, Net::IMAP::BadResponseError, and + # Net::IMAP::ByeResponseError, all of which are subclasses of + # Net::IMAP::ResponseError. Essentially, all methods that involve + # sending a request to the server can generate one of these errors. + # Only the most pertinent instances have been documented below. + # + # Because the IMAP class uses Sockets for communication, its methods + # are also susceptible to the various errors that can occur when + # working with sockets. These are generally represented as + # Errno errors. For instance, any method that involves sending a + # request to the server and/or receiving a response from it could + # raise an Errno::EPIPE error if the network connection unexpectedly + # goes down. See the socket(7), ip(7), tcp(7), socket(2), connect(2), + # and associated man pages. + # + # Finally, a Net::IMAP::DataFormatError is thrown if low-level data + # is found to be in an incorrect format (for instance, when converting + # between UTF-8 and UTF-16), and Net::IMAP::ResponseParseError is + # thrown if a server response is non-parseable. + # + # + # == References + # + # [[IMAP]] + # M. Crispin, "INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1", + # RFC 2060, December 1996. (Note: since obsoleted by RFC 3501) + # + # [[LANGUAGE-TAGS]] + # Alvestrand, H., "Tags for the Identification of + # Languages", RFC 1766, March 1995. + # + # [[MD5]] + # Myers, J., and M. Rose, "The Content-MD5 Header Field", RFC + # 1864, October 1995. + # + # [[MIME-IMB]] + # Freed, N., and N. Borenstein, "MIME (Multipurpose Internet + # Mail Extensions) Part One: Format of Internet Message Bodies", RFC + # 2045, November 1996. + # + # [[RFC-822]] + # Crocker, D., "Standard for the Format of ARPA Internet Text + # Messages", STD 11, RFC 822, University of Delaware, August 1982. + # + # [[RFC-2087]] + # Myers, J., "IMAP4 QUOTA extension", RFC 2087, January 1997. + # + # [[RFC-2086]] + # Myers, J., "IMAP4 ACL extension", RFC 2086, January 1997. + # + # [[RFC-2195]] + # Klensin, J., Catoe, R., and Krumviede, P., "IMAP/POP AUTHorize Extension + # for Simple Challenge/Response", RFC 2195, September 1997. + # + # [[SORT-THREAD-EXT]] + # Crispin, M., "INTERNET MESSAGE ACCESS PROTOCOL - SORT and THREAD + # Extensions", draft-ietf-imapext-sort, May 2003. + # + # [[OSSL]] + # http://www.openssl.org + # + # [[RSSL]] + # http://savannah.gnu.org/projects/rubypki + # + # [[UTF7]] + # Goldsmith, D. and Davis, M., "UTF-7: A Mail-Safe Transformation Format of + # Unicode", RFC 2152, May 1997. + # + class IMAP + include MonitorMixin + if defined?(OpenSSL::SSL) + include OpenSSL + include SSL + end + + # Returns an initial greeting response from the server. + attr_reader :greeting + + # Returns recorded untagged responses. For example: + # + # imap.select("inbox") + # p imap.responses["EXISTS"][-1] + # #=> 2 + # p imap.responses["UIDVALIDITY"][-1] + # #=> 968263756 + attr_reader :responses + + # Returns all response handlers. + attr_reader :response_handlers + + # The thread to receive exceptions. + attr_accessor :client_thread + + # Flag indicating a message has been seen + SEEN = :Seen + + # Flag indicating a message has been answered + ANSWERED = :Answered + + # Flag indicating a message has been flagged for special or urgent + # attention + FLAGGED = :Flagged + + # Flag indicating a message has been marked for deletion. This + # will occur when the mailbox is closed or expunged. + DELETED = :Deleted + + # Flag indicating a message is only a draft or work-in-progress version. + DRAFT = :Draft + + # Flag indicating that the message is "recent", meaning that this + # session is the first session in which the client has been notified + # of this message. + RECENT = :Recent + + # Flag indicating that a mailbox context name cannot contain + # children. + NOINFERIORS = :Noinferiors + + # Flag indicating that a mailbox is not selected. + NOSELECT = :Noselect + + # Flag indicating that a mailbox has been marked "interesting" by + # the server; this commonly indicates that the mailbox contains + # new messages. + MARKED = :Marked + + # Flag indicating that the mailbox does not contains new messages. + UNMARKED = :Unmarked + + # Returns the debug mode. + def self.debug + return @@debug + end + + # Sets the debug mode. + def self.debug=(val) + return @@debug = val + end + + # Returns the max number of flags interned to symbols. + def self.max_flag_count + return @@max_flag_count + end + + # Sets the max number of flags interned to symbols. + def self.max_flag_count=(count) + @@max_flag_count = count + end + + # Adds an authenticator for Net::IMAP#authenticate. +auth_type+ + # is the type of authentication this authenticator supports + # (for instance, "LOGIN"). The +authenticator+ is an object + # which defines a process() method to handle authentication with + # the server. See Net::IMAP::LoginAuthenticator, + # Net::IMAP::CramMD5Authenticator, and Net::IMAP::DigestMD5Authenticator + # for examples. + # + # + # If +auth_type+ refers to an existing authenticator, it will be + # replaced by the new one. + def self.add_authenticator(auth_type, authenticator) + @@authenticators[auth_type] = authenticator + end + + # The default port for IMAP connections, port 143 + def self.default_port + return PORT + end + + # The default port for IMAPS connections, port 993 + def self.default_tls_port + return SSL_PORT + end + + class << self + alias default_imap_port default_port + alias default_imaps_port default_tls_port + alias default_ssl_port default_tls_port + end + + # Disconnects from the server. + def disconnect + begin + begin + # try to call SSL::SSLSocket#io. + @sock.io.shutdown + rescue NoMethodError + # @sock is not an SSL::SSLSocket. + @sock.shutdown + end + rescue Errno::ENOTCONN + # ignore `Errno::ENOTCONN: Socket is not connected' on some platforms. + rescue Exception => e + @receiver_thread.raise(e) + end + @receiver_thread.join + synchronize do + unless @sock.closed? + @sock.close + end + end + raise e if e + end + + # Returns true if disconnected from the server. + def disconnected? + return @sock.closed? + end + + # Sends a CAPABILITY command, and returns an array of + # capabilities that the server supports. Each capability + # is a string. See [IMAP] for a list of possible + # capabilities. + # + # Note that the Net::IMAP class does not modify its + # behaviour according to the capabilities of the server; + # it is up to the user of the class to ensure that + # a certain capability is supported by a server before + # using it. + def capability + synchronize do + send_command("CAPABILITY") + return @responses.delete("CAPABILITY")[-1] + end + end + + # Sends a NOOP command to the server. It does nothing. + def noop + send_command("NOOP") + end + + # Sends a LOGOUT command to inform the server that the client is + # done with the connection. + def logout + send_command("LOGOUT") + end + + # Sends a STARTTLS command to start TLS session. + def starttls(options = {}, verify = true) + send_command("STARTTLS") do |resp| + if resp.kind_of?(TaggedResponse) && resp.name == "OK" + begin + # for backward compatibility + certs = options.to_str + options = create_ssl_params(certs, verify) + rescue NoMethodError + end + start_tls_session(options) + end + end + end + + # Sends an AUTHENTICATE command to authenticate the client. + # The +auth_type+ parameter is a string that represents + # the authentication mechanism to be used. Currently Net::IMAP + # supports authentication mechanisms: + # + # LOGIN:: login using cleartext user and password. + # CRAM-MD5:: login with cleartext user and encrypted password + # (see [RFC-2195] for a full description). This + # mechanism requires that the server have the user's + # password stored in clear-text password. + # + # For both these mechanisms, there should be two +args+: username + # and (cleartext) password. A server may not support one or other + # of these mechanisms; check #capability() for a capability of + # the form "AUTH=LOGIN" or "AUTH=CRAM-MD5". + # + # Authentication is done using the appropriate authenticator object: + # see @@authenticators for more information on plugging in your own + # authenticator. + # + # For example: + # + # imap.authenticate('LOGIN', user, password) + # + # A Net::IMAP::NoResponseError is raised if authentication fails. + def authenticate(auth_type, *args) + auth_type = auth_type.upcase + unless @@authenticators.has_key?(auth_type) + raise ArgumentError, + format('unknown auth type - "%s"', auth_type) + end + authenticator = @@authenticators[auth_type].new(*args) + send_command("AUTHENTICATE", auth_type) do |resp| + if resp.instance_of?(ContinuationRequest) + data = authenticator.process(resp.data.text.unpack("m")[0]) + s = [data].pack("m").gsub(/\n/, "") + send_string_data(s) + put_string(CRLF) + end + end + end + + # Sends a LOGIN command to identify the client and carries + # the plaintext +password+ authenticating this +user+. Note + # that, unlike calling #authenticate() with an +auth_type+ + # of "LOGIN", #login() does *not* use the login authenticator. + # + # A Net::IMAP::NoResponseError is raised if authentication fails. + def login(user, password) + send_command("LOGIN", user, password) + end + + # Sends a SELECT command to select a +mailbox+ so that messages + # in the +mailbox+ can be accessed. + # + # After you have selected a mailbox, you may retrieve the + # number of items in that mailbox from @responses["EXISTS"][-1], + # and the number of recent messages from @responses["RECENT"][-1]. + # Note that these values can change if new messages arrive + # during a session; see #add_response_handler() for a way of + # detecting this event. + # + # A Net::IMAP::NoResponseError is raised if the mailbox does not + # exist or is for some reason non-selectable. + def select(mailbox) + synchronize do + @responses.clear + send_command("SELECT", mailbox) + end + end + + # Sends a EXAMINE command to select a +mailbox+ so that messages + # in the +mailbox+ can be accessed. Behaves the same as #select(), + # except that the selected +mailbox+ is identified as read-only. + # + # A Net::IMAP::NoResponseError is raised if the mailbox does not + # exist or is for some reason non-examinable. + def examine(mailbox) + synchronize do + @responses.clear + send_command("EXAMINE", mailbox) + end + end + + # Sends a CREATE command to create a new +mailbox+. + # + # A Net::IMAP::NoResponseError is raised if a mailbox with that name + # cannot be created. + def create(mailbox) + send_command("CREATE", mailbox) + end + + # Sends a DELETE command to remove the +mailbox+. + # + # A Net::IMAP::NoResponseError is raised if a mailbox with that name + # cannot be deleted, either because it does not exist or because the + # client does not have permission to delete it. + def delete(mailbox) + send_command("DELETE", mailbox) + end + + # Sends a RENAME command to change the name of the +mailbox+ to + # +newname+. + # + # A Net::IMAP::NoResponseError is raised if a mailbox with the + # name +mailbox+ cannot be renamed to +newname+ for whatever + # reason; for instance, because +mailbox+ does not exist, or + # because there is already a mailbox with the name +newname+. + def rename(mailbox, newname) + send_command("RENAME", mailbox, newname) + end + + # Sends a SUBSCRIBE command to add the specified +mailbox+ name to + # the server's set of "active" or "subscribed" mailboxes as returned + # by #lsub(). + # + # A Net::IMAP::NoResponseError is raised if +mailbox+ cannot be + # subscribed to, for instance because it does not exist. + def subscribe(mailbox) + send_command("SUBSCRIBE", mailbox) + end + + # Sends a UNSUBSCRIBE command to remove the specified +mailbox+ name + # from the server's set of "active" or "subscribed" mailboxes. + # + # A Net::IMAP::NoResponseError is raised if +mailbox+ cannot be + # unsubscribed from, for instance because the client is not currently + # subscribed to it. + def unsubscribe(mailbox) + send_command("UNSUBSCRIBE", mailbox) + end + + # Sends a LIST command, and returns a subset of names from + # the complete set of all names available to the client. + # +refname+ provides a context (for instance, a base directory + # in a directory-based mailbox hierarchy). +mailbox+ specifies + # a mailbox or (via wildcards) mailboxes under that context. + # Two wildcards may be used in +mailbox+: '*', which matches + # all characters *including* the hierarchy delimiter (for instance, + # '/' on a UNIX-hosted directory-based mailbox hierarchy); and '%', + # which matches all characters *except* the hierarchy delimiter. + # + # If +refname+ is empty, +mailbox+ is used directly to determine + # which mailboxes to match. If +mailbox+ is empty, the root + # name of +refname+ and the hierarchy delimiter are returned. + # + # The return value is an array of +Net::IMAP::MailboxList+. For example: + # + # imap.create("foo/bar") + # imap.create("foo/baz") + # p imap.list("", "foo/%") + # #=> [#, \\ + # #, \\ + # #] + def list(refname, mailbox) + synchronize do + send_command("LIST", refname, mailbox) + return @responses.delete("LIST") + end + end + + # Sends a XLIST command, and returns a subset of names from + # the complete set of all names available to the client. + # +refname+ provides a context (for instance, a base directory + # in a directory-based mailbox hierarchy). +mailbox+ specifies + # a mailbox or (via wildcards) mailboxes under that context. + # Two wildcards may be used in +mailbox+: '*', which matches + # all characters *including* the hierarchy delimiter (for instance, + # '/' on a UNIX-hosted directory-based mailbox hierarchy); and '%', + # which matches all characters *except* the hierarchy delimiter. + # + # If +refname+ is empty, +mailbox+ is used directly to determine + # which mailboxes to match. If +mailbox+ is empty, the root + # name of +refname+ and the hierarchy delimiter are returned. + # + # The XLIST command is like the LIST command except that the flags + # returned refer to the function of the folder/mailbox, e.g. :Sent + # + # The return value is an array of +Net::IMAP::MailboxList+. For example: + # + # imap.create("foo/bar") + # imap.create("foo/baz") + # p imap.xlist("", "foo/%") + # #=> [#, \\ + # #, \\ + # #] + def xlist(refname, mailbox) + synchronize do + send_command("XLIST", refname, mailbox) + return @responses.delete("XLIST") + end + end + + # Sends the GETQUOTAROOT command along with specified +mailbox+. + # This command is generally available to both admin and user. + # If mailbox exists, returns an array containing objects of + # Net::IMAP::MailboxQuotaRoot and Net::IMAP::MailboxQuota. + def getquotaroot(mailbox) + synchronize do + send_command("GETQUOTAROOT", mailbox) + result = [] + result.concat(@responses.delete("QUOTAROOT")) + result.concat(@responses.delete("QUOTA")) + return result + end + end + + # Sends the GETQUOTA command along with specified +mailbox+. + # If this mailbox exists, then an array containing a + # Net::IMAP::MailboxQuota object is returned. This + # command generally is only available to server admin. + def getquota(mailbox) + synchronize do + send_command("GETQUOTA", mailbox) + return @responses.delete("QUOTA") + end + end + + # Sends a SETQUOTA command along with the specified +mailbox+ and + # +quota+. If +quota+ is nil, then quota will be unset for that + # mailbox. Typically one needs to be logged in as server admin + # for this to work. The IMAP quota commands are described in + # [RFC-2087]. + def setquota(mailbox, quota) + if quota.nil? + data = '()' + else + data = '(STORAGE ' + quota.to_s + ')' + end + send_command("SETQUOTA", mailbox, RawData.new(data)) + end + + # Sends the SETACL command along with +mailbox+, +user+ and the + # +rights+ that user is to have on that mailbox. If +rights+ is nil, + # then that user will be stripped of any rights to that mailbox. + # The IMAP ACL commands are described in [RFC-2086]. + def setacl(mailbox, user, rights) + if rights.nil? + send_command("SETACL", mailbox, user, "") + else + send_command("SETACL", mailbox, user, rights) + end + end + + # Send the GETACL command along with specified +mailbox+. + # If this mailbox exists, an array containing objects of + # Net::IMAP::MailboxACLItem will be returned. + def getacl(mailbox) + synchronize do + send_command("GETACL", mailbox) + return @responses.delete("ACL")[-1] + end + end + + # Sends a LSUB command, and returns a subset of names from the set + # of names that the user has declared as being "active" or + # "subscribed". +refname+ and +mailbox+ are interpreted as + # for #list(). + # The return value is an array of +Net::IMAP::MailboxList+. + def lsub(refname, mailbox) + synchronize do + send_command("LSUB", refname, mailbox) + return @responses.delete("LSUB") + end + end + + # Sends a STATUS command, and returns the status of the indicated + # +mailbox+. +attr+ is a list of one or more attributes that + # we are request the status of. Supported attributes include: + # + # MESSAGES:: the number of messages in the mailbox. + # RECENT:: the number of recent messages in the mailbox. + # UNSEEN:: the number of unseen messages in the mailbox. + # + # The return value is a hash of attributes. For example: + # + # p imap.status("inbox", ["MESSAGES", "RECENT"]) + # #=> {"RECENT"=>0, "MESSAGES"=>44} + # + # A Net::IMAP::NoResponseError is raised if status values + # for +mailbox+ cannot be returned, for instance because it + # does not exist. + def status(mailbox, attr) + synchronize do + send_command("STATUS", mailbox, attr) + return @responses.delete("STATUS")[-1].attr + end + end + + # Sends a APPEND command to append the +message+ to the end of + # the +mailbox+. The optional +flags+ argument is an array of + # flags to initially passing to the new message. The optional + # +date_time+ argument specifies the creation time to assign to the + # new message; it defaults to the current time. + # For example: + # + # imap.append("inbox", <:: a set of message sequence numbers. ',' indicates + # an interval, ':' indicates a range. For instance, + # '2,10:12,15' means "2,10,11,12,15". + # + # BEFORE :: messages with an internal date strictly before + # . The date argument has a format similar + # to 8-Aug-2002. + # + # BODY :: messages that contain within their body. + # + # CC :: messages containing in their CC field. + # + # FROM :: messages that contain in their FROM field. + # + # NEW:: messages with the \Recent, but not the \Seen, flag set. + # + # NOT :: negate the following search key. + # + # OR :: "or" two search keys together. + # + # ON :: messages with an internal date exactly equal to , + # which has a format similar to 8-Aug-2002. + # + # SINCE :: messages with an internal date on or after . + # + # SUBJECT :: messages with in their subject. + # + # TO :: messages with in their TO field. + # + # For example: + # + # p imap.search(["SUBJECT", "hello", "NOT", "NEW"]) + # #=> [1, 6, 7, 8] + def search(keys, charset = nil) + return search_internal("SEARCH", keys, charset) + end + + # As for #search(), but returns unique identifiers. + def uid_search(keys, charset = nil) + return search_internal("UID SEARCH", keys, charset) + end + + # Sends a FETCH command to retrieve data associated with a message + # in the mailbox. The +set+ parameter is a number or an array of + # numbers or a Range object. The number is a message sequence + # number. +attr+ is a list of attributes to fetch; see the + # documentation for Net::IMAP::FetchData for a list of valid + # attributes. + # The return value is an array of Net::IMAP::FetchData. For example: + # + # p imap.fetch(6..8, "UID") + # #=> [#98}>, \\ + # #99}>, \\ + # #100}>] + # p imap.fetch(6, "BODY[HEADER.FIELDS (SUBJECT)]") + # #=> [#"Subject: test\r\n\r\n"}>] + # data = imap.uid_fetch(98, ["RFC822.SIZE", "INTERNALDATE"])[0] + # p data.seqno + # #=> 6 + # p data.attr["RFC822.SIZE"] + # #=> 611 + # p data.attr["INTERNALDATE"] + # #=> "12-Oct-2000 22:40:59 +0900" + # p data.attr["UID"] + # #=> 98 + def fetch(set, attr) + return fetch_internal("FETCH", set, attr) + end + + # As for #fetch(), but +set+ contains unique identifiers. + def uid_fetch(set, attr) + return fetch_internal("UID FETCH", set, attr) + end + + # Sends a STORE command to alter data associated with messages + # in the mailbox, in particular their flags. The +set+ parameter + # is a number or an array of numbers or a Range object. Each number + # is a message sequence number. +attr+ is the name of a data item + # to store: 'FLAGS' means to replace the message's flag list + # with the provided one; '+FLAGS' means to add the provided flags; + # and '-FLAGS' means to remove them. +flags+ is a list of flags. + # + # The return value is an array of Net::IMAP::FetchData. For example: + # + # p imap.store(6..8, "+FLAGS", [:Deleted]) + # #=> [#[:Seen, :Deleted]}>, \\ + # #[:Seen, :Deleted]}>, \\ + # #[:Seen, :Deleted]}>] + def store(set, attr, flags) + return store_internal("STORE", set, attr, flags) + end + + # As for #store(), but +set+ contains unique identifiers. + def uid_store(set, attr, flags) + return store_internal("UID STORE", set, attr, flags) + end + + # Sends a COPY command to copy the specified message(s) to the end + # of the specified destination +mailbox+. The +set+ parameter is + # a number or an array of numbers or a Range object. The number is + # a message sequence number. + def copy(set, mailbox) + copy_internal("COPY", set, mailbox) + end + + # As for #copy(), but +set+ contains unique identifiers. + def uid_copy(set, mailbox) + copy_internal("UID COPY", set, mailbox) + end + + # Sends a SORT command to sort messages in the mailbox. + # Returns an array of message sequence numbers. For example: + # + # p imap.sort(["FROM"], ["ALL"], "US-ASCII") + # #=> [1, 2, 3, 5, 6, 7, 8, 4, 9] + # p imap.sort(["DATE"], ["SUBJECT", "hello"], "US-ASCII") + # #=> [6, 7, 8, 1] + # + # See [SORT-THREAD-EXT] for more details. + def sort(sort_keys, search_keys, charset) + return sort_internal("SORT", sort_keys, search_keys, charset) + end + + # As for #sort(), but returns an array of unique identifiers. + def uid_sort(sort_keys, search_keys, charset) + return sort_internal("UID SORT", sort_keys, search_keys, charset) + end + + # Adds a response handler. For example, to detect when + # the server sends us a new EXISTS response (which normally + # indicates new messages being added to the mail box), + # you could add the following handler after selecting the + # mailbox. + # + # imap.add_response_handler { |resp| + # if resp.kind_of?(Net::IMAP::UntaggedResponse) and resp.name == "EXISTS" + # puts "Mailbox now has #{resp.data} messages" + # end + # } + # + def add_response_handler(handler = Proc.new) + @response_handlers.push(handler) + end + + # Removes the response handler. + def remove_response_handler(handler) + @response_handlers.delete(handler) + end + + # As for #search(), but returns message sequence numbers in threaded + # format, as a Net::IMAP::ThreadMember tree. The supported algorithms + # are: + # + # ORDEREDSUBJECT:: split into single-level threads according to subject, + # ordered by date. + # REFERENCES:: split into threads by parent/child relationships determined + # by which message is a reply to which. + # + # Unlike #search(), +charset+ is a required argument. US-ASCII + # and UTF-8 are sample values. + # + # See [SORT-THREAD-EXT] for more details. + def thread(algorithm, search_keys, charset) + return thread_internal("THREAD", algorithm, search_keys, charset) + end + + # As for #thread(), but returns unique identifiers instead of + # message sequence numbers. + def uid_thread(algorithm, search_keys, charset) + return thread_internal("UID THREAD", algorithm, search_keys, charset) + end + + # Sends an IDLE command that waits for notifications of new or expunged + # messages. Yields responses from the server during the IDLE. + # + # Use #idle_done() to leave IDLE. + def idle(&response_handler) + raise LocalJumpError, "no block given" unless response_handler + + response = nil + + synchronize do + tag = Thread.current[:net_imap_tag] = generate_tag + put_string("#{tag} IDLE#{CRLF}") + + begin + add_response_handler(response_handler) + @idle_done_cond = new_cond + @idle_done_cond.wait + @idle_done_cond = nil + if @receiver_thread_terminating + raise Net::IMAP::Error, "connection closed" + end + ensure + unless @receiver_thread_terminating + remove_response_handler(response_handler) + put_string("DONE#{CRLF}") + response = get_tagged_response(tag, "IDLE") + end + end + end + + return response + end + + # Leaves IDLE. + def idle_done + synchronize do + if @idle_done_cond.nil? + raise Net::IMAP::Error, "not during IDLE" + end + @idle_done_cond.signal + end + end + + # Decode a string from modified UTF-7 format to UTF-8. + # + # UTF-7 is a 7-bit encoding of Unicode [UTF7]. IMAP uses a + # slightly modified version of this to encode mailbox names + # containing non-ASCII characters; see [IMAP] section 5.1.3. + # + # Net::IMAP does _not_ automatically encode and decode + # mailbox names to and from utf7. + def self.decode_utf7(s) + return s.gsub(/&([^-]+)?-/n) { + if $1 + ($1.tr(",", "/") + "===").unpack("m")[0].encode(Encoding::UTF_8, Encoding::UTF_16BE) + else + "&" + end + } + end + + # Encode a string from UTF-8 format to modified UTF-7. + def self.encode_utf7(s) + return s.gsub(/(&)|[^\x20-\x7e]+/) { + if $1 + "&-" + else + base64 = [$&.encode(Encoding::UTF_16BE)].pack("m") + "&" + base64.delete("=\n").tr("/", ",") + "-" + end + }.force_encoding("ASCII-8BIT") + end + + # Formats +time+ as an IMAP-style date. + def self.format_date(time) + return time.strftime('%d-%b-%Y') + end + + # Formats +time+ as an IMAP-style date-time. + def self.format_datetime(time) + return time.strftime('%d-%b-%Y %H:%M %z') + end + + private + + CRLF = "\r\n" # :nodoc: + PORT = 143 # :nodoc: + SSL_PORT = 993 # :nodoc: + + @@debug = false + @@authenticators = {} + @@max_flag_count = 10000 + + # :call-seq: + # Net::IMAP.new(host, options = {}) + # + # Creates a new Net::IMAP object and connects it to the specified + # +host+. + # + # +options+ is an option hash, each key of which is a symbol. + # + # The available options are: + # + # port:: port number (default value is 143 for imap, or 993 for imaps) + # ssl:: if options[:ssl] is true, then an attempt will be made + # to use SSL (now TLS) to connect to the server. For this to work + # OpenSSL [OSSL] and the Ruby OpenSSL [RSSL] extensions need to + # be installed. + # if options[:ssl] is a hash, it's passed to + # OpenSSL::SSL::SSLContext#set_params as parameters. + # + # The most common errors are: + # + # Errno::ECONNREFUSED:: connection refused by +host+ or an intervening + # firewall. + # Errno::ETIMEDOUT:: connection timed out (possibly due to packets + # being dropped by an intervening firewall). + # Errno::ENETUNREACH:: there is no route to that network. + # SocketError:: hostname not known or other socket error. + # Net::IMAP::ByeResponseError:: we connected to the host, but they + # immediately said goodbye to us. + def initialize(host, port_or_options = {}, + usessl = false, certs = nil, verify = true) + super() + @host = host + begin + options = port_or_options.to_hash + rescue NoMethodError + # for backward compatibility + options = {} + options[:port] = port_or_options + if usessl + options[:ssl] = create_ssl_params(certs, verify) + end + end + @port = options[:port] || (options[:ssl] ? SSL_PORT : PORT) + @tag_prefix = "RUBY" + @tagno = 0 + @parser = ResponseParser.new + @sock = TCPSocket.open(@host, @port) + if options[:ssl] + start_tls_session(options[:ssl]) + @usessl = true + else + @usessl = false + end + @responses = Hash.new([].freeze) + @tagged_responses = {} + @response_handlers = [] + @tagged_response_arrival = new_cond + @continuation_request_arrival = new_cond + @idle_done_cond = nil + @logout_command_tag = nil + @debug_output_bol = true + @exception = nil + + @greeting = get_response + if @greeting.nil? + @sock.close + raise Error, "connection closed" + end + if @greeting.name == "BYE" + @sock.close + raise ByeResponseError, @greeting + end + + @client_thread = Thread.current + @receiver_thread = Thread.start { + begin + receive_responses + rescue Exception + end + } + @receiver_thread_terminating = false + end + + def receive_responses + connection_closed = false + until connection_closed + synchronize do + @exception = nil + end + begin + resp = get_response + rescue Exception => e + synchronize do + @sock.close + @exception = e + end + break + end + unless resp + synchronize do + @exception = EOFError.new("end of file reached") + end + break + end + begin + synchronize do + case resp + when TaggedResponse + @tagged_responses[resp.tag] = resp + @tagged_response_arrival.broadcast + if resp.tag == @logout_command_tag + return + end + when UntaggedResponse + record_response(resp.name, resp.data) + if resp.data.instance_of?(ResponseText) && + (code = resp.data.code) + record_response(code.name, code.data) + end + if resp.name == "BYE" && @logout_command_tag.nil? + @sock.close + @exception = ByeResponseError.new(resp) + connection_closed = true + end + when ContinuationRequest + @continuation_request_arrival.signal + end + @response_handlers.each do |handler| + handler.call(resp) + end + end + rescue Exception => e + @exception = e + synchronize do + @tagged_response_arrival.broadcast + @continuation_request_arrival.broadcast + end + end + end + synchronize do + @receiver_thread_terminating = true + @tagged_response_arrival.broadcast + @continuation_request_arrival.broadcast + if @idle_done_cond + @idle_done_cond.signal + end + end + end + + def get_tagged_response(tag, cmd) + until @tagged_responses.key?(tag) + raise @exception if @exception + @tagged_response_arrival.wait + end + resp = @tagged_responses.delete(tag) + case resp.name + when /\A(?:NO)\z/ni + raise NoResponseError, resp + when /\A(?:BAD)\z/ni + raise BadResponseError, resp + else + return resp + end + end + + def get_response + buff = "" + while true + s = @sock.gets(CRLF) + break unless s + buff.concat(s) + if /\{(\d+)\}\r\n/n =~ s + s = @sock.read($1.to_i) + buff.concat(s) + else + break + end + end + return nil if buff.length == 0 + if @@debug + $stderr.print(buff.gsub(/^/n, "S: ")) + end + return @parser.parse(buff) + end + + def record_response(name, data) + unless @responses.has_key?(name) + @responses[name] = [] + end + @responses[name].push(data) + end + + def send_command(cmd, *args, &block) + synchronize do + args.each do |i| + validate_data(i) + end + tag = generate_tag + put_string(tag + " " + cmd) + args.each do |i| + put_string(" ") + send_data(i) + end + put_string(CRLF) + if cmd == "LOGOUT" + @logout_command_tag = tag + end + if block + add_response_handler(block) + end + begin + return get_tagged_response(tag, cmd) + ensure + if block + remove_response_handler(block) + end + end + end + end + + def generate_tag + @tagno += 1 + return format("%s%04d", @tag_prefix, @tagno) + end + + def put_string(str) + @sock.print(str) + if @@debug + if @debug_output_bol + $stderr.print("C: ") + end + $stderr.print(str.gsub(/\n(?!\z)/n, "\nC: ")) + if /\r\n\z/n.match(str) + @debug_output_bol = true + else + @debug_output_bol = false + end + end + end + + def validate_data(data) + case data + when nil + when String + when Integer + if data < 0 || data >= 4294967296 + raise DataFormatError, num.to_s + end + when Array + data.each do |i| + validate_data(i) + end + when Time + when Symbol + else + data.validate + end + end + + def send_data(data) + case data + when nil + put_string("NIL") + when String + send_string_data(data) + when Integer + send_number_data(data) + when Array + send_list_data(data) + when Time + send_time_data(data) + when Symbol + send_symbol_data(data) + else + data.send_data(self) + end + end + + def send_string_data(str) + case str + when "" + put_string('""') + when /[\x80-\xff\r\n]/n + # literal + send_literal(str) + when /[(){ \x00-\x1f\x7f%*"\\]/n + # quoted string + send_quoted_string(str) + else + put_string(str) + end + end + + def send_quoted_string(str) + put_string('"' + str.gsub(/["\\]/n, "\\\\\\&") + '"') + end + + def send_literal(str) + put_string("{" + str.bytesize.to_s + "}" + CRLF) + @continuation_request_arrival.wait + raise @exception if @exception + put_string(str) + end + + def send_number_data(num) + put_string(num.to_s) + end + + def send_list_data(list) + put_string("(") + first = true + list.each do |i| + if first + first = false + else + put_string(" ") + end + send_data(i) + end + put_string(")") + end + + DATE_MONTH = %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) + + def send_time_data(time) + t = time.dup.gmtime + s = format('"%2d-%3s-%4d %02d:%02d:%02d +0000"', + t.day, DATE_MONTH[t.month - 1], t.year, + t.hour, t.min, t.sec) + put_string(s) + end + + def send_symbol_data(symbol) + put_string("\\" + symbol.to_s) + end + + def search_internal(cmd, keys, charset) + if keys.instance_of?(String) + keys = [RawData.new(keys)] + else + normalize_searching_criteria(keys) + end + synchronize do + if charset + send_command(cmd, "CHARSET", charset, *keys) + else + send_command(cmd, *keys) + end + return @responses.delete("SEARCH")[-1] + end + end + + def fetch_internal(cmd, set, attr) + case attr + when String then + attr = RawData.new(attr) + when Array then + attr = attr.map { |arg| + arg.is_a?(String) ? RawData.new(arg) : arg + } + end + + synchronize do + @responses.delete("FETCH") + send_command(cmd, MessageSet.new(set), attr) + return @responses.delete("FETCH") + end + end + + def store_internal(cmd, set, attr, flags) + if attr.instance_of?(String) + attr = RawData.new(attr) + end + synchronize do + @responses.delete("FETCH") + send_command(cmd, MessageSet.new(set), attr, flags) + return @responses.delete("FETCH") + end + end + + def copy_internal(cmd, set, mailbox) + send_command(cmd, MessageSet.new(set), mailbox) + end + + def sort_internal(cmd, sort_keys, search_keys, charset) + if search_keys.instance_of?(String) + search_keys = [RawData.new(search_keys)] + else + normalize_searching_criteria(search_keys) + end + normalize_searching_criteria(search_keys) + synchronize do + send_command(cmd, sort_keys, charset, *search_keys) + return @responses.delete("SORT")[-1] + end + end + + def thread_internal(cmd, algorithm, search_keys, charset) + if search_keys.instance_of?(String) + search_keys = [RawData.new(search_keys)] + else + normalize_searching_criteria(search_keys) + end + normalize_searching_criteria(search_keys) + send_command(cmd, algorithm, charset, *search_keys) + return @responses.delete("THREAD")[-1] + end + + def normalize_searching_criteria(keys) + keys.collect! do |i| + case i + when -1, Range, Array + MessageSet.new(i) + else + i + end + end + end + + def create_ssl_params(certs = nil, verify = true) + params = {} + if certs + if File.file?(certs) + params[:ca_file] = certs + elsif File.directory?(certs) + params[:ca_path] = certs + end + end + if verify + params[:verify_mode] = VERIFY_PEER + else + params[:verify_mode] = VERIFY_NONE + end + return params + end + + def start_tls_session(params = {}) + unless defined?(OpenSSL::SSL) + raise "SSL extension not installed" + end + if @sock.kind_of?(OpenSSL::SSL::SSLSocket) + raise RuntimeError, "already using SSL" + end + begin + params = params.to_hash + rescue NoMethodError + params = {} + end + context = SSLContext.new + context.set_params(params) + if defined?(VerifyCallbackProc) + context.verify_callback = VerifyCallbackProc + end + @sock = SSLSocket.new(@sock, context) + @sock.sync_close = true + @sock.connect + if context.verify_mode != VERIFY_NONE + @sock.post_connection_check(@host) + end + end + + class RawData # :nodoc: + def send_data(imap) + imap.send(:put_string, @data) + end + + def validate + end + + private + + def initialize(data) + @data = data + end + end + + class Atom # :nodoc: + def send_data(imap) + imap.send(:put_string, @data) + end + + def validate + end + + private + + def initialize(data) + @data = data + end + end + + class QuotedString # :nodoc: + def send_data(imap) + imap.send(:send_quoted_string, @data) + end + + def validate + end + + private + + def initialize(data) + @data = data + end + end + + class Literal # :nodoc: + def send_data(imap) + imap.send(:send_literal, @data) + end + + def validate + end + + private + + def initialize(data) + @data = data + end + end + + class MessageSet # :nodoc: + def send_data(imap) + imap.send(:put_string, format_internal(@data)) + end + + def validate + validate_internal(@data) + end + + private + + def initialize(data) + @data = data + end + + def format_internal(data) + case data + when "*" + return data + when Integer + if data == -1 + return "*" + else + return data.to_s + end + when Range + return format_internal(data.first) + + ":" + format_internal(data.last) + when Array + return data.collect {|i| format_internal(i)}.join(",") + when ThreadMember + return data.seqno.to_s + + ":" + data.children.collect {|i| format_internal(i).join(",")} + end + end + + def validate_internal(data) + case data + when "*" + when Integer + ensure_nz_number(data) + when Range + when Array + data.each do |i| + validate_internal(i) + end + when ThreadMember + data.children.each do |i| + validate_internal(i) + end + else + raise DataFormatError, data.inspect + end + end + + def ensure_nz_number(num) + if num < -1 || num == 0 || num >= 4294967296 + msg = "nz_number must be non-zero unsigned 32-bit integer: " + + num.inspect + raise DataFormatError, msg + end + end + end + + # Net::IMAP::ContinuationRequest represents command continuation requests. + # + # The command continuation request response is indicated by a "+" token + # instead of a tag. This form of response indicates that the server is + # ready to accept the continuation of a command from the client. The + # remainder of this response is a line of text. + # + # continue_req ::= "+" SPACE (resp_text / base64) + # + # ==== Fields: + # + # data:: Returns the data (Net::IMAP::ResponseText). + # + # raw_data:: Returns the raw data string. + ContinuationRequest = Struct.new(:data, :raw_data) + + # Net::IMAP::UntaggedResponse represents untagged responses. + # + # Data transmitted by the server to the client and status responses + # that do not indicate command completion are prefixed with the token + # "*", and are called untagged responses. + # + # response_data ::= "*" SPACE (resp_cond_state / resp_cond_bye / + # mailbox_data / message_data / capability_data) + # + # ==== Fields: + # + # name:: Returns the name such as "FLAGS", "LIST", "FETCH".... + # + # data:: Returns the data such as an array of flag symbols, + # a (()) object.... + # + # raw_data:: Returns the raw data string. + UntaggedResponse = Struct.new(:name, :data, :raw_data) + + # Net::IMAP::TaggedResponse represents tagged responses. + # + # The server completion result response indicates the success or + # failure of the operation. It is tagged with the same tag as the + # client command which began the operation. + # + # response_tagged ::= tag SPACE resp_cond_state CRLF + # + # tag ::= 1* + # + # resp_cond_state ::= ("OK" / "NO" / "BAD") SPACE resp_text + # + # ==== Fields: + # + # tag:: Returns the tag. + # + # name:: Returns the name. the name is one of "OK", "NO", "BAD". + # + # data:: Returns the data. See (()). + # + # raw_data:: Returns the raw data string. + # + TaggedResponse = Struct.new(:tag, :name, :data, :raw_data) + + # Net::IMAP::ResponseText represents texts of responses. + # The text may be prefixed by the response code. + # + # resp_text ::= ["[" resp_text_code "]" SPACE] (text_mime2 / text) + # ;; text SHOULD NOT begin with "[" or "=" + # + # ==== Fields: + # + # code:: Returns the response code. See (()). + # + # text:: Returns the text. + # + ResponseText = Struct.new(:code, :text) + + # + # Net::IMAP::ResponseCode represents response codes. + # + # resp_text_code ::= "ALERT" / "PARSE" / + # "PERMANENTFLAGS" SPACE "(" #(flag / "\*") ")" / + # "READ-ONLY" / "READ-WRITE" / "TRYCREATE" / + # "UIDVALIDITY" SPACE nz_number / + # "UNSEEN" SPACE nz_number / + # atom [SPACE 1*] + # + # ==== Fields: + # + # name:: Returns the name such as "ALERT", "PERMANENTFLAGS", "UIDVALIDITY".... + # + # data:: Returns the data if it exists. + # + ResponseCode = Struct.new(:name, :data) + + # Net::IMAP::MailboxList represents contents of the LIST response. + # + # mailbox_list ::= "(" #("\Marked" / "\Noinferiors" / + # "\Noselect" / "\Unmarked" / flag_extension) ")" + # SPACE (<"> QUOTED_CHAR <"> / nil) SPACE mailbox + # + # ==== Fields: + # + # attr:: Returns the name attributes. Each name attribute is a symbol + # capitalized by String#capitalize, such as :Noselect (not :NoSelect). + # + # delim:: Returns the hierarchy delimiter + # + # name:: Returns the mailbox name. + # + MailboxList = Struct.new(:attr, :delim, :name) + + # Net::IMAP::MailboxQuota represents contents of GETQUOTA response. + # This object can also be a response to GETQUOTAROOT. In the syntax + # specification below, the delimiter used with the "#" construct is a + # single space (SPACE). + # + # quota_list ::= "(" #quota_resource ")" + # + # quota_resource ::= atom SPACE number SPACE number + # + # quota_response ::= "QUOTA" SPACE astring SPACE quota_list + # + # ==== Fields: + # + # mailbox:: The mailbox with the associated quota. + # + # usage:: Current storage usage of mailbox. + # + # quota:: Quota limit imposed on mailbox. + # + MailboxQuota = Struct.new(:mailbox, :usage, :quota) + + # Net::IMAP::MailboxQuotaRoot represents part of the GETQUOTAROOT + # response. (GETQUOTAROOT can also return Net::IMAP::MailboxQuota.) + # + # quotaroot_response ::= "QUOTAROOT" SPACE astring *(SPACE astring) + # + # ==== Fields: + # + # mailbox:: The mailbox with the associated quota. + # + # quotaroots:: Zero or more quotaroots that effect the quota on the + # specified mailbox. + # + MailboxQuotaRoot = Struct.new(:mailbox, :quotaroots) + + # Net::IMAP::MailboxACLItem represents response from GETACL. + # + # acl_data ::= "ACL" SPACE mailbox *(SPACE identifier SPACE rights) + # + # identifier ::= astring + # + # rights ::= astring + # + # ==== Fields: + # + # user:: Login name that has certain rights to the mailbox + # that was specified with the getacl command. + # + # rights:: The access rights the indicated user has to the + # mailbox. + # + MailboxACLItem = Struct.new(:user, :rights, :mailbox) + + # Net::IMAP::StatusData represents contents of the STATUS response. + # + # ==== Fields: + # + # mailbox:: Returns the mailbox name. + # + # attr:: Returns a hash. Each key is one of "MESSAGES", "RECENT", "UIDNEXT", + # "UIDVALIDITY", "UNSEEN". Each value is a number. + # + StatusData = Struct.new(:mailbox, :attr) + + # Net::IMAP::FetchData represents contents of the FETCH response. + # + # ==== Fields: + # + # seqno:: Returns the message sequence number. + # (Note: not the unique identifier, even for the UID command response.) + # + # attr:: Returns a hash. Each key is a data item name, and each value is + # its value. + # + # The current data items are: + # + # [BODY] + # A form of BODYSTRUCTURE without extension data. + # [BODY[
]<>] + # A string expressing the body contents of the specified section. + # [BODYSTRUCTURE] + # An object that describes the [MIME-IMB] body structure of a message. + # See Net::IMAP::BodyTypeBasic, Net::IMAP::BodyTypeText, + # Net::IMAP::BodyTypeMessage, Net::IMAP::BodyTypeMultipart. + # [ENVELOPE] + # A Net::IMAP::Envelope object that describes the envelope + # structure of a message. + # [FLAGS] + # A array of flag symbols that are set for this message. flag symbols + # are capitalized by String#capitalize. + # [INTERNALDATE] + # A string representing the internal date of the message. + # [RFC822] + # Equivalent to BODY[]. + # [RFC822.HEADER] + # Equivalent to BODY.PEEK[HEADER]. + # [RFC822.SIZE] + # A number expressing the [RFC-822] size of the message. + # [RFC822.TEXT] + # Equivalent to BODY[TEXT]. + # [UID] + # A number expressing the unique identifier of the message. + # + FetchData = Struct.new(:seqno, :attr) + + # Net::IMAP::Envelope represents envelope structures of messages. + # + # ==== Fields: + # + # date:: Returns a string that represents the date. + # + # subject:: Returns a string that represents the subject. + # + # from:: Returns an array of Net::IMAP::Address that represents the from. + # + # sender:: Returns an array of Net::IMAP::Address that represents the sender. + # + # reply_to:: Returns an array of Net::IMAP::Address that represents the reply-to. + # + # to:: Returns an array of Net::IMAP::Address that represents the to. + # + # cc:: Returns an array of Net::IMAP::Address that represents the cc. + # + # bcc:: Returns an array of Net::IMAP::Address that represents the bcc. + # + # in_reply_to:: Returns a string that represents the in-reply-to. + # + # message_id:: Returns a string that represents the message-id. + # + Envelope = Struct.new(:date, :subject, :from, :sender, :reply_to, + :to, :cc, :bcc, :in_reply_to, :message_id) + + # + # Net::IMAP::Address represents electronic mail addresses. + # + # ==== Fields: + # + # name:: Returns the phrase from [RFC-822] mailbox. + # + # route:: Returns the route from [RFC-822] route-addr. + # + # mailbox:: nil indicates end of [RFC-822] group. + # If non-nil and host is nil, returns [RFC-822] group name. + # Otherwise, returns [RFC-822] local-part + # + # host:: nil indicates [RFC-822] group syntax. + # Otherwise, returns [RFC-822] domain name. + # + Address = Struct.new(:name, :route, :mailbox, :host) + + # + # Net::IMAP::ContentDisposition represents Content-Disposition fields. + # + # ==== Fields: + # + # dsp_type:: Returns the disposition type. + # + # param:: Returns a hash that represents parameters of the Content-Disposition + # field. + # + ContentDisposition = Struct.new(:dsp_type, :param) + + # Net::IMAP::ThreadMember represents a thread-node returned + # by Net::IMAP#thread + # + # ==== Fields: + # + # seqno:: The sequence number of this message. + # + # children:: an array of Net::IMAP::ThreadMember objects for mail + # items that are children of this in the thread. + # + ThreadMember = Struct.new(:seqno, :children) + + # Net::IMAP::BodyTypeBasic represents basic body structures of messages. + # + # ==== Fields: + # + # media_type:: Returns the content media type name as defined in [MIME-IMB]. + # + # subtype:: Returns the content subtype name as defined in [MIME-IMB]. + # + # param:: Returns a hash that represents parameters as defined in [MIME-IMB]. + # + # content_id:: Returns a string giving the content id as defined in [MIME-IMB]. + # + # description:: Returns a string giving the content description as defined in + # [MIME-IMB]. + # + # encoding:: Returns a string giving the content transfer encoding as defined in + # [MIME-IMB]. + # + # size:: Returns a number giving the size of the body in octets. + # + # md5:: Returns a string giving the body MD5 value as defined in [MD5]. + # + # disposition:: Returns a Net::IMAP::ContentDisposition object giving + # the content disposition. + # + # language:: Returns a string or an array of strings giving the body + # language value as defined in [LANGUAGE-TAGS]. + # + # extension:: Returns extension data. + # + # multipart?:: Returns false. + # + class BodyTypeBasic < Struct.new(:media_type, :subtype, + :param, :content_id, + :description, :encoding, :size, + :md5, :disposition, :language, + :extension) + def multipart? + return false + end + + # Obsolete: use +subtype+ instead. Calling this will + # generate a warning message to +stderr+, then return + # the value of +subtype+. + def media_subtype + $stderr.printf("warning: media_subtype is obsolete.\n") + $stderr.printf(" use subtype instead.\n") + return subtype + end + end + + # Net::IMAP::BodyTypeText represents TEXT body structures of messages. + # + # ==== Fields: + # + # lines:: Returns the size of the body in text lines. + # + # And Net::IMAP::BodyTypeText has all fields of Net::IMAP::BodyTypeBasic. + # + class BodyTypeText < Struct.new(:media_type, :subtype, + :param, :content_id, + :description, :encoding, :size, + :lines, + :md5, :disposition, :language, + :extension) + def multipart? + return false + end + + # Obsolete: use +subtype+ instead. Calling this will + # generate a warning message to +stderr+, then return + # the value of +subtype+. + def media_subtype + $stderr.printf("warning: media_subtype is obsolete.\n") + $stderr.printf(" use subtype instead.\n") + return subtype + end + end + + # Net::IMAP::BodyTypeMessage represents MESSAGE/RFC822 body structures of messages. + # + # ==== Fields: + # + # envelope:: Returns a Net::IMAP::Envelope giving the envelope structure. + # + # body:: Returns an object giving the body structure. + # + # And Net::IMAP::BodyTypeMessage has all methods of Net::IMAP::BodyTypeText. + # + class BodyTypeMessage < Struct.new(:media_type, :subtype, + :param, :content_id, + :description, :encoding, :size, + :envelope, :body, :lines, + :md5, :disposition, :language, + :extension) + def multipart? + return false + end + + # Obsolete: use +subtype+ instead. Calling this will + # generate a warning message to +stderr+, then return + # the value of +subtype+. + def media_subtype + $stderr.printf("warning: media_subtype is obsolete.\n") + $stderr.printf(" use subtype instead.\n") + return subtype + end + end + + # Net::IMAP::BodyTypeAttachment represents attachment body structures + # of messages. + # + # ==== Fields: + # + # media_type:: Returns the content media type name. + # + # subtype:: Returns +nil+. + # + # param:: Returns a hash that represents parameters. + # + # multipart?:: Returns false. + # + class BodyTypeAttachment < Struct.new(:media_type, :subtype, + :param) + def multipart? + return false + end + end + + # Net::IMAP::BodyTypeMultipart represents multipart body structures + # of messages. + # + # ==== Fields: + # + # media_type:: Returns the content media type name as defined in [MIME-IMB]. + # + # subtype:: Returns the content subtype name as defined in [MIME-IMB]. + # + # parts:: Returns multiple parts. + # + # param:: Returns a hash that represents parameters as defined in [MIME-IMB]. + # + # disposition:: Returns a Net::IMAP::ContentDisposition object giving + # the content disposition. + # + # language:: Returns a string or an array of strings giving the body + # language value as defined in [LANGUAGE-TAGS]. + # + # extension:: Returns extension data. + # + # multipart?:: Returns true. + # + class BodyTypeMultipart < Struct.new(:media_type, :subtype, + :parts, + :param, :disposition, :language, + :extension) + def multipart? + return true + end + + # Obsolete: use +subtype+ instead. Calling this will + # generate a warning message to +stderr+, then return + # the value of +subtype+. + def media_subtype + $stderr.printf("warning: media_subtype is obsolete.\n") + $stderr.printf(" use subtype instead.\n") + return subtype + end + end + + class BodyTypeExtension < Struct.new(:media_type, :subtype, + :params, :content_id, + :description, :encoding, :size) + def multipart? + return false + end + end + + class ResponseParser # :nodoc: + def initialize + @str = nil + @pos = nil + @lex_state = nil + @token = nil + @flag_symbols = {} + end + + def parse(str) + @str = str + @pos = 0 + @lex_state = EXPR_BEG + @token = nil + return response + end + + private + + EXPR_BEG = :EXPR_BEG + EXPR_DATA = :EXPR_DATA + EXPR_TEXT = :EXPR_TEXT + EXPR_RTEXT = :EXPR_RTEXT + EXPR_CTEXT = :EXPR_CTEXT + + T_SPACE = :SPACE + T_NIL = :NIL + T_NUMBER = :NUMBER + T_ATOM = :ATOM + T_QUOTED = :QUOTED + T_LPAR = :LPAR + T_RPAR = :RPAR + T_BSLASH = :BSLASH + T_STAR = :STAR + T_LBRA = :LBRA + T_RBRA = :RBRA + T_LITERAL = :LITERAL + T_PLUS = :PLUS + T_PERCENT = :PERCENT + T_CRLF = :CRLF + T_EOF = :EOF + T_TEXT = :TEXT + + BEG_REGEXP = /\G(?:\ +(?# 1: SPACE )( +)|\ +(?# 2: NIL )(NIL)(?=[\x80-\xff(){ \x00-\x1f\x7f%*#{'"'}\\\[\]+])|\ +(?# 3: NUMBER )(\d+)(?=[\x80-\xff(){ \x00-\x1f\x7f%*#{'"'}\\\[\]+])|\ +(?# 4: ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*#{'"'}\\\[\]+]+)|\ +(?# 5: QUOTED )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\ +(?# 6: LPAR )(\()|\ +(?# 7: RPAR )(\))|\ +(?# 8: BSLASH )(\\)|\ +(?# 9: STAR )(\*)|\ +(?# 10: LBRA )(\[)|\ +(?# 11: RBRA )(\])|\ +(?# 12: LITERAL )\{(\d+)\}\r\n|\ +(?# 13: PLUS )(\+)|\ +(?# 14: PERCENT )(%)|\ +(?# 15: CRLF )(\r\n)|\ +(?# 16: EOF )(\z))/ni + + DATA_REGEXP = /\G(?:\ +(?# 1: SPACE )( )|\ +(?# 2: NIL )(NIL)|\ +(?# 3: NUMBER )(\d+)|\ +(?# 4: QUOTED )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\ +(?# 5: LITERAL )\{(\d+)\}\r\n|\ +(?# 6: LPAR )(\()|\ +(?# 7: RPAR )(\)))/ni + + TEXT_REGEXP = /\G(?:\ +(?# 1: TEXT )([^\x00\r\n]*))/ni + + RTEXT_REGEXP = /\G(?:\ +(?# 1: LBRA )(\[)|\ +(?# 2: TEXT )([^\x00\r\n]*))/ni + + CTEXT_REGEXP = /\G(?:\ +(?# 1: TEXT )([^\x00\r\n\]]*))/ni + + Token = Struct.new(:symbol, :value) + + def response + token = lookahead + case token.symbol + when T_PLUS + result = continue_req + when T_STAR + result = response_untagged + else + result = response_tagged + end + match(T_CRLF) + match(T_EOF) + return result + end + + def continue_req + match(T_PLUS) + match(T_SPACE) + return ContinuationRequest.new(resp_text, @str) + end + + def response_untagged + match(T_STAR) + match(T_SPACE) + token = lookahead + if token.symbol == T_NUMBER + return numeric_response + elsif token.symbol == T_ATOM + case token.value + when /\A(?:OK|NO|BAD|BYE|PREAUTH)\z/ni + return response_cond + when /\A(?:FLAGS)\z/ni + return flags_response + when /\A(?:LIST|LSUB|XLIST)\z/ni + return list_response + when /\A(?:QUOTA)\z/ni + return getquota_response + when /\A(?:QUOTAROOT)\z/ni + return getquotaroot_response + when /\A(?:ACL)\z/ni + return getacl_response + when /\A(?:SEARCH|SORT)\z/ni + return search_response + when /\A(?:THREAD)\z/ni + return thread_response + when /\A(?:STATUS)\z/ni + return status_response + when /\A(?:CAPABILITY)\z/ni + return capability_response + else + return text_response + end + else + parse_error("unexpected token %s", token.symbol) + end + end + + def response_tagged + tag = atom + match(T_SPACE) + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return TaggedResponse.new(tag, name, resp_text, @str) + end + + def response_cond + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return UntaggedResponse.new(name, resp_text, @str) + end + + def numeric_response + n = number + match(T_SPACE) + token = match(T_ATOM) + name = token.value.upcase + case name + when "EXISTS", "RECENT", "EXPUNGE" + return UntaggedResponse.new(name, n, @str) + when "FETCH" + shift_token + match(T_SPACE) + data = FetchData.new(n, msg_att(n)) + return UntaggedResponse.new(name, data, @str) + end + end + + def msg_att(n) + match(T_LPAR) + attr = {} + while true + token = lookahead + case token.symbol + when T_RPAR + shift_token + break + when T_SPACE + shift_token + next + end + case token.value + when /\A(?:ENVELOPE)\z/ni + name, val = envelope_data + when /\A(?:FLAGS)\z/ni + name, val = flags_data + when /\A(?:INTERNALDATE)\z/ni + name, val = internaldate_data + when /\A(?:RFC822(?:\.HEADER|\.TEXT)?)\z/ni + name, val = rfc822_text + when /\A(?:RFC822\.SIZE)\z/ni + name, val = rfc822_size + when /\A(?:BODY(?:STRUCTURE)?)\z/ni + name, val = body_data + when /\A(?:UID)\z/ni + name, val = uid_data + else + parse_error("unknown attribute `%s' for {%d}", token.value, n) + end + attr[name] = val + end + return attr + end + + def envelope_data + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return name, envelope + end + + def envelope + @lex_state = EXPR_DATA + token = lookahead + if token.symbol == T_NIL + shift_token + result = nil + else + match(T_LPAR) + date = nstring + match(T_SPACE) + subject = nstring + match(T_SPACE) + from = address_list + match(T_SPACE) + sender = address_list + match(T_SPACE) + reply_to = address_list + match(T_SPACE) + to = address_list + match(T_SPACE) + cc = address_list + match(T_SPACE) + bcc = address_list + match(T_SPACE) + in_reply_to = nstring + match(T_SPACE) + message_id = nstring + match(T_RPAR) + result = Envelope.new(date, subject, from, sender, reply_to, + to, cc, bcc, in_reply_to, message_id) + end + @lex_state = EXPR_BEG + return result + end + + def flags_data + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return name, flag_list + end + + def internaldate_data + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + token = match(T_QUOTED) + return name, token.value + end + + def rfc822_text + token = match(T_ATOM) + name = token.value.upcase + token = lookahead + if token.symbol == T_LBRA + shift_token + match(T_RBRA) + end + match(T_SPACE) + return name, nstring + end + + def rfc822_size + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return name, number + end + + def body_data + token = match(T_ATOM) + name = token.value.upcase + token = lookahead + if token.symbol == T_SPACE + shift_token + return name, body + end + name.concat(section) + token = lookahead + if token.symbol == T_ATOM + name.concat(token.value) + shift_token + end + match(T_SPACE) + data = nstring + return name, data + end + + def body + @lex_state = EXPR_DATA + token = lookahead + if token.symbol == T_NIL + shift_token + result = nil + else + match(T_LPAR) + token = lookahead + if token.symbol == T_LPAR + result = body_type_mpart + else + result = body_type_1part + end + match(T_RPAR) + end + @lex_state = EXPR_BEG + return result + end + + def body_type_1part + token = lookahead + case token.value + when /\A(?:TEXT)\z/ni + return body_type_text + when /\A(?:MESSAGE)\z/ni + return body_type_msg + when /\A(?:ATTACHMENT)\z/ni + return body_type_attachment + when /\A(?:MIXED)\z/ni + return body_type_mixed + else + return body_type_basic + end + end + + def body_type_basic + mtype, msubtype = media_type + token = lookahead + if token.symbol == T_RPAR + return BodyTypeBasic.new(mtype, msubtype) + end + match(T_SPACE) + param, content_id, desc, enc, size = body_fields + md5, disposition, language, extension = body_ext_1part + return BodyTypeBasic.new(mtype, msubtype, + param, content_id, + desc, enc, size, + md5, disposition, language, extension) + end + + def body_type_text + mtype, msubtype = media_type + match(T_SPACE) + param, content_id, desc, enc, size = body_fields + match(T_SPACE) + lines = number + md5, disposition, language, extension = body_ext_1part + return BodyTypeText.new(mtype, msubtype, + param, content_id, + desc, enc, size, + lines, + md5, disposition, language, extension) + end + + def body_type_msg + mtype, msubtype = media_type + match(T_SPACE) + param, content_id, desc, enc, size = body_fields + + token = lookahead + if token.symbol == T_RPAR + # If this is not message/rfc822, we shouldn't apply the RFC822 + # spec to it. We should handle anything other than + # message/rfc822 using multipart extension data [rfc3501] (i.e. + # the data itself won't be returned, we would have to retrieve it + # with BODYSTRUCTURE instead of with BODY + + # Also, sometimes a message/rfc822 is included as a large + # attachment instead of having all of the other details + # (e.g. attaching a .eml file to an email) + if msubtype == "RFC822" + return BodyTypeMessage.new(mtype, msubtype, param, content_id, + desc, enc, size, nil, nil, nil, nil, + nil, nil, nil) + else + return BodyTypeExtension.new(mtype, msubtype, + param, content_id, + desc, enc, size) + end + end + + match(T_SPACE) + env = envelope + match(T_SPACE) + b = body + match(T_SPACE) + lines = number + md5, disposition, language, extension = body_ext_1part + return BodyTypeMessage.new(mtype, msubtype, + param, content_id, + desc, enc, size, + env, b, lines, + md5, disposition, language, extension) + end + + def body_type_attachment + mtype = case_insensitive_string + match(T_SPACE) + param = body_fld_param + return BodyTypeAttachment.new(mtype, nil, param) + end + + def body_type_mixed + mtype = "MULTIPART" + msubtype = case_insensitive_string + param, disposition, language, extension = body_ext_mpart + return BodyTypeBasic.new(mtype, msubtype, param, nil, nil, nil, nil, nil, disposition, language, extension) + end + + def body_type_mpart + parts = [] + while true + token = lookahead + if token.symbol == T_SPACE + shift_token + break + end + parts.push(body) + end + mtype = "MULTIPART" + msubtype = case_insensitive_string + param, disposition, language, extension = body_ext_mpart + return BodyTypeMultipart.new(mtype, msubtype, parts, + param, disposition, language, + extension) + end + + def media_type + mtype = case_insensitive_string + token = lookahead + if token.symbol != T_SPACE + return mtype, nil + end + match(T_SPACE) + msubtype = case_insensitive_string + return mtype, msubtype + end + + def body_fields + param = body_fld_param + match(T_SPACE) + content_id = nstring + match(T_SPACE) + desc = nstring + match(T_SPACE) + enc = case_insensitive_string + match(T_SPACE) + size = number + return param, content_id, desc, enc, size + end + + def body_fld_param + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + end + match(T_LPAR) + param = {} + while true + token = lookahead + case token.symbol + when T_RPAR + shift_token + break + when T_SPACE + shift_token + end + name = case_insensitive_string + match(T_SPACE) + val = string + param[name] = val + end + return param + end + + def body_ext_1part + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return nil + end + md5 = nstring + + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return md5 + end + disposition = body_fld_dsp + + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return md5, disposition + end + language = body_fld_lang + + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return md5, disposition, language + end + + extension = body_extensions + return md5, disposition, language, extension + end + + def body_ext_mpart + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return nil + end + param = body_fld_param + + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return param + end + disposition = body_fld_dsp + match(T_SPACE) + language = body_fld_lang + + token = lookahead + if token.symbol == T_SPACE + shift_token + else + return param, disposition, language + end + + extension = body_extensions + return param, disposition, language, extension + end + + def body_fld_dsp + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + end + match(T_LPAR) + dsp_type = case_insensitive_string + match(T_SPACE) + param = body_fld_param + match(T_RPAR) + return ContentDisposition.new(dsp_type, param) + end + + def body_fld_lang + token = lookahead + if token.symbol == T_LPAR + shift_token + result = [] + while true + token = lookahead + case token.symbol + when T_RPAR + shift_token + return result + when T_SPACE + shift_token + end + result.push(case_insensitive_string) + end + else + lang = nstring + if lang + return lang.upcase + else + return lang + end + end + end + + def body_extensions + result = [] + while true + token = lookahead + case token.symbol + when T_RPAR + return result + when T_SPACE + shift_token + end + result.push(body_extension) + end + end + + def body_extension + token = lookahead + case token.symbol + when T_LPAR + shift_token + result = body_extensions + match(T_RPAR) + return result + when T_NUMBER + return number + else + return nstring + end + end + + def section + str = "" + token = match(T_LBRA) + str.concat(token.value) + token = match(T_ATOM, T_NUMBER, T_RBRA) + if token.symbol == T_RBRA + str.concat(token.value) + return str + end + str.concat(token.value) + token = lookahead + if token.symbol == T_SPACE + shift_token + str.concat(token.value) + token = match(T_LPAR) + str.concat(token.value) + while true + token = lookahead + case token.symbol + when T_RPAR + str.concat(token.value) + shift_token + break + when T_SPACE + shift_token + str.concat(token.value) + end + str.concat(format_string(astring)) + end + end + token = match(T_RBRA) + str.concat(token.value) + return str + end + + def format_string(str) + case str + when "" + return '""' + when /[\x80-\xff\r\n]/n + # literal + return "{" + str.bytesize.to_s + "}" + CRLF + str + when /[(){ \x00-\x1f\x7f%*"\\]/n + # quoted string + return '"' + str.gsub(/["\\]/n, "\\\\\\&") + '"' + else + # atom + return str + end + end + + def uid_data + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return name, number + end + + def text_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + @lex_state = EXPR_TEXT + token = match(T_TEXT) + @lex_state = EXPR_BEG + return UntaggedResponse.new(name, token.value) + end + + def flags_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return UntaggedResponse.new(name, flag_list, @str) + end + + def list_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + return UntaggedResponse.new(name, mailbox_list, @str) + end + + def mailbox_list + attr = flag_list + match(T_SPACE) + token = match(T_QUOTED, T_NIL) + if token.symbol == T_NIL + delim = nil + else + delim = token.value + end + match(T_SPACE) + name = astring + return MailboxList.new(attr, delim, name) + end + + def getquota_response + # If quota never established, get back + # `NO Quota root does not exist'. + # If quota removed, get `()' after the + # folder spec with no mention of `STORAGE'. + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + mailbox = astring + match(T_SPACE) + match(T_LPAR) + token = lookahead + case token.symbol + when T_RPAR + shift_token + data = MailboxQuota.new(mailbox, nil, nil) + return UntaggedResponse.new(name, data, @str) + when T_ATOM + shift_token + match(T_SPACE) + token = match(T_NUMBER) + usage = token.value + match(T_SPACE) + token = match(T_NUMBER) + quota = token.value + match(T_RPAR) + data = MailboxQuota.new(mailbox, usage, quota) + return UntaggedResponse.new(name, data, @str) + else + parse_error("unexpected token %s", token.symbol) + end + end + + def getquotaroot_response + # Similar to getquota, but only admin can use getquota. + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + mailbox = astring + quotaroots = [] + while true + token = lookahead + break unless token.symbol == T_SPACE + shift_token + quotaroots.push(astring) + end + data = MailboxQuotaRoot.new(mailbox, quotaroots) + return UntaggedResponse.new(name, data, @str) + end + + def getacl_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + mailbox = astring + data = [] + token = lookahead + if token.symbol == T_SPACE + shift_token + while true + token = lookahead + case token.symbol + when T_CRLF + break + when T_SPACE + shift_token + end + user = astring + match(T_SPACE) + rights = astring + data.push(MailboxACLItem.new(user, rights, mailbox)) + end + end + return UntaggedResponse.new(name, data, @str) + end + + def search_response + token = match(T_ATOM) + name = token.value.upcase + token = lookahead + if token.symbol == T_SPACE + shift_token + data = [] + while true + token = lookahead + case token.symbol + when T_CRLF + break + when T_SPACE + shift_token + else + data.push(number) + end + end + else + data = [] + end + return UntaggedResponse.new(name, data, @str) + end + + def thread_response + token = match(T_ATOM) + name = token.value.upcase + token = lookahead + + if token.symbol == T_SPACE + threads = [] + + while true + shift_token + token = lookahead + + case token.symbol + when T_LPAR + threads << thread_branch(token) + when T_CRLF + break + end + end + else + # no member + threads = [] + end + + return UntaggedResponse.new(name, threads, @str) + end + + def thread_branch(token) + rootmember = nil + lastmember = nil + + while true + shift_token # ignore first T_LPAR + token = lookahead + + case token.symbol + when T_NUMBER + # new member + newmember = ThreadMember.new(number, []) + if rootmember.nil? + rootmember = newmember + else + lastmember.children << newmember + end + lastmember = newmember + when T_SPACE + # do nothing + when T_LPAR + if rootmember.nil? + # dummy member + lastmember = rootmember = ThreadMember.new(nil, []) + end + + lastmember.children << thread_branch(token) + when T_RPAR + break + end + end + + return rootmember + end + + def status_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + mailbox = astring + match(T_SPACE) + match(T_LPAR) + attr = {} + while true + token = lookahead + case token.symbol + when T_RPAR + shift_token + break + when T_SPACE + shift_token + end + token = match(T_ATOM) + key = token.value.upcase + match(T_SPACE) + val = number + attr[key] = val + end + data = StatusData.new(mailbox, attr) + return UntaggedResponse.new(name, data, @str) + end + + def capability_response + token = match(T_ATOM) + name = token.value.upcase + match(T_SPACE) + data = [] + while true + token = lookahead + case token.symbol + when T_CRLF + break + when T_SPACE + shift_token + next + end + data.push(atom.upcase) + end + return UntaggedResponse.new(name, data, @str) + end + + def resp_text + @lex_state = EXPR_RTEXT + token = lookahead + if token.symbol == T_LBRA + code = resp_text_code + else + code = nil + end + token = match(T_TEXT) + @lex_state = EXPR_BEG + return ResponseText.new(code, token.value) + end + + def resp_text_code + @lex_state = EXPR_BEG + match(T_LBRA) + token = match(T_ATOM) + name = token.value.upcase + case name + when /\A(?:ALERT|PARSE|READ-ONLY|READ-WRITE|TRYCREATE|NOMODSEQ)\z/n + result = ResponseCode.new(name, nil) + when /\A(?:PERMANENTFLAGS)\z/n + match(T_SPACE) + result = ResponseCode.new(name, flag_list) + when /\A(?:UIDVALIDITY|UIDNEXT|UNSEEN)\z/n + match(T_SPACE) + result = ResponseCode.new(name, number) + else + token = lookahead + if token.symbol == T_SPACE + shift_token + @lex_state = EXPR_CTEXT + token = match(T_TEXT) + @lex_state = EXPR_BEG + result = ResponseCode.new(name, token.value) + else + result = ResponseCode.new(name, nil) + end + end + match(T_RBRA) + @lex_state = EXPR_RTEXT + return result + end + + def address_list + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + else + result = [] + match(T_LPAR) + while true + token = lookahead + case token.symbol + when T_RPAR + shift_token + break + when T_SPACE + shift_token + end + result.push(address) + end + return result + end + end + + ADDRESS_REGEXP = /\G\ +(?# 1: NAME )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \ +(?# 2: ROUTE )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \ +(?# 3: MAILBOX )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \ +(?# 4: HOST )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)")\ +\)/ni + + def address + match(T_LPAR) + if @str.index(ADDRESS_REGEXP, @pos) + # address does not include literal. + @pos = $~.end(0) + name = $1 + route = $2 + mailbox = $3 + host = $4 + for s in [name, route, mailbox, host] + if s + s.gsub!(/\\(["\\])/n, "\\1") + end + end + else + name = nstring + match(T_SPACE) + route = nstring + match(T_SPACE) + mailbox = nstring + match(T_SPACE) + host = nstring + match(T_RPAR) + end + return Address.new(name, route, mailbox, host) + end + +# def flag_list +# result = [] +# match(T_LPAR) +# while true +# token = lookahead +# case token.symbol +# when T_RPAR +# shift_token +# break +# when T_SPACE +# shift_token +# end +# result.push(flag) +# end +# return result +# end + +# def flag +# token = lookahead +# if token.symbol == T_BSLASH +# shift_token +# token = lookahead +# if token.symbol == T_STAR +# shift_token +# return token.value.intern +# else +# return atom.intern +# end +# else +# return atom +# end +# end + + FLAG_REGEXP = /\ +(?# FLAG )\\([^\x80-\xff(){ \x00-\x1f\x7f%"\\]+)|\ +(?# ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*"\\]+)/n + + def flag_list + if @str.index(/\(([^)]*)\)/ni, @pos) + @pos = $~.end(0) + return $1.scan(FLAG_REGEXP).collect { |flag, atom| + if atom + atom + else + symbol = flag.capitalize.untaint.intern + @flag_symbols[symbol] = true + if @flag_symbols.length > IMAP.max_flag_count + raise FlagCountError, "number of flag symbols exceeded" + end + symbol + end + } + else + parse_error("invalid flag list") + end + end + + def nstring + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + else + return string + end + end + + def astring + token = lookahead + if string_token?(token) + return string + else + return atom + end + end + + def string + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + end + token = match(T_QUOTED, T_LITERAL) + return token.value + end + + STRING_TOKENS = [T_QUOTED, T_LITERAL, T_NIL] + + def string_token?(token) + return STRING_TOKENS.include?(token.symbol) + end + + def case_insensitive_string + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + end + token = match(T_QUOTED, T_LITERAL) + return token.value.upcase + end + + def atom + result = "" + while true + token = lookahead + if atom_token?(token) + result.concat(token.value) + shift_token + else + if result.empty? + parse_error("unexpected token %s", token.symbol) + else + return result + end + end + end + end + + ATOM_TOKENS = [ + T_ATOM, + T_NUMBER, + T_NIL, + T_LBRA, + T_RBRA, + T_PLUS + ] + + def atom_token?(token) + return ATOM_TOKENS.include?(token.symbol) + end + + def number + token = lookahead + if token.symbol == T_NIL + shift_token + return nil + end + token = match(T_NUMBER) + return token.value.to_i + end + + def nil_atom + match(T_NIL) + return nil + end + + def match(*args) + token = lookahead + unless args.include?(token.symbol) + parse_error('unexpected token %s (expected %s)', + token.symbol.id2name, + args.collect {|i| i.id2name}.join(" or ")) + end + shift_token + return token + end + + def lookahead + unless @token + @token = next_token + end + return @token + end + + def shift_token + @token = nil + end + + def next_token + case @lex_state + when EXPR_BEG + if @str.index(BEG_REGEXP, @pos) + @pos = $~.end(0) + if $1 + return Token.new(T_SPACE, $+) + elsif $2 + return Token.new(T_NIL, $+) + elsif $3 + return Token.new(T_NUMBER, $+) + elsif $4 + return Token.new(T_ATOM, $+) + elsif $5 + return Token.new(T_QUOTED, + $+.gsub(/\\(["\\])/n, "\\1")) + elsif $6 + return Token.new(T_LPAR, $+) + elsif $7 + return Token.new(T_RPAR, $+) + elsif $8 + return Token.new(T_BSLASH, $+) + elsif $9 + return Token.new(T_STAR, $+) + elsif $10 + return Token.new(T_LBRA, $+) + elsif $11 + return Token.new(T_RBRA, $+) + elsif $12 + len = $+.to_i + val = @str[@pos, len] + @pos += len + return Token.new(T_LITERAL, val) + elsif $13 + return Token.new(T_PLUS, $+) + elsif $14 + return Token.new(T_PERCENT, $+) + elsif $15 + return Token.new(T_CRLF, $+) + elsif $16 + return Token.new(T_EOF, $+) + else + parse_error("[Net::IMAP BUG] BEG_REGEXP is invalid") + end + else + @str.index(/\S*/n, @pos) + parse_error("unknown token - %s", $&.dump) + end + when EXPR_DATA + if @str.index(DATA_REGEXP, @pos) + @pos = $~.end(0) + if $1 + return Token.new(T_SPACE, $+) + elsif $2 + return Token.new(T_NIL, $+) + elsif $3 + return Token.new(T_NUMBER, $+) + elsif $4 + return Token.new(T_QUOTED, + $+.gsub(/\\(["\\])/n, "\\1")) + elsif $5 + len = $+.to_i + val = @str[@pos, len] + @pos += len + return Token.new(T_LITERAL, val) + elsif $6 + return Token.new(T_LPAR, $+) + elsif $7 + return Token.new(T_RPAR, $+) + else + parse_error("[Net::IMAP BUG] DATA_REGEXP is invalid") + end + else + @str.index(/\S*/n, @pos) + parse_error("unknown token - %s", $&.dump) + end + when EXPR_TEXT + if @str.index(TEXT_REGEXP, @pos) + @pos = $~.end(0) + if $1 + return Token.new(T_TEXT, $+) + else + parse_error("[Net::IMAP BUG] TEXT_REGEXP is invalid") + end + else + @str.index(/\S*/n, @pos) + parse_error("unknown token - %s", $&.dump) + end + when EXPR_RTEXT + if @str.index(RTEXT_REGEXP, @pos) + @pos = $~.end(0) + if $1 + return Token.new(T_LBRA, $+) + elsif $2 + return Token.new(T_TEXT, $+) + else + parse_error("[Net::IMAP BUG] RTEXT_REGEXP is invalid") + end + else + @str.index(/\S*/n, @pos) + parse_error("unknown token - %s", $&.dump) + end + when EXPR_CTEXT + if @str.index(CTEXT_REGEXP, @pos) + @pos = $~.end(0) + if $1 + return Token.new(T_TEXT, $+) + else + parse_error("[Net::IMAP BUG] CTEXT_REGEXP is invalid") + end + else + @str.index(/\S*/n, @pos) #/ + parse_error("unknown token - %s", $&.dump) + end + else + parse_error("invalid @lex_state - %s", @lex_state.inspect) + end + end + + def parse_error(fmt, *args) + if IMAP.debug + $stderr.printf("@str: %s\n", @str.dump) + $stderr.printf("@pos: %d\n", @pos) + $stderr.printf("@lex_state: %s\n", @lex_state) + if @token + $stderr.printf("@token.symbol: %s\n", @token.symbol) + $stderr.printf("@token.value: %s\n", @token.value.inspect) + end + end + raise ResponseParseError, format(fmt, *args) + end + end + + # Authenticator for the "LOGIN" authentication type. See + # #authenticate(). + class LoginAuthenticator + def process(data) + case @state + when STATE_USER + @state = STATE_PASSWORD + return @user + when STATE_PASSWORD + return @password + end + end + + private + + STATE_USER = :USER + STATE_PASSWORD = :PASSWORD + + def initialize(user, password) + @user = user + @password = password + @state = STATE_USER + end + end + add_authenticator "LOGIN", LoginAuthenticator + + # Authenticator for the "PLAIN" authentication type. See + # #authenticate(). + class PlainAuthenticator + def process(data) + return "\0#{@user}\0#{@password}" + end + + private + + def initialize(user, password) + @user = user + @password = password + end + end + add_authenticator "PLAIN", PlainAuthenticator + + # Authenticator for the "CRAM-MD5" authentication type. See + # #authenticate(). + class CramMD5Authenticator + def process(challenge) + digest = hmac_md5(challenge, @password) + return @user + " " + digest + end + + private + + def initialize(user, password) + @user = user + @password = password + end + + def hmac_md5(text, key) + if key.length > 64 + key = Digest::MD5.digest(key) + end + + k_ipad = key + "\0" * (64 - key.length) + k_opad = key + "\0" * (64 - key.length) + for i in 0..63 + k_ipad[i] = (k_ipad[i].ord ^ 0x36).chr + k_opad[i] = (k_opad[i].ord ^ 0x5c).chr + end + + digest = Digest::MD5.digest(k_ipad + text) + + return Digest::MD5.hexdigest(k_opad + digest) + end + end + add_authenticator "CRAM-MD5", CramMD5Authenticator + + # Authenticator for the "DIGEST-MD5" authentication type. See + # #authenticate(). + class DigestMD5Authenticator + def process(challenge) + case @stage + when STAGE_ONE + @stage = STAGE_TWO + sparams = {} + c = StringScanner.new(challenge) + while c.scan(/(?:\s*,)?\s*(\w+)=("(?:[^\\"]+|\\.)*"|[^,]+)\s*/) + k, v = c[1], c[2] + if v =~ /^"(.*)"$/ + v = $1 + if v =~ /,/ + v = v.split(',') + end + end + sparams[k] = v + end + + raise DataFormatError, "Bad Challenge: '#{challenge}'" unless c.rest.size == 0 + raise Error, "Server does not support auth (qop = #{sparams['qop'].join(',')})" unless sparams['qop'].include?("auth") + + response = { + :nonce => sparams['nonce'], + :username => @user, + :realm => sparams['realm'], + :cnonce => Digest::MD5.hexdigest("%.15f:%.15f:%d" % [Time.now.to_f, rand, Process.pid.to_s]), + :'digest-uri' => 'imap/' + sparams['realm'], + :qop => 'auth', + :maxbuf => 65535, + :nc => "%08d" % nc(sparams['nonce']), + :charset => sparams['charset'], + } + + response[:authzid] = @authname unless @authname.nil? + + # now, the real thing + a0 = Digest::MD5.digest( [ response.values_at(:username, :realm), @password ].join(':') ) + + a1 = [ a0, response.values_at(:nonce,:cnonce) ].join(':') + a1 << ':' + response[:authzid] unless response[:authzid].nil? + + a2 = "AUTHENTICATE:" + response[:'digest-uri'] + a2 << ":00000000000000000000000000000000" if response[:qop] and response[:qop] =~ /^auth-(?:conf|int)$/ + + response[:response] = Digest::MD5.hexdigest( + [ + Digest::MD5.hexdigest(a1), + response.values_at(:nonce, :nc, :cnonce, :qop), + Digest::MD5.hexdigest(a2) + ].join(':') + ) + + return response.keys.map {|key| qdval(key.to_s, response[key]) }.join(',') + when STAGE_TWO + @stage = nil + # if at the second stage, return an empty string + if challenge =~ /rspauth=/ + return '' + else + raise ResponseParseError, challenge + end + else + raise ResponseParseError, challenge + end + end + + def initialize(user, password, authname = nil) + @user, @password, @authname = user, password, authname + @nc, @stage = {}, STAGE_ONE + end + + private + + STAGE_ONE = :stage_one + STAGE_TWO = :stage_two + + def nc(nonce) + if @nc.has_key? nonce + @nc[nonce] = @nc[nonce] + 1 + else + @nc[nonce] = 1 + end + return @nc[nonce] + end + + # some responses need quoting + def qdval(k, v) + return if k.nil? or v.nil? + if %w"username authzid realm nonce cnonce digest-uri qop".include? k + v.gsub!(/([\\"])/, "\\\1") + return '%s="%s"' % [k, v] + else + return '%s=%s' % [k, v] + end + end + end + add_authenticator "DIGEST-MD5", DigestMD5Authenticator + + # Superclass of IMAP errors. + class Error < StandardError + end + + # Error raised when data is in the incorrect format. + class DataFormatError < Error + end + + # Error raised when a response from the server is non-parseable. + class ResponseParseError < Error + end + + # Superclass of all errors used to encapsulate "fail" responses + # from the server. + class ResponseError < Error + + # The response that caused this error + attr_accessor :response + + def initialize(response) + @response = response + + super @response.data.text + end + + end + + # Error raised upon a "NO" response from the server, indicating + # that the client command could not be completed successfully. + class NoResponseError < ResponseError + end + + # Error raised upon a "BAD" response from the server, indicating + # that the client command violated the IMAP protocol, or an internal + # server failure has occurred. + class BadResponseError < ResponseError + end + + # Error raised upon a "BYE" response from the server, indicating + # that the client is not being allowed to login, or has been timed + # out due to inactivity. + class ByeResponseError < ResponseError + end + + # Error raised when too many flags are interned to symbols. + class FlagCountError < Error + end + end +end + +if __FILE__ == $0 + # :enddoc: + require "getoptlong" + + $stdout.sync = true + $port = nil + $user = ENV["USER"] || ENV["LOGNAME"] + $auth = "login" + $ssl = false + $starttls = false + + def usage + < + + --help print this message + --port=PORT specifies port + --user=USER specifies user + --auth=AUTH specifies auth type + --starttls use starttls + --ssl use ssl +EOF + end + + begin + require 'io/console' + rescue LoadError + def _noecho(&block) + system("stty", "-echo") + begin + yield STDIN + ensure + system("stty", "echo") + end + end + else + def _noecho(&block) + STDIN.noecho(&block) + end + end + + def get_password + print "password: " + begin + return _noecho(&:gets).chomp + ensure + puts + end + end + + def get_command + printf("%s@%s> ", $user, $host) + if line = gets + return line.strip.split(/\s+/) + else + return nil + end + end + + parser = GetoptLong.new + parser.set_options(['--debug', GetoptLong::NO_ARGUMENT], + ['--help', GetoptLong::NO_ARGUMENT], + ['--port', GetoptLong::REQUIRED_ARGUMENT], + ['--user', GetoptLong::REQUIRED_ARGUMENT], + ['--auth', GetoptLong::REQUIRED_ARGUMENT], + ['--starttls', GetoptLong::NO_ARGUMENT], + ['--ssl', GetoptLong::NO_ARGUMENT]) + begin + parser.each_option do |name, arg| + case name + when "--port" + $port = arg + when "--user" + $user = arg + when "--auth" + $auth = arg + when "--ssl" + $ssl = true + when "--starttls" + $starttls = true + when "--debug" + Net::IMAP.debug = true + when "--help" + usage + exit + end + end + rescue + abort usage + end + + $host = ARGV.shift + unless $host + abort usage + end + + imap = Net::IMAP.new($host, :port => $port, :ssl => $ssl) + begin + imap.starttls if $starttls + class << password = method(:get_password) + alias to_str call + end + imap.authenticate($auth, $user, password) + while true + cmd, *args = get_command + break unless cmd + begin + case cmd + when "list" + for mbox in imap.list("", args[0] || "*") + if mbox.attr.include?(Net::IMAP::NOSELECT) + prefix = "!" + elsif mbox.attr.include?(Net::IMAP::MARKED) + prefix = "*" + else + prefix = " " + end + print prefix, mbox.name, "\n" + end + when "select" + imap.select(args[0] || "inbox") + print "ok\n" + when "close" + imap.close + print "ok\n" + when "summary" + unless messages = imap.responses["EXISTS"][-1] + puts "not selected" + next + end + if messages > 0 + for data in imap.fetch(1..-1, ["ENVELOPE"]) + print data.seqno, ": ", data.attr["ENVELOPE"].subject, "\n" + end + else + puts "no message" + end + when "fetch" + if args[0] + data = imap.fetch(args[0].to_i, ["RFC822.HEADER", "RFC822.TEXT"])[0] + puts data.attr["RFC822.HEADER"] + puts data.attr["RFC822.TEXT"] + else + puts "missing argument" + end + when "logout", "exit", "quit" + break + when "help", "?" + print <. +# +# Documented by William Webber and Minero Aoki. +# +# This program is free software. You can re-distribute and/or +# modify this program under the same terms as Ruby itself, +# Ruby Distribute License. +# +# NOTE: You can find Japanese version of this document at: +# http://www.ruby-lang.org/ja/man/html/net_pop.html +# +# $Id: pop.rb 44164 2013-12-13 02:38:55Z a_matsuda $ +# +# See Net::POP3 for documentation. +# + +require 'net/protocol' +require 'digest/md5' +require 'timeout' + +begin + require "openssl" +rescue LoadError +end + +module Net + + # Non-authentication POP3 protocol error + # (reply code "-ERR", except authentication). + class POPError < ProtocolError; end + + # POP3 authentication error. + class POPAuthenticationError < ProtoAuthError; end + + # Unexpected response from the server. + class POPBadResponse < POPError; end + + # + # == What is This Library? + # + # This library provides functionality for retrieving + # email via POP3, the Post Office Protocol version 3. For details + # of POP3, see [RFC1939] (http://www.ietf.org/rfc/rfc1939.txt). + # + # == Examples + # + # === Retrieving Messages + # + # This example retrieves messages from the server and deletes them + # on the server. + # + # Messages are written to files named 'inbox/1', 'inbox/2', .... + # Replace 'pop.example.com' with your POP3 server address, and + # 'YourAccount' and 'YourPassword' with the appropriate account + # details. + # + # require 'net/pop' + # + # pop = Net::POP3.new('pop.example.com') + # pop.start('YourAccount', 'YourPassword') # (1) + # if pop.mails.empty? + # puts 'No mail.' + # else + # i = 0 + # pop.each_mail do |m| # or "pop.mails.each ..." # (2) + # File.open("inbox/#{i}", 'w') do |f| + # f.write m.pop + # end + # m.delete + # i += 1 + # end + # puts "#{pop.mails.size} mails popped." + # end + # pop.finish # (3) + # + # 1. Call Net::POP3#start and start POP session. + # 2. Access messages by using POP3#each_mail and/or POP3#mails. + # 3. Close POP session by calling POP3#finish or use the block form of #start. + # + # === Shortened Code + # + # The example above is very verbose. You can shorten the code by using + # some utility methods. First, the block form of Net::POP3.start can + # be used instead of POP3.new, POP3#start and POP3#finish. + # + # require 'net/pop' + # + # Net::POP3.start('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |pop| + # if pop.mails.empty? + # puts 'No mail.' + # else + # i = 0 + # pop.each_mail do |m| # or "pop.mails.each ..." + # File.open("inbox/#{i}", 'w') do |f| + # f.write m.pop + # end + # m.delete + # i += 1 + # end + # puts "#{pop.mails.size} mails popped." + # end + # end + # + # POP3#delete_all is an alternative for #each_mail and #delete. + # + # require 'net/pop' + # + # Net::POP3.start('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |pop| + # if pop.mails.empty? + # puts 'No mail.' + # else + # i = 1 + # pop.delete_all do |m| + # File.open("inbox/#{i}", 'w') do |f| + # f.write m.pop + # end + # i += 1 + # end + # end + # end + # + # And here is an even shorter example. + # + # require 'net/pop' + # + # i = 0 + # Net::POP3.delete_all('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |m| + # File.open("inbox/#{i}", 'w') do |f| + # f.write m.pop + # end + # i += 1 + # end + # + # === Memory Space Issues + # + # All the examples above get each message as one big string. + # This example avoids this. + # + # require 'net/pop' + # + # i = 1 + # Net::POP3.delete_all('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |m| + # File.open("inbox/#{i}", 'w') do |f| + # m.pop do |chunk| # get a message little by little. + # f.write chunk + # end + # i += 1 + # end + # end + # + # === Using APOP + # + # The net/pop library supports APOP authentication. + # To use APOP, use the Net::APOP class instead of the Net::POP3 class. + # You can use the utility method, Net::POP3.APOP(). For example: + # + # require 'net/pop' + # + # # Use APOP authentication if $isapop == true + # pop = Net::POP3.APOP($is_apop).new('apop.example.com', 110) + # pop.start(YourAccount', 'YourPassword') do |pop| + # # Rest of the code is the same. + # end + # + # === Fetch Only Selected Mail Using 'UIDL' POP Command + # + # If your POP server provides UIDL functionality, + # you can grab only selected mails from the POP server. + # e.g. + # + # def need_pop?( id ) + # # determine if we need pop this mail... + # end + # + # Net::POP3.start('pop.example.com', 110, + # 'Your account', 'Your password') do |pop| + # pop.mails.select { |m| need_pop?(m.unique_id) }.each do |m| + # do_something(m.pop) + # end + # end + # + # The POPMail#unique_id() method returns the unique-id of the message as a + # String. Normally the unique-id is a hash of the message. + # + class POP3 < Protocol + + # svn revision of this library + Revision = %q$Revision: 44164 $.split[1] + + # + # Class Parameters + # + + # returns the port for POP3 + def POP3.default_port + default_pop3_port() + end + + # The default port for POP3 connections, port 110 + def POP3.default_pop3_port + 110 + end + + # The default port for POP3S connections, port 995 + def POP3.default_pop3s_port + 995 + end + + def POP3.socket_type #:nodoc: obsolete + Net::InternetMessageIO + end + + # + # Utilities + # + + # Returns the APOP class if +isapop+ is true; otherwise, returns + # the POP class. For example: + # + # # Example 1 + # pop = Net::POP3::APOP($is_apop).new(addr, port) + # + # # Example 2 + # Net::POP3::APOP($is_apop).start(addr, port) do |pop| + # .... + # end + # + def POP3.APOP(isapop) + isapop ? APOP : POP3 + end + + # Starts a POP3 session and iterates over each POPMail object, + # yielding it to the +block+. + # This method is equivalent to: + # + # Net::POP3.start(address, port, account, password) do |pop| + # pop.each_mail do |m| + # yield m + # end + # end + # + # This method raises a POPAuthenticationError if authentication fails. + # + # === Example + # + # Net::POP3.foreach('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |m| + # file.write m.pop + # m.delete if $DELETE + # end + # + def POP3.foreach(address, port = nil, + account = nil, password = nil, + isapop = false, &block) # :yields: message + start(address, port, account, password, isapop) {|pop| + pop.each_mail(&block) + } + end + + # Starts a POP3 session and deletes all messages on the server. + # If a block is given, each POPMail object is yielded to it before + # being deleted. + # + # This method raises a POPAuthenticationError if authentication fails. + # + # === Example + # + # Net::POP3.delete_all('pop.example.com', 110, + # 'YourAccount', 'YourPassword') do |m| + # file.write m.pop + # end + # + def POP3.delete_all(address, port = nil, + account = nil, password = nil, + isapop = false, &block) + start(address, port, account, password, isapop) {|pop| + pop.delete_all(&block) + } + end + + # Opens a POP3 session, attempts authentication, and quits. + # + # This method raises POPAuthenticationError if authentication fails. + # + # === Example: normal POP3 + # + # Net::POP3.auth_only('pop.example.com', 110, + # 'YourAccount', 'YourPassword') + # + # === Example: APOP + # + # Net::POP3.auth_only('pop.example.com', 110, + # 'YourAccount', 'YourPassword', true) + # + def POP3.auth_only(address, port = nil, + account = nil, password = nil, + isapop = false) + new(address, port, isapop).auth_only account, password + end + + # Starts a pop3 session, attempts authentication, and quits. + # This method must not be called while POP3 session is opened. + # This method raises POPAuthenticationError if authentication fails. + def auth_only(account, password) + raise IOError, 'opening previously opened POP session' if started? + start(account, password) { + ; + } + end + + # + # SSL + # + + @ssl_params = nil + + # :call-seq: + # Net::POP.enable_ssl(params = {}) + # + # Enable SSL for all new instances. + # +params+ is passed to OpenSSL::SSLContext#set_params. + def POP3.enable_ssl(*args) + @ssl_params = create_ssl_params(*args) + end + + # Constructs proper parameters from arguments + def POP3.create_ssl_params(verify_or_params = {}, certs = nil) + begin + params = verify_or_params.to_hash + rescue NoMethodError + params = {} + params[:verify_mode] = verify_or_params + if certs + if File.file?(certs) + params[:ca_file] = certs + elsif File.directory?(certs) + params[:ca_path] = certs + end + end + end + return params + end + + # Disable SSL for all new instances. + def POP3.disable_ssl + @ssl_params = nil + end + + # returns the SSL Parameters + # + # see also POP3.enable_ssl + def POP3.ssl_params + return @ssl_params + end + + # returns +true+ if POP3.ssl_params is set + def POP3.use_ssl? + return !@ssl_params.nil? + end + + # returns whether verify_mode is enable from POP3.ssl_params + def POP3.verify + return @ssl_params[:verify_mode] + end + + # returns the :ca_file or :ca_path from POP3.ssl_params + def POP3.certs + return @ssl_params[:ca_file] || @ssl_params[:ca_path] + end + + # + # Session management + # + + # Creates a new POP3 object and open the connection. Equivalent to + # + # Net::POP3.new(address, port, isapop).start(account, password) + # + # If +block+ is provided, yields the newly-opened POP3 object to it, + # and automatically closes it at the end of the session. + # + # === Example + # + # Net::POP3.start(addr, port, account, password) do |pop| + # pop.each_mail do |m| + # file.write m.pop + # m.delete + # end + # end + # + def POP3.start(address, port = nil, + account = nil, password = nil, + isapop = false, &block) # :yield: pop + new(address, port, isapop).start(account, password, &block) + end + + # Creates a new POP3 object. + # + # +address+ is the hostname or ip address of your POP3 server. + # + # The optional +port+ is the port to connect to. + # + # The optional +isapop+ specifies whether this connection is going + # to use APOP authentication; it defaults to +false+. + # + # This method does *not* open the TCP connection. + def initialize(addr, port = nil, isapop = false) + @address = addr + @ssl_params = POP3.ssl_params + @port = port + @apop = isapop + + @command = nil + @socket = nil + @started = false + @open_timeout = 30 + @read_timeout = 60 + @debug_output = nil + + @mails = nil + @n_mails = nil + @n_bytes = nil + end + + # Does this instance use APOP authentication? + def apop? + @apop + end + + # does this instance use SSL? + def use_ssl? + return !@ssl_params.nil? + end + + # :call-seq: + # Net::POP#enable_ssl(params = {}) + # + # Enables SSL for this instance. Must be called before the connection is + # established to have any effect. + # +params[:port]+ is port to establish the SSL connection on; Defaults to 995. + # +params+ (except :port) is passed to OpenSSL::SSLContext#set_params. + def enable_ssl(verify_or_params = {}, certs = nil, port = nil) + begin + @ssl_params = verify_or_params.to_hash.dup + @port = @ssl_params.delete(:port) || @port + rescue NoMethodError + @ssl_params = POP3.create_ssl_params(verify_or_params, certs) + @port = port || @port + end + end + + # Disable SSL for all new instances. + def disable_ssl + @ssl_params = nil + end + + # Provide human-readable stringification of class state. + def inspect + "#<#{self.class} #{@address}:#{@port} open=#{@started}>" + end + + # *WARNING*: This method causes a serious security hole. + # Use this method only for debugging. + # + # Set an output stream for debugging. + # + # === Example + # + # pop = Net::POP.new(addr, port) + # pop.set_debug_output $stderr + # pop.start(account, passwd) do |pop| + # .... + # end + # + def set_debug_output(arg) + @debug_output = arg + end + + # The address to connect to. + attr_reader :address + + # The port number to connect to. + def port + return @port || (use_ssl? ? POP3.default_pop3s_port : POP3.default_pop3_port) + end + + # Seconds to wait until a connection is opened. + # If the POP3 object cannot open a connection within this time, + # it raises a Net::OpenTimeout exception. The default value is 30 seconds. + attr_accessor :open_timeout + + # Seconds to wait until reading one block (by one read(1) call). + # If the POP3 object cannot complete a read() within this time, + # it raises a Net::ReadTimeout exception. The default value is 60 seconds. + attr_reader :read_timeout + + # Set the read timeout. + def read_timeout=(sec) + @command.socket.read_timeout = sec if @command + @read_timeout = sec + end + + # +true+ if the POP3 session has started. + def started? + @started + end + + alias active? started? #:nodoc: obsolete + + # Starts a POP3 session. + # + # When called with block, gives a POP3 object to the block and + # closes the session after block call finishes. + # + # This method raises a POPAuthenticationError if authentication fails. + def start(account, password) # :yield: pop + raise IOError, 'POP session already started' if @started + if block_given? + begin + do_start account, password + return yield(self) + ensure + do_finish + end + else + do_start account, password + return self + end + end + + # internal method for Net::POP3.start + def do_start(account, password) # :nodoc: + s = Timeout.timeout(@open_timeout, Net::OpenTimeout) do + TCPSocket.open(@address, port) + end + if use_ssl? + raise 'openssl library not installed' unless defined?(OpenSSL) + context = OpenSSL::SSL::SSLContext.new + context.set_params(@ssl_params) + s = OpenSSL::SSL::SSLSocket.new(s, context) + s.sync_close = true + s.connect + if context.verify_mode != OpenSSL::SSL::VERIFY_NONE + s.post_connection_check(@address) + end + end + @socket = InternetMessageIO.new(s) + logging "POP session started: #{@address}:#{@port} (#{@apop ? 'APOP' : 'POP'})" + @socket.read_timeout = @read_timeout + @socket.debug_output = @debug_output + on_connect + @command = POP3Command.new(@socket) + if apop? + @command.apop account, password + else + @command.auth account, password + end + @started = true + ensure + # Authentication failed, clean up connection. + unless @started + s.close if s and not s.closed? + @socket = nil + @command = nil + end + end + private :do_start + + # Does nothing + def on_connect # :nodoc: + end + private :on_connect + + # Finishes a POP3 session and closes TCP connection. + def finish + raise IOError, 'POP session not yet started' unless started? + do_finish + end + + # nil's out the: + # - mails + # - number counter for mails + # - number counter for bytes + # - quits the current command, if any + def do_finish # :nodoc: + @mails = nil + @n_mails = nil + @n_bytes = nil + @command.quit if @command + ensure + @started = false + @command = nil + @socket.close if @socket and not @socket.closed? + @socket = nil + end + private :do_finish + + # Returns the current command. + # + # Raises IOError if there is no active socket + def command # :nodoc: + raise IOError, 'POP session not opened yet' \ + if not @socket or @socket.closed? + @command + end + private :command + + # + # POP protocol wrapper + # + + # Returns the number of messages on the POP server. + def n_mails + return @n_mails if @n_mails + @n_mails, @n_bytes = command().stat + @n_mails + end + + # Returns the total size in bytes of all the messages on the POP server. + def n_bytes + return @n_bytes if @n_bytes + @n_mails, @n_bytes = command().stat + @n_bytes + end + + # Returns an array of Net::POPMail objects, representing all the + # messages on the server. This array is renewed when the session + # restarts; otherwise, it is fetched from the server the first time + # this method is called (directly or indirectly) and cached. + # + # This method raises a POPError if an error occurs. + def mails + return @mails.dup if @mails + if n_mails() == 0 + # some popd raises error for LIST on the empty mailbox. + @mails = [] + return [] + end + + @mails = command().list.map {|num, size| + POPMail.new(num, size, self, command()) + } + @mails.dup + end + + # Yields each message to the passed-in block in turn. + # Equivalent to: + # + # pop3.mails.each do |popmail| + # .... + # end + # + # This method raises a POPError if an error occurs. + def each_mail(&block) # :yield: message + mails().each(&block) + end + + alias each each_mail + + # Deletes all messages on the server. + # + # If called with a block, yields each message in turn before deleting it. + # + # === Example + # + # n = 1 + # pop.delete_all do |m| + # File.open("inbox/#{n}") do |f| + # f.write m.pop + # end + # n += 1 + # end + # + # This method raises a POPError if an error occurs. + # + def delete_all # :yield: message + mails().each do |m| + yield m if block_given? + m.delete unless m.deleted? + end + end + + # Resets the session. This clears all "deleted" marks from messages. + # + # This method raises a POPError if an error occurs. + def reset + command().rset + mails().each do |m| + m.instance_eval { + @deleted = false + } + end + end + + def set_all_uids #:nodoc: internal use only (called from POPMail#uidl) + uidl = command().uidl + @mails.each {|m| m.uid = uidl[m.number] } + end + + # debugging output for +msg+ + def logging(msg) + @debug_output << msg + "\n" if @debug_output + end + + end # class POP3 + + # class aliases + POP = POP3 # :nodoc: + POPSession = POP3 # :nodoc: + POP3Session = POP3 # :nodoc: + + # + # This class is equivalent to POP3, except that it uses APOP authentication. + # + class APOP < POP3 + # Always returns true. + def apop? + true + end + end + + # class aliases + APOPSession = APOP + + # + # This class represents a message which exists on the POP server. + # Instances of this class are created by the POP3 class; they should + # not be directly created by the user. + # + class POPMail + + def initialize(num, len, pop, cmd) #:nodoc: + @number = num + @length = len + @pop = pop + @command = cmd + @deleted = false + @uid = nil + end + + # The sequence number of the message on the server. + attr_reader :number + + # The length of the message in octets. + attr_reader :length + alias size length + + # Provide human-readable stringification of class state. + def inspect + "#<#{self.class} #{@number}#{@deleted ? ' deleted' : ''}>" + end + + # + # This method fetches the message. If called with a block, the + # message is yielded to the block one chunk at a time. If called + # without a block, the message is returned as a String. The optional + # +dest+ argument will be prepended to the returned String; this + # argument is essentially obsolete. + # + # === Example without block + # + # POP3.start('pop.example.com', 110, + # 'YourAccount, 'YourPassword') do |pop| + # n = 1 + # pop.mails.each do |popmail| + # File.open("inbox/#{n}", 'w') do |f| + # f.write popmail.pop + # end + # popmail.delete + # n += 1 + # end + # end + # + # === Example with block + # + # POP3.start('pop.example.com', 110, + # 'YourAccount, 'YourPassword') do |pop| + # n = 1 + # pop.mails.each do |popmail| + # File.open("inbox/#{n}", 'w') do |f| + # popmail.pop do |chunk| #### + # f.write chunk + # end + # end + # n += 1 + # end + # end + # + # This method raises a POPError if an error occurs. + # + def pop( dest = '', &block ) # :yield: message_chunk + if block_given? + @command.retr(@number, &block) + nil + else + @command.retr(@number) do |chunk| + dest << chunk + end + dest + end + end + + alias all pop #:nodoc: obsolete + alias mail pop #:nodoc: obsolete + + # Fetches the message header and +lines+ lines of body. + # + # The optional +dest+ argument is obsolete. + # + # This method raises a POPError if an error occurs. + def top(lines, dest = '') + @command.top(@number, lines) do |chunk| + dest << chunk + end + dest + end + + # Fetches the message header. + # + # The optional +dest+ argument is obsolete. + # + # This method raises a POPError if an error occurs. + def header(dest = '') + top(0, dest) + end + + # Marks a message for deletion on the server. Deletion does not + # actually occur until the end of the session; deletion may be + # cancelled for _all_ marked messages by calling POP3#reset(). + # + # This method raises a POPError if an error occurs. + # + # === Example + # + # POP3.start('pop.example.com', 110, + # 'YourAccount, 'YourPassword') do |pop| + # n = 1 + # pop.mails.each do |popmail| + # File.open("inbox/#{n}", 'w') do |f| + # f.write popmail.pop + # end + # popmail.delete #### + # n += 1 + # end + # end + # + def delete + @command.dele @number + @deleted = true + end + + alias delete! delete #:nodoc: obsolete + + # True if the mail has been deleted. + def deleted? + @deleted + end + + # Returns the unique-id of the message. + # Normally the unique-id is a hash string of the message. + # + # This method raises a POPError if an error occurs. + def unique_id + return @uid if @uid + @pop.set_all_uids + @uid + end + + alias uidl unique_id + + def uid=(uid) #:nodoc: internal use only + @uid = uid + end + + end # class POPMail + + + class POP3Command #:nodoc: internal use only + + def initialize(sock) + @socket = sock + @error_occurred = false + res = check_response(critical { recv_response() }) + @apop_stamp = res.slice(/<[!-~]+@[!-~]+>/) + end + + attr_reader :socket + + def inspect + "#<#{self.class} socket=#{@socket}>" + end + + def auth(account, password) + check_response_auth(critical { + check_response_auth(get_response('USER %s', account)) + get_response('PASS %s', password) + }) + end + + def apop(account, password) + raise POPAuthenticationError, 'not APOP server; cannot login' \ + unless @apop_stamp + check_response_auth(critical { + get_response('APOP %s %s', + account, + Digest::MD5.hexdigest(@apop_stamp + password)) + }) + end + + def list + critical { + getok 'LIST' + list = [] + @socket.each_list_item do |line| + m = /\A(\d+)[ \t]+(\d+)/.match(line) or + raise POPBadResponse, "bad response: #{line}" + list.push [m[1].to_i, m[2].to_i] + end + return list + } + end + + def stat + res = check_response(critical { get_response('STAT') }) + m = /\A\+OK\s+(\d+)\s+(\d+)/.match(res) or + raise POPBadResponse, "wrong response format: #{res}" + [m[1].to_i, m[2].to_i] + end + + def rset + check_response(critical { get_response('RSET') }) + end + + def top(num, lines = 0, &block) + critical { + getok('TOP %d %d', num, lines) + @socket.each_message_chunk(&block) + } + end + + def retr(num, &block) + critical { + getok('RETR %d', num) + @socket.each_message_chunk(&block) + } + end + + def dele(num) + check_response(critical { get_response('DELE %d', num) }) + end + + def uidl(num = nil) + if num + res = check_response(critical { get_response('UIDL %d', num) }) + return res.split(/ /)[1] + else + critical { + getok('UIDL') + table = {} + @socket.each_list_item do |line| + num, uid = line.split + table[num.to_i] = uid + end + return table + } + end + end + + def quit + check_response(critical { get_response('QUIT') }) + end + + private + + def getok(fmt, *fargs) + @socket.writeline sprintf(fmt, *fargs) + check_response(recv_response()) + end + + def get_response(fmt, *fargs) + @socket.writeline sprintf(fmt, *fargs) + recv_response() + end + + def recv_response + @socket.readline + end + + def check_response(res) + raise POPError, res unless /\A\+OK/i =~ res + res + end + + def check_response_auth(res) + raise POPAuthenticationError, res unless /\A\+OK/i =~ res + res + end + + def critical + return '+OK dummy ok response' if @error_occurred + begin + return yield() + rescue Exception + @error_occurred = true + raise + end + end + + end # class POP3Command + +end # module Net diff --git a/ruby/lib/ruby/2.1.0/net/protocol.rb b/ruby/lib/ruby/2.1.0/net/protocol.rb new file mode 100644 index 0000000..174c37a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/protocol.rb @@ -0,0 +1,420 @@ +# +# = net/protocol.rb +# +#-- +# Copyright (c) 1999-2004 Yukihiro Matsumoto +# Copyright (c) 1999-2004 Minero Aoki +# +# written and maintained by Minero Aoki +# +# This program is free software. You can re-distribute and/or +# modify this program under the same terms as Ruby itself, +# Ruby Distribute License or GNU General Public License. +# +# $Id: protocol.rb 46496 2014-06-22 16:53:59Z nagachika $ +#++ +# +# WARNING: This file is going to remove. +# Do not rely on the implementation written in this file. +# + +require 'socket' +require 'timeout' + +module Net # :nodoc: + + class Protocol #:nodoc: internal use only + private + def Protocol.protocol_param(name, val) + module_eval(<<-End, __FILE__, __LINE__ + 1) + def #{name} + #{val} + end + End + end + end + + + class ProtocolError < StandardError; end + class ProtoSyntaxError < ProtocolError; end + class ProtoFatalError < ProtocolError; end + class ProtoUnknownError < ProtocolError; end + class ProtoServerError < ProtocolError; end + class ProtoAuthError < ProtocolError; end + class ProtoCommandError < ProtocolError; end + class ProtoRetriableError < ProtocolError; end + ProtocRetryError = ProtoRetriableError + + ## + # OpenTimeout, a subclass of Timeout::Error, is raised if a connection cannot + # be created within the open_timeout. + + class OpenTimeout < Timeout::Error; end + + ## + # ReadTimeout, a subclass of Timeout::Error, is raised if a chunk of the + # response cannot be read within the read_timeout. + + class ReadTimeout < Timeout::Error; end + + + class BufferedIO #:nodoc: internal use only + def initialize(io) + @io = io + @read_timeout = 60 + @continue_timeout = nil + @debug_output = nil + @rbuf = '' + end + + attr_reader :io + attr_accessor :read_timeout + attr_accessor :continue_timeout + attr_accessor :debug_output + + def inspect + "#<#{self.class} io=#{@io}>" + end + + def eof? + @io.eof? + end + + def closed? + @io.closed? + end + + def close + @io.close + end + + # + # Read + # + + public + + def read(len, dest = '', ignore_eof = false) + LOG "reading #{len} bytes..." + read_bytes = 0 + begin + while read_bytes + @rbuf.size < len + dest << (s = rbuf_consume(@rbuf.size)) + read_bytes += s.size + rbuf_fill + end + dest << (s = rbuf_consume(len - read_bytes)) + read_bytes += s.size + rescue EOFError + raise unless ignore_eof + end + LOG "read #{read_bytes} bytes" + dest + end + + def read_all(dest = '') + LOG 'reading all...' + read_bytes = 0 + begin + while true + dest << (s = rbuf_consume(@rbuf.size)) + read_bytes += s.size + rbuf_fill + end + rescue EOFError + ; + end + LOG "read #{read_bytes} bytes" + dest + end + + def readuntil(terminator, ignore_eof = false) + begin + until idx = @rbuf.index(terminator) + rbuf_fill + end + return rbuf_consume(idx + terminator.size) + rescue EOFError + raise unless ignore_eof + return rbuf_consume(@rbuf.size) + end + end + + def readline + readuntil("\n").chop + end + + private + + BUFSIZE = 1024 * 16 + + def rbuf_fill + begin + @rbuf << @io.read_nonblock(BUFSIZE) + rescue IO::WaitReadable + if IO.select([@io], nil, nil, @read_timeout) + retry + else + raise Net::ReadTimeout + end + rescue IO::WaitWritable + # OpenSSL::Buffering#read_nonblock may fail with IO::WaitWritable. + # http://www.openssl.org/support/faq.html#PROG10 + if IO.select(nil, [@io], nil, @read_timeout) + retry + else + raise Net::ReadTimeout + end + end + end + + def rbuf_consume(len) + s = @rbuf.slice!(0, len) + @debug_output << %Q[-> #{s.dump}\n] if @debug_output + s + end + + # + # Write + # + + public + + def write(str) + writing { + write0 str + } + end + + alias << write + + def writeline(str) + writing { + write0 str + "\r\n" + } + end + + private + + def writing + @written_bytes = 0 + @debug_output << '<- ' if @debug_output + yield + @debug_output << "\n" if @debug_output + bytes = @written_bytes + @written_bytes = nil + bytes + end + + def write0(str) + @debug_output << str.dump if @debug_output + len = @io.write(str) + @written_bytes += len + len + end + + # + # Logging + # + + private + + def LOG_off + @save_debug_out = @debug_output + @debug_output = nil + end + + def LOG_on + @debug_output = @save_debug_out + end + + def LOG(msg) + return unless @debug_output + @debug_output << msg + "\n" + end + end + + + class InternetMessageIO < BufferedIO #:nodoc: internal use only + def initialize(io) + super + @wbuf = nil + end + + # + # Read + # + + def each_message_chunk + LOG 'reading message...' + LOG_off() + read_bytes = 0 + while (line = readuntil("\r\n")) != ".\r\n" + read_bytes += line.size + yield line.sub(/\A\./, '') + end + LOG_on() + LOG "read message (#{read_bytes} bytes)" + end + + # *library private* (cannot handle 'break') + def each_list_item + while (str = readuntil("\r\n")) != ".\r\n" + yield str.chop + end + end + + def write_message_0(src) + prev = @written_bytes + each_crlf_line(src) do |line| + write0 dot_stuff(line) + end + @written_bytes - prev + end + + # + # Write + # + + def write_message(src) + LOG "writing message from #{src.class}" + LOG_off() + len = writing { + using_each_crlf_line { + write_message_0 src + } + } + LOG_on() + LOG "wrote #{len} bytes" + len + end + + def write_message_by_block(&block) + LOG 'writing message from block' + LOG_off() + len = writing { + using_each_crlf_line { + begin + block.call(WriteAdapter.new(self, :write_message_0)) + rescue LocalJumpError + # allow `break' from writer block + end + } + } + LOG_on() + LOG "wrote #{len} bytes" + len + end + + private + + def dot_stuff(s) + s.sub(/\A\./, '..') + end + + def using_each_crlf_line + @wbuf = '' + yield + if not @wbuf.empty? # unterminated last line + write0 dot_stuff(@wbuf.chomp) + "\r\n" + elsif @written_bytes == 0 # empty src + write0 "\r\n" + end + write0 ".\r\n" + @wbuf = nil + end + + def each_crlf_line(src) + buffer_filling(@wbuf, src) do + while line = @wbuf.slice!(/\A[^\r\n]*(?:\n|\r(?:\n|(?!\z)))/) + yield line.chomp("\n") + "\r\n" + end + end + end + + def buffer_filling(buf, src) + case src + when String # for speeding up. + 0.step(src.size - 1, 1024) do |i| + buf << src[i, 1024] + yield + end + when File # for speeding up. + while s = src.read(1024) + buf << s + yield + end + else # generic reader + src.each do |str| + buf << str + yield if buf.size > 1024 + end + yield unless buf.empty? + end + end + end + + + # + # The writer adapter class + # + class WriteAdapter + def initialize(socket, method) + @socket = socket + @method_id = method + end + + def inspect + "#<#{self.class} socket=#{@socket.inspect}>" + end + + def write(str) + @socket.__send__(@method_id, str) + end + + alias print write + + def <<(str) + write str + self + end + + def puts(str = '') + write str.chomp("\n") + "\n" + end + + def printf(*args) + write sprintf(*args) + end + end + + + class ReadAdapter #:nodoc: internal use only + def initialize(block) + @block = block + end + + def inspect + "#<#{self.class}>" + end + + def <<(str) + call_block(str, &@block) if @block + end + + private + + # This method is needed because @block must be called by yield, + # not Proc#call. You can see difference when using `break' in + # the block. + def call_block(str) + yield str + end + end + + + module NetPrivate #:nodoc: obsolete + Socket = ::Net::InternetMessageIO + end + +end # module Net diff --git a/ruby/lib/ruby/2.1.0/net/smtp.rb b/ruby/lib/ruby/2.1.0/net/smtp.rb new file mode 100644 index 0000000..a796593 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/net/smtp.rb @@ -0,0 +1,1066 @@ +# = net/smtp.rb +# +# Copyright (c) 1999-2007 Yukihiro Matsumoto. +# +# Copyright (c) 1999-2007 Minero Aoki. +# +# Written & maintained by Minero Aoki . +# +# Documented by William Webber and Minero Aoki. +# +# This program is free software. You can re-distribute and/or +# modify this program under the same terms as Ruby itself. +# +# NOTE: You can find Japanese version of this document at: +# http://www.ruby-lang.org/ja/man/html/net_smtp.html +# +# $Id: smtp.rb 45111 2014-02-22 05:39:58Z naruse $ +# +# See Net::SMTP for documentation. +# + +require 'net/protocol' +require 'digest/md5' +require 'timeout' +begin + require 'openssl' +rescue LoadError +end + +module Net + + # Module mixed in to all SMTP error classes + module SMTPError + # This *class* is a module for backward compatibility. + # In later release, this module becomes a class. + end + + # Represents an SMTP authentication error. + class SMTPAuthenticationError < ProtoAuthError + include SMTPError + end + + # Represents SMTP error code 420 or 450, a temporary error. + class SMTPServerBusy < ProtoServerError + include SMTPError + end + + # Represents an SMTP command syntax error (error code 500) + class SMTPSyntaxError < ProtoSyntaxError + include SMTPError + end + + # Represents a fatal SMTP error (error code 5xx, except for 500) + class SMTPFatalError < ProtoFatalError + include SMTPError + end + + # Unexpected reply code returned from server. + class SMTPUnknownError < ProtoUnknownError + include SMTPError + end + + # Command is not supported on server. + class SMTPUnsupportedCommand < ProtocolError + include SMTPError + end + + # + # == What is This Library? + # + # This library provides functionality to send internet + # mail via SMTP, the Simple Mail Transfer Protocol. For details of + # SMTP itself, see [RFC2821] (http://www.ietf.org/rfc/rfc2821.txt). + # + # == What is This Library NOT? + # + # This library does NOT provide functions to compose internet mails. + # You must create them by yourself. If you want better mail support, + # try RubyMail or TMail or search for alternatives in + # {RubyGems.org}[https://rubygems.org/] or {The Ruby + # Toolbox}[https://www.ruby-toolbox.com/]. + # + # FYI: the official documentation on internet mail is: [RFC2822] (http://www.ietf.org/rfc/rfc2822.txt). + # + # == Examples + # + # === Sending Messages + # + # You must open a connection to an SMTP server before sending messages. + # The first argument is the address of your SMTP server, and the second + # argument is the port number. Using SMTP.start with a block is the simplest + # way to do this. This way, the SMTP connection is closed automatically + # after the block is executed. + # + # require 'net/smtp' + # Net::SMTP.start('your.smtp.server', 25) do |smtp| + # # Use the SMTP object smtp only in this block. + # end + # + # Replace 'your.smtp.server' with your SMTP server. Normally + # your system manager or internet provider supplies a server + # for you. + # + # Then you can send messages. + # + # msgstr = < + # To: Destination Address + # Subject: test message + # Date: Sat, 23 Jun 2001 16:26:43 +0900 + # Message-Id: + # + # This is a test message. + # END_OF_MESSAGE + # + # require 'net/smtp' + # Net::SMTP.start('your.smtp.server', 25) do |smtp| + # smtp.send_message msgstr, + # 'your@mail.address', + # 'his_address@example.com' + # end + # + # === Closing the Session + # + # You MUST close the SMTP session after sending messages, by calling + # the #finish method: + # + # # using SMTP#finish + # smtp = Net::SMTP.start('your.smtp.server', 25) + # smtp.send_message msgstr, 'from@address', 'to@address' + # smtp.finish + # + # You can also use the block form of SMTP.start/SMTP#start. This closes + # the SMTP session automatically: + # + # # using block form of SMTP.start + # Net::SMTP.start('your.smtp.server', 25) do |smtp| + # smtp.send_message msgstr, 'from@address', 'to@address' + # end + # + # I strongly recommend this scheme. This form is simpler and more robust. + # + # === HELO domain + # + # In almost all situations, you must provide a third argument + # to SMTP.start/SMTP#start. This is the domain name which you are on + # (the host to send mail from). It is called the "HELO domain". + # The SMTP server will judge whether it should send or reject + # the SMTP session by inspecting the HELO domain. + # + # Net::SMTP.start('your.smtp.server', 25, + # 'mail.from.domain') { |smtp| ... } + # + # === SMTP Authentication + # + # The Net::SMTP class supports three authentication schemes; + # PLAIN, LOGIN and CRAM MD5. (SMTP Authentication: [RFC2554]) + # To use SMTP authentication, pass extra arguments to + # SMTP.start/SMTP#start. + # + # # PLAIN + # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain', + # 'Your Account', 'Your Password', :plain) + # # LOGIN + # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain', + # 'Your Account', 'Your Password', :login) + # + # # CRAM MD5 + # Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain', + # 'Your Account', 'Your Password', :cram_md5) + # + class SMTP + + Revision = %q$Revision: 45111 $.split[1] + + # The default SMTP port number, 25. + def SMTP.default_port + 25 + end + + # The default mail submission port number, 587. + def SMTP.default_submission_port + 587 + end + + # The default SMTPS port number, 465. + def SMTP.default_tls_port + 465 + end + + class << self + alias default_ssl_port default_tls_port + end + + def SMTP.default_ssl_context + OpenSSL::SSL::SSLContext.new + end + + # + # Creates a new Net::SMTP object. + # + # +address+ is the hostname or ip address of your SMTP + # server. +port+ is the port to connect to; it defaults to + # port 25. + # + # This method does not open the TCP connection. You can use + # SMTP.start instead of SMTP.new if you want to do everything + # at once. Otherwise, follow SMTP.new with SMTP#start. + # + def initialize(address, port = nil) + @address = address + @port = (port || SMTP.default_port) + @esmtp = true + @capabilities = nil + @socket = nil + @started = false + @open_timeout = 30 + @read_timeout = 60 + @error_occurred = false + @debug_output = nil + @tls = false + @starttls = false + @ssl_context = nil + end + + # Provide human-readable stringification of class state. + def inspect + "#<#{self.class} #{@address}:#{@port} started=#{@started}>" + end + + # + # Set whether to use ESMTP or not. This should be done before + # calling #start. Note that if #start is called in ESMTP mode, + # and the connection fails due to a ProtocolError, the SMTP + # object will automatically switch to plain SMTP mode and + # retry (but not vice versa). + # + attr_accessor :esmtp + + # +true+ if the SMTP object uses ESMTP (which it does by default). + alias :esmtp? :esmtp + + # true if server advertises STARTTLS. + # You cannot get valid value before opening SMTP session. + def capable_starttls? + capable?('STARTTLS') + end + + def capable?(key) + return nil unless @capabilities + @capabilities[key] ? true : false + end + private :capable? + + # true if server advertises AUTH PLAIN. + # You cannot get valid value before opening SMTP session. + def capable_plain_auth? + auth_capable?('PLAIN') + end + + # true if server advertises AUTH LOGIN. + # You cannot get valid value before opening SMTP session. + def capable_login_auth? + auth_capable?('LOGIN') + end + + # true if server advertises AUTH CRAM-MD5. + # You cannot get valid value before opening SMTP session. + def capable_cram_md5_auth? + auth_capable?('CRAM-MD5') + end + + def auth_capable?(type) + return nil unless @capabilities + return false unless @capabilities['AUTH'] + @capabilities['AUTH'].include?(type) + end + private :auth_capable? + + # Returns supported authentication methods on this server. + # You cannot get valid value before opening SMTP session. + def capable_auth_types + return [] unless @capabilities + return [] unless @capabilities['AUTH'] + @capabilities['AUTH'] + end + + # true if this object uses SMTP/TLS (SMTPS). + def tls? + @tls + end + + alias ssl? tls? + + # Enables SMTP/TLS (SMTPS: SMTP over direct TLS connection) for + # this object. Must be called before the connection is established + # to have any effect. +context+ is a OpenSSL::SSL::SSLContext object. + def enable_tls(context = SMTP.default_ssl_context) + raise 'openssl library not installed' unless defined?(OpenSSL) + raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @starttls + @tls = true + @ssl_context = context + end + + alias enable_ssl enable_tls + + # Disables SMTP/TLS for this object. Must be called before the + # connection is established to have any effect. + def disable_tls + @tls = false + @ssl_context = nil + end + + alias disable_ssl disable_tls + + # Returns truth value if this object uses STARTTLS. + # If this object always uses STARTTLS, returns :always. + # If this object uses STARTTLS when the server support TLS, returns :auto. + def starttls? + @starttls + end + + # true if this object uses STARTTLS. + def starttls_always? + @starttls == :always + end + + # true if this object uses STARTTLS when server advertises STARTTLS. + def starttls_auto? + @starttls == :auto + end + + # Enables SMTP/TLS (STARTTLS) for this object. + # +context+ is a OpenSSL::SSL::SSLContext object. + def enable_starttls(context = SMTP.default_ssl_context) + raise 'openssl library not installed' unless defined?(OpenSSL) + raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @tls + @starttls = :always + @ssl_context = context + end + + # Enables SMTP/TLS (STARTTLS) for this object if server accepts. + # +context+ is a OpenSSL::SSL::SSLContext object. + def enable_starttls_auto(context = SMTP.default_ssl_context) + raise 'openssl library not installed' unless defined?(OpenSSL) + raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @tls + @starttls = :auto + @ssl_context = context + end + + # Disables SMTP/TLS (STARTTLS) for this object. Must be called + # before the connection is established to have any effect. + def disable_starttls + @starttls = false + @ssl_context = nil + end + + # The address of the SMTP server to connect to. + attr_reader :address + + # The port number of the SMTP server to connect to. + attr_reader :port + + # Seconds to wait while attempting to open a connection. + # If the connection cannot be opened within this time, a + # Net::OpenTimeout is raised. The default value is 30 seconds. + attr_accessor :open_timeout + + # Seconds to wait while reading one block (by one read(2) call). + # If the read(2) call does not complete within this time, a + # Net::ReadTimeout is raised. The default value is 60 seconds. + attr_reader :read_timeout + + # Set the number of seconds to wait until timing-out a read(2) + # call. + def read_timeout=(sec) + @socket.read_timeout = sec if @socket + @read_timeout = sec + end + + # + # WARNING: This method causes serious security holes. + # Use this method for only debugging. + # + # Set an output stream for debug logging. + # You must call this before #start. + # + # # example + # smtp = Net::SMTP.new(addr, port) + # smtp.set_debug_output $stderr + # smtp.start do |smtp| + # .... + # end + # + def debug_output=(arg) + @debug_output = arg + end + + alias set_debug_output debug_output= + + # + # SMTP session control + # + + # + # Creates a new Net::SMTP object and connects to the server. + # + # This method is equivalent to: + # + # Net::SMTP.new(address, port).start(helo_domain, account, password, authtype) + # + # === Example + # + # Net::SMTP.start('your.smtp.server') do |smtp| + # smtp.send_message msgstr, 'from@example.com', ['dest@example.com'] + # end + # + # === Block Usage + # + # If called with a block, the newly-opened Net::SMTP object is yielded + # to the block, and automatically closed when the block finishes. If called + # without a block, the newly-opened Net::SMTP object is returned to + # the caller, and it is the caller's responsibility to close it when + # finished. + # + # === Parameters + # + # +address+ is the hostname or ip address of your smtp server. + # + # +port+ is the port to connect to; it defaults to port 25. + # + # +helo+ is the _HELO_ _domain_ provided by the client to the + # server (see overview comments); it defaults to 'localhost'. + # + # The remaining arguments are used for SMTP authentication, if required + # or desired. +user+ is the account name; +secret+ is your password + # or other authentication token; and +authtype+ is the authentication + # type, one of :plain, :login, or :cram_md5. See the discussion of + # SMTP Authentication in the overview notes. + # + # === Errors + # + # This method may raise: + # + # * Net::SMTPAuthenticationError + # * Net::SMTPServerBusy + # * Net::SMTPSyntaxError + # * Net::SMTPFatalError + # * Net::SMTPUnknownError + # * Net::OpenTimeout + # * Net::ReadTimeout + # * IOError + # + def SMTP.start(address, port = nil, helo = 'localhost', + user = nil, secret = nil, authtype = nil, + &block) # :yield: smtp + new(address, port).start(helo, user, secret, authtype, &block) + end + + # +true+ if the SMTP session has been started. + def started? + @started + end + + # + # Opens a TCP connection and starts the SMTP session. + # + # === Parameters + # + # +helo+ is the _HELO_ _domain_ that you'll dispatch mails from; see + # the discussion in the overview notes. + # + # If both of +user+ and +secret+ are given, SMTP authentication + # will be attempted using the AUTH command. +authtype+ specifies + # the type of authentication to attempt; it must be one of + # :login, :plain, and :cram_md5. See the notes on SMTP Authentication + # in the overview. + # + # === Block Usage + # + # When this methods is called with a block, the newly-started SMTP + # object is yielded to the block, and automatically closed after + # the block call finishes. Otherwise, it is the caller's + # responsibility to close the session when finished. + # + # === Example + # + # This is very similar to the class method SMTP.start. + # + # require 'net/smtp' + # smtp = Net::SMTP.new('smtp.mail.server', 25) + # smtp.start(helo_domain, account, password, authtype) do |smtp| + # smtp.send_message msgstr, 'from@example.com', ['dest@example.com'] + # end + # + # The primary use of this method (as opposed to SMTP.start) + # is probably to set debugging (#set_debug_output) or ESMTP + # (#esmtp=), which must be done before the session is + # started. + # + # === Errors + # + # If session has already been started, an IOError will be raised. + # + # This method may raise: + # + # * Net::SMTPAuthenticationError + # * Net::SMTPServerBusy + # * Net::SMTPSyntaxError + # * Net::SMTPFatalError + # * Net::SMTPUnknownError + # * Net::OpenTimeout + # * Net::ReadTimeout + # * IOError + # + def start(helo = 'localhost', + user = nil, secret = nil, authtype = nil) # :yield: smtp + if block_given? + begin + do_start helo, user, secret, authtype + return yield(self) + ensure + do_finish + end + else + do_start helo, user, secret, authtype + return self + end + end + + # Finishes the SMTP session and closes TCP connection. + # Raises IOError if not started. + def finish + raise IOError, 'not yet started' unless started? + do_finish + end + + private + + def tcp_socket(address, port) + TCPSocket.open address, port + end + + def do_start(helo_domain, user, secret, authtype) + raise IOError, 'SMTP session already started' if @started + if user or secret + check_auth_method(authtype || DEFAULT_AUTH_TYPE) + check_auth_args user, secret + end + s = Timeout.timeout(@open_timeout, Net::OpenTimeout) do + tcp_socket(@address, @port) + end + logging "Connection opened: #{@address}:#{@port}" + @socket = new_internet_message_io(tls? ? tlsconnect(s) : s) + check_response critical { recv_response() } + do_helo helo_domain + if starttls_always? or (capable_starttls? and starttls_auto?) + unless capable_starttls? + raise SMTPUnsupportedCommand, + "STARTTLS is not supported on this server" + end + starttls + @socket = new_internet_message_io(tlsconnect(s)) + # helo response may be different after STARTTLS + do_helo helo_domain + end + authenticate user, secret, (authtype || DEFAULT_AUTH_TYPE) if user + @started = true + ensure + unless @started + # authentication failed, cancel connection. + s.close if s and not s.closed? + @socket = nil + end + end + + def ssl_socket(socket, context) + OpenSSL::SSL::SSLSocket.new socket, context + end + + def tlsconnect(s) + verified = false + s = ssl_socket(s, @ssl_context) + logging "TLS connection started" + s.sync_close = true + s.connect + if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE + s.post_connection_check(@address) + end + verified = true + s + ensure + s.close unless verified + end + + def new_internet_message_io(s) + io = InternetMessageIO.new(s) + io.read_timeout = @read_timeout + io.debug_output = @debug_output + io + end + + def do_helo(helo_domain) + res = @esmtp ? ehlo(helo_domain) : helo(helo_domain) + @capabilities = res.capabilities + rescue SMTPError + if @esmtp + @esmtp = false + @error_occurred = false + retry + end + raise + end + + def do_finish + quit if @socket and not @socket.closed? and not @error_occurred + ensure + @started = false + @error_occurred = false + @socket.close if @socket and not @socket.closed? + @socket = nil + end + + # + # Message Sending + # + + public + + # + # Sends +msgstr+ as a message. Single CR ("\r") and LF ("\n") found + # in the +msgstr+, are converted into the CR LF pair. You cannot send a + # binary message with this method. +msgstr+ should include both + # the message headers and body. + # + # +from_addr+ is a String representing the source mail address. + # + # +to_addr+ is a String or Strings or Array of Strings, representing + # the destination mail address or addresses. + # + # === Example + # + # Net::SMTP.start('smtp.example.com') do |smtp| + # smtp.send_message msgstr, + # 'from@example.com', + # ['dest@example.com', 'dest2@example.com'] + # end + # + # === Errors + # + # This method may raise: + # + # * Net::SMTPServerBusy + # * Net::SMTPSyntaxError + # * Net::SMTPFatalError + # * Net::SMTPUnknownError + # * Net::ReadTimeout + # * IOError + # + def send_message(msgstr, from_addr, *to_addrs) + raise IOError, 'closed session' unless @socket + mailfrom from_addr + rcptto_list(to_addrs) {data msgstr} + end + + alias send_mail send_message + alias sendmail send_message # obsolete + + # + # Opens a message writer stream and gives it to the block. + # The stream is valid only in the block, and has these methods: + # + # puts(str = ''):: outputs STR and CR LF. + # print(str):: outputs STR. + # printf(fmt, *args):: outputs sprintf(fmt,*args). + # write(str):: outputs STR and returns the length of written bytes. + # <<(str):: outputs STR and returns self. + # + # If a single CR ("\r") or LF ("\n") is found in the message, + # it is converted to the CR LF pair. You cannot send a binary + # message with this method. + # + # === Parameters + # + # +from_addr+ is a String representing the source mail address. + # + # +to_addr+ is a String or Strings or Array of Strings, representing + # the destination mail address or addresses. + # + # === Example + # + # Net::SMTP.start('smtp.example.com', 25) do |smtp| + # smtp.open_message_stream('from@example.com', ['dest@example.com']) do |f| + # f.puts 'From: from@example.com' + # f.puts 'To: dest@example.com' + # f.puts 'Subject: test message' + # f.puts + # f.puts 'This is a test message.' + # end + # end + # + # === Errors + # + # This method may raise: + # + # * Net::SMTPServerBusy + # * Net::SMTPSyntaxError + # * Net::SMTPFatalError + # * Net::SMTPUnknownError + # * Net::ReadTimeout + # * IOError + # + def open_message_stream(from_addr, *to_addrs, &block) # :yield: stream + raise IOError, 'closed session' unless @socket + mailfrom from_addr + rcptto_list(to_addrs) {data(&block)} + end + + alias ready open_message_stream # obsolete + + # + # Authentication + # + + public + + DEFAULT_AUTH_TYPE = :plain + + def authenticate(user, secret, authtype = DEFAULT_AUTH_TYPE) + check_auth_method authtype + check_auth_args user, secret + send auth_method(authtype), user, secret + end + + def auth_plain(user, secret) + check_auth_args user, secret + res = critical { + get_response('AUTH PLAIN ' + base64_encode("\0#{user}\0#{secret}")) + } + check_auth_response res + res + end + + def auth_login(user, secret) + check_auth_args user, secret + res = critical { + check_auth_continue get_response('AUTH LOGIN') + check_auth_continue get_response(base64_encode(user)) + get_response(base64_encode(secret)) + } + check_auth_response res + res + end + + def auth_cram_md5(user, secret) + check_auth_args user, secret + res = critical { + res0 = get_response('AUTH CRAM-MD5') + check_auth_continue res0 + crammed = cram_md5_response(secret, res0.cram_md5_challenge) + get_response(base64_encode("#{user} #{crammed}")) + } + check_auth_response res + res + end + + private + + def check_auth_method(type) + unless respond_to?(auth_method(type), true) + raise ArgumentError, "wrong authentication type #{type}" + end + end + + def auth_method(type) + "auth_#{type.to_s.downcase}".intern + end + + def check_auth_args(user, secret, authtype = DEFAULT_AUTH_TYPE) + unless user + raise ArgumentError, 'SMTP-AUTH requested but missing user name' + end + unless secret + raise ArgumentError, 'SMTP-AUTH requested but missing secret phrase' + end + end + + def base64_encode(str) + # expects "str" may not become too long + [str].pack('m').gsub(/\s+/, '') + end + + IMASK = 0x36 + OMASK = 0x5c + + # CRAM-MD5: [RFC2195] + def cram_md5_response(secret, challenge) + tmp = Digest::MD5.digest(cram_secret(secret, IMASK) + challenge) + Digest::MD5.hexdigest(cram_secret(secret, OMASK) + tmp) + end + + CRAM_BUFSIZE = 64 + + def cram_secret(secret, mask) + secret = Digest::MD5.digest(secret) if secret.size > CRAM_BUFSIZE + buf = secret.ljust(CRAM_BUFSIZE, "\0") + 0.upto(buf.size - 1) do |i| + buf[i] = (buf[i].ord ^ mask).chr + end + buf + end + + # + # SMTP command dispatcher + # + + public + + # Aborts the current mail transaction + + def rset + getok('RSET') + end + + def starttls + getok('STARTTLS') + end + + def helo(domain) + getok("HELO #{domain}") + end + + def ehlo(domain) + getok("EHLO #{domain}") + end + + def mailfrom(from_addr) + if $SAFE > 0 + raise SecurityError, 'tainted from_addr' if from_addr.tainted? + end + getok("MAIL FROM:<#{from_addr}>") + end + + def rcptto_list(to_addrs) + raise ArgumentError, 'mail destination not given' if to_addrs.empty? + ok_users = [] + unknown_users = [] + to_addrs.flatten.each do |addr| + begin + rcptto addr + rescue SMTPAuthenticationError + unknown_users << addr.dump + else + ok_users << addr + end + end + raise ArgumentError, 'mail destination not given' if ok_users.empty? + ret = yield + unless unknown_users.empty? + raise SMTPAuthenticationError, "failed to deliver for #{unknown_users.join(', ')}" + end + ret + end + + def rcptto(to_addr) + if $SAFE > 0 + raise SecurityError, 'tainted to_addr' if to_addr.tainted? + end + getok("RCPT TO:<#{to_addr}>") + end + + # This method sends a message. + # If +msgstr+ is given, sends it as a message. + # If block is given, yield a message writer stream. + # You must write message before the block is closed. + # + # # Example 1 (by string) + # smtp.data(< +# Documentation:: William Webber and Wakou Aoyama +# +# This file holds the class Net::Telnet, which provides client-side +# telnet functionality. +# +# For documentation, see Net::Telnet. +# + +require "net/protocol" +require "English" + +module Net + + # + # == Net::Telnet + # + # Provides telnet client functionality. + # + # This class also has, through delegation, all the methods of a + # socket object (by default, a +TCPSocket+, but can be set by the + # +Proxy+ option to new()). This provides methods such as + # close() to end the session and sysread() to read + # data directly from the host, instead of via the waitfor() + # mechanism. Note that if you do use sysread() directly + # when in telnet mode, you should probably pass the output through + # preprocess() to extract telnet command sequences. + # + # == Overview + # + # The telnet protocol allows a client to login remotely to a user + # account on a server and execute commands via a shell. The equivalent + # is done by creating a Net::Telnet class with the +Host+ option + # set to your host, calling #login() with your user and password, + # issuing one or more #cmd() calls, and then calling #close() + # to end the session. The #waitfor(), #print(), #puts(), and + # #write() methods, which #cmd() is implemented on top of, are + # only needed if you are doing something more complicated. + # + # A Net::Telnet object can also be used to connect to non-telnet + # services, such as SMTP or HTTP. In this case, you normally + # want to provide the +Port+ option to specify the port to + # connect to, and set the +Telnetmode+ option to false to prevent + # the client from attempting to interpret telnet command sequences. + # Generally, #login() will not work with other protocols, and you + # have to handle authentication yourself. + # + # For some protocols, it will be possible to specify the +Prompt+ + # option once when you create the Telnet object and use #cmd() calls; + # for others, you will have to specify the response sequence to + # look for as the Match option to every #cmd() call, or call + # #puts() and #waitfor() directly; for yet others, you will have + # to use #sysread() instead of #waitfor() and parse server + # responses yourself. + # + # It is worth noting that when you create a new Net::Telnet object, + # you can supply a proxy IO channel via the Proxy option. This + # can be used to attach the Telnet object to other Telnet objects, + # to already open sockets, or to any read-write IO object. This + # can be useful, for instance, for setting up a test fixture for + # unit testing. + # + # == Examples + # + # === Log in and send a command, echoing all output to stdout + # + # localhost = Net::Telnet::new("Host" => "localhost", + # "Timeout" => 10, + # "Prompt" => /[$%#>] \z/n) + # localhost.login("username", "password") { |c| print c } + # localhost.cmd("command") { |c| print c } + # localhost.close + # + # + # === Check a POP server to see if you have mail + # + # pop = Net::Telnet::new("Host" => "your_destination_host_here", + # "Port" => 110, + # "Telnetmode" => false, + # "Prompt" => /^\+OK/n) + # pop.cmd("user " + "your_username_here") { |c| print c } + # pop.cmd("pass " + "your_password_here") { |c| print c } + # pop.cmd("list") { |c| print c } + # + # == References + # + # There are a large number of RFCs relevant to the Telnet protocol. + # RFCs 854-861 define the base protocol. For a complete listing + # of relevant RFCs, see + # http://www.omnifarious.org/~hopper/technical/telnet-rfc.html + # + class Telnet + + # :stopdoc: + IAC = 255.chr # "\377" # "\xff" # interpret as command + DONT = 254.chr # "\376" # "\xfe" # you are not to use option + DO = 253.chr # "\375" # "\xfd" # please, you use option + WONT = 252.chr # "\374" # "\xfc" # I won't use option + WILL = 251.chr # "\373" # "\xfb" # I will use option + SB = 250.chr # "\372" # "\xfa" # interpret as subnegotiation + GA = 249.chr # "\371" # "\xf9" # you may reverse the line + EL = 248.chr # "\370" # "\xf8" # erase the current line + EC = 247.chr # "\367" # "\xf7" # erase the current character + AYT = 246.chr # "\366" # "\xf6" # are you there + AO = 245.chr # "\365" # "\xf5" # abort output--but let prog finish + IP = 244.chr # "\364" # "\xf4" # interrupt process--permanently + BREAK = 243.chr # "\363" # "\xf3" # break + DM = 242.chr # "\362" # "\xf2" # data mark--for connect. cleaning + NOP = 241.chr # "\361" # "\xf1" # nop + SE = 240.chr # "\360" # "\xf0" # end sub negotiation + EOR = 239.chr # "\357" # "\xef" # end of record (transparent mode) + ABORT = 238.chr # "\356" # "\xee" # Abort process + SUSP = 237.chr # "\355" # "\xed" # Suspend process + EOF = 236.chr # "\354" # "\xec" # End of file + SYNCH = 242.chr # "\362" # "\xf2" # for telfunc calls + + OPT_BINARY = 0.chr # "\000" # "\x00" # Binary Transmission + OPT_ECHO = 1.chr # "\001" # "\x01" # Echo + OPT_RCP = 2.chr # "\002" # "\x02" # Reconnection + OPT_SGA = 3.chr # "\003" # "\x03" # Suppress Go Ahead + OPT_NAMS = 4.chr # "\004" # "\x04" # Approx Message Size Negotiation + OPT_STATUS = 5.chr # "\005" # "\x05" # Status + OPT_TM = 6.chr # "\006" # "\x06" # Timing Mark + OPT_RCTE = 7.chr # "\a" # "\x07" # Remote Controlled Trans and Echo + OPT_NAOL = 8.chr # "\010" # "\x08" # Output Line Width + OPT_NAOP = 9.chr # "\t" # "\x09" # Output Page Size + OPT_NAOCRD = 10.chr # "\n" # "\x0a" # Output Carriage-Return Disposition + OPT_NAOHTS = 11.chr # "\v" # "\x0b" # Output Horizontal Tab Stops + OPT_NAOHTD = 12.chr # "\f" # "\x0c" # Output Horizontal Tab Disposition + OPT_NAOFFD = 13.chr # "\r" # "\x0d" # Output Formfeed Disposition + OPT_NAOVTS = 14.chr # "\016" # "\x0e" # Output Vertical Tabstops + OPT_NAOVTD = 15.chr # "\017" # "\x0f" # Output Vertical Tab Disposition + OPT_NAOLFD = 16.chr # "\020" # "\x10" # Output Linefeed Disposition + OPT_XASCII = 17.chr # "\021" # "\x11" # Extended ASCII + OPT_LOGOUT = 18.chr # "\022" # "\x12" # Logout + OPT_BM = 19.chr # "\023" # "\x13" # Byte Macro + OPT_DET = 20.chr # "\024" # "\x14" # Data Entry Terminal + OPT_SUPDUP = 21.chr # "\025" # "\x15" # SUPDUP + OPT_SUPDUPOUTPUT = 22.chr # "\026" # "\x16" # SUPDUP Output + OPT_SNDLOC = 23.chr # "\027" # "\x17" # Send Location + OPT_TTYPE = 24.chr # "\030" # "\x18" # Terminal Type + OPT_EOR = 25.chr # "\031" # "\x19" # End of Record + OPT_TUID = 26.chr # "\032" # "\x1a" # TACACS User Identification + OPT_OUTMRK = 27.chr # "\e" # "\x1b" # Output Marking + OPT_TTYLOC = 28.chr # "\034" # "\x1c" # Terminal Location Number + OPT_3270REGIME = 29.chr # "\035" # "\x1d" # Telnet 3270 Regime + OPT_X3PAD = 30.chr # "\036" # "\x1e" # X.3 PAD + OPT_NAWS = 31.chr # "\037" # "\x1f" # Negotiate About Window Size + OPT_TSPEED = 32.chr # " " # "\x20" # Terminal Speed + OPT_LFLOW = 33.chr # "!" # "\x21" # Remote Flow Control + OPT_LINEMODE = 34.chr # "\"" # "\x22" # Linemode + OPT_XDISPLOC = 35.chr # "#" # "\x23" # X Display Location + OPT_OLD_ENVIRON = 36.chr # "$" # "\x24" # Environment Option + OPT_AUTHENTICATION = 37.chr # "%" # "\x25" # Authentication Option + OPT_ENCRYPT = 38.chr # "&" # "\x26" # Encryption Option + OPT_NEW_ENVIRON = 39.chr # "'" # "\x27" # New Environment Option + OPT_EXOPL = 255.chr # "\377" # "\xff" # Extended-Options-List + + NULL = "\000" + CR = "\015" + LF = "\012" + EOL = CR + LF + REVISION = '$Id: telnet.rb 35304 2012-04-11 21:20:51Z drbrain $' + # :startdoc: + + # + # Creates a new Net::Telnet object. + # + # Attempts to connect to the host (unless the Proxy option is + # provided: see below). If a block is provided, it is yielded + # status messages on the attempt to connect to the server, of + # the form: + # + # Trying localhost... + # Connected to localhost. + # + # +options+ is a hash of options. The following example lists + # all options and their default values. + # + # host = Net::Telnet::new( + # "Host" => "localhost", # default: "localhost" + # "Port" => 23, # default: 23 + # "Binmode" => false, # default: false + # "Output_log" => "output_log", # default: nil (no output) + # "Dump_log" => "dump_log", # default: nil (no output) + # "Prompt" => /[$%#>] \z/n, # default: /[$%#>] \z/n + # "Telnetmode" => true, # default: true + # "Timeout" => 10, # default: 10 + # # if ignore timeout then set "Timeout" to false. + # "Waittime" => 0, # default: 0 + # "Proxy" => proxy # default: nil + # # proxy is Net::Telnet or IO object + # ) + # + # The options have the following meanings: + # + # Host:: the hostname or IP address of the host to connect to, as a String. + # Defaults to "localhost". + # + # Port:: the port to connect to. Defaults to 23. + # + # Binmode:: if false (the default), newline substitution is performed. + # Outgoing LF is + # converted to CRLF, and incoming CRLF is converted to LF. If + # true, this substitution is not performed. This value can + # also be set with the #binmode() method. The + # outgoing conversion only applies to the #puts() and #print() + # methods, not the #write() method. The precise nature of + # the newline conversion is also affected by the telnet options + # SGA and BIN. + # + # Output_log:: the name of the file to write connection status messages + # and all received traffic to. In the case of a proper + # Telnet session, this will include the client input as + # echoed by the host; otherwise, it only includes server + # responses. Output is appended verbatim to this file. + # By default, no output log is kept. + # + # Dump_log:: as for Output_log, except that output is written in hexdump + # format (16 bytes per line as hex pairs, followed by their + # printable equivalent), with connection status messages + # preceded by '#', sent traffic preceded by '>', and + # received traffic preceded by '<'. By default, not dump log + # is kept. + # + # Prompt:: a regular expression matching the host's command-line prompt + # sequence. This is needed by the Telnet class to determine + # when the output from a command has finished and the host is + # ready to receive a new command. By default, this regular + # expression is /[$%#>] \z/n. + # + # Telnetmode:: a boolean value, true by default. In telnet mode, + # traffic received from the host is parsed for special + # command sequences, and these sequences are escaped + # in outgoing traffic sent using #puts() or #print() + # (but not #write()). If you are using the Net::Telnet + # object to connect to a non-telnet service (such as + # SMTP or POP), this should be set to "false" to prevent + # undesired data corruption. This value can also be set + # by the #telnetmode() method. + # + # Timeout:: the number of seconds to wait before timing out both the + # initial attempt to connect to host (in this constructor), + # which raises a Net::OpenTimeout, and all attempts to read data + # from the host, which raises a Net::ReadTimeout (in #waitfor(), + # #cmd(), and #login()). The default value is 10 seconds. + # You can disable the timeout by setting this value to false. + # In this case, the connect attempt will eventually timeout + # on the underlying connect(2) socket call with an + # Errno::ETIMEDOUT error (but generally only after a few + # minutes), but other attempts to read data from the host + # will hang indefinitely if no data is forthcoming. + # + # Waittime:: the amount of time to wait after seeing what looks like a + # prompt (that is, received data that matches the Prompt + # option regular expression) to see if more data arrives. + # If more data does arrive in this time, Net::Telnet assumes + # that what it saw was not really a prompt. This is to try to + # avoid false matches, but it can also lead to missing real + # prompts (if, for instance, a background process writes to + # the terminal soon after the prompt is displayed). By + # default, set to 0, meaning not to wait for more data. + # + # Proxy:: a proxy object to used instead of opening a direct connection + # to the host. Must be either another Net::Telnet object or + # an IO object. If it is another Net::Telnet object, this + # instance will use that one's socket for communication. If an + # IO object, it is used directly for communication. Any other + # kind of object will cause an error to be raised. + # + def initialize(options) # :yield: mesg + @options = options + @options["Host"] = "localhost" unless @options.has_key?("Host") + @options["Port"] = 23 unless @options.has_key?("Port") + @options["Prompt"] = /[$%#>] \z/n unless @options.has_key?("Prompt") + @options["Timeout"] = 10 unless @options.has_key?("Timeout") + @options["Waittime"] = 0 unless @options.has_key?("Waittime") + unless @options.has_key?("Binmode") + @options["Binmode"] = false + else + unless (true == @options["Binmode"] or false == @options["Binmode"]) + raise ArgumentError, "Binmode option must be true or false" + end + end + + unless @options.has_key?("Telnetmode") + @options["Telnetmode"] = true + else + unless (true == @options["Telnetmode"] or false == @options["Telnetmode"]) + raise ArgumentError, "Telnetmode option must be true or false" + end + end + + @telnet_option = { "SGA" => false, "BINARY" => false } + + if @options.has_key?("Output_log") + @log = File.open(@options["Output_log"], 'a+') + @log.sync = true + @log.binmode + end + + if @options.has_key?("Dump_log") + @dumplog = File.open(@options["Dump_log"], 'a+') + @dumplog.sync = true + @dumplog.binmode + def @dumplog.log_dump(dir, x) # :nodoc: + len = x.length + addr = 0 + offset = 0 + while 0 < len + if len < 16 + line = x[offset, len] + else + line = x[offset, 16] + end + hexvals = line.unpack('H*')[0] + hexvals += ' ' * (32 - hexvals.length) + hexvals = format("%s %s %s %s " * 4, *hexvals.unpack('a2' * 16)) + line = line.gsub(/[\000-\037\177-\377]/n, '.') + printf "%s 0x%5.5x: %s%s\n", dir, addr, hexvals, line + addr += 16 + offset += 16 + len -= 16 + end + print "\n" + end + end + + if @options.has_key?("Proxy") + if @options["Proxy"].kind_of?(Net::Telnet) + @sock = @options["Proxy"].sock + elsif @options["Proxy"].kind_of?(IO) + @sock = @options["Proxy"] + else + raise "Error: Proxy must be an instance of Net::Telnet or IO." + end + else + message = "Trying " + @options["Host"] + "...\n" + yield(message) if block_given? + @log.write(message) if @options.has_key?("Output_log") + @dumplog.log_dump('#', message) if @options.has_key?("Dump_log") + + begin + if @options["Timeout"] == false + @sock = TCPSocket.open(@options["Host"], @options["Port"]) + else + Timeout.timeout(@options["Timeout"], Net::OpenTimeout) do + @sock = TCPSocket.open(@options["Host"], @options["Port"]) + end + end + rescue Net::OpenTimeout + raise Net::OpenTimeout, "timed out while opening a connection to the host" + rescue + @log.write($ERROR_INFO.to_s + "\n") if @options.has_key?("Output_log") + @dumplog.log_dump('#', $ERROR_INFO.to_s + "\n") if @options.has_key?("Dump_log") + raise + end + @sock.sync = true + @sock.binmode + + message = "Connected to " + @options["Host"] + ".\n" + yield(message) if block_given? + @log.write(message) if @options.has_key?("Output_log") + @dumplog.log_dump('#', message) if @options.has_key?("Dump_log") + end + + end # initialize + + # The socket the Telnet object is using. Note that this object becomes + # a delegate of the Telnet object, so normally you invoke its methods + # directly on the Telnet object. + attr :sock + + # Set telnet command interpretation on (+mode+ == true) or off + # (+mode+ == false), or return the current value (+mode+ not + # provided). It should be on for true telnet sessions, off if + # using Net::Telnet to connect to a non-telnet service such + # as SMTP. + def telnetmode(mode = nil) + case mode + when nil + @options["Telnetmode"] + when true, false + @options["Telnetmode"] = mode + else + raise ArgumentError, "argument must be true or false, or missing" + end + end + + # Turn telnet command interpretation on (true) or off (false). It + # should be on for true telnet sessions, off if using Net::Telnet + # to connect to a non-telnet service such as SMTP. + def telnetmode=(mode) + if (true == mode or false == mode) + @options["Telnetmode"] = mode + else + raise ArgumentError, "argument must be true or false" + end + end + + # Turn newline conversion on (+mode+ == false) or off (+mode+ == true), + # or return the current value (+mode+ is not specified). + def binmode(mode = nil) + case mode + when nil + @options["Binmode"] + when true, false + @options["Binmode"] = mode + else + raise ArgumentError, "argument must be true or false" + end + end + + # Turn newline conversion on (false) or off (true). + def binmode=(mode) + if (true == mode or false == mode) + @options["Binmode"] = mode + else + raise ArgumentError, "argument must be true or false" + end + end + + # Preprocess received data from the host. + # + # Performs newline conversion and detects telnet command sequences. + # Called automatically by #waitfor(). You should only use this + # method yourself if you have read input directly using sysread() + # or similar, and even then only if in telnet mode. + def preprocess(string) + # combine CR+NULL into CR + string = string.gsub(/#{CR}#{NULL}/no, CR) if @options["Telnetmode"] + + # combine EOL into "\n" + string = string.gsub(/#{EOL}/no, "\n") unless @options["Binmode"] + + # remove NULL + string = string.gsub(/#{NULL}/no, '') unless @options["Binmode"] + + string.gsub(/#{IAC}( + [#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]| + [#{DO}#{DONT}#{WILL}#{WONT}] + [#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}]| + #{SB}[^#{IAC}]*#{IAC}#{SE} + )/xno) do + if IAC == $1 # handle escaped IAC characters + IAC + elsif AYT == $1 # respond to "IAC AYT" (are you there) + self.write("nobody here but us pigeons" + EOL) + '' + elsif DO[0] == $1[0] # respond to "IAC DO x" + if OPT_BINARY[0] == $1[1] + @telnet_option["BINARY"] = true + self.write(IAC + WILL + OPT_BINARY) + else + self.write(IAC + WONT + $1[1..1]) + end + '' + elsif DONT[0] == $1[0] # respond to "IAC DON'T x" with "IAC WON'T x" + self.write(IAC + WONT + $1[1..1]) + '' + elsif WILL[0] == $1[0] # respond to "IAC WILL x" + if OPT_BINARY[0] == $1[1] + self.write(IAC + DO + OPT_BINARY) + elsif OPT_ECHO[0] == $1[1] + self.write(IAC + DO + OPT_ECHO) + elsif OPT_SGA[0] == $1[1] + @telnet_option["SGA"] = true + self.write(IAC + DO + OPT_SGA) + else + self.write(IAC + DONT + $1[1..1]) + end + '' + elsif WONT[0] == $1[0] # respond to "IAC WON'T x" + if OPT_ECHO[0] == $1[1] + self.write(IAC + DONT + OPT_ECHO) + elsif OPT_SGA[0] == $1[1] + @telnet_option["SGA"] = false + self.write(IAC + DONT + OPT_SGA) + else + self.write(IAC + DONT + $1[1..1]) + end + '' + else + '' + end + end + end # preprocess + + # Read data from the host until a certain sequence is matched. + # + # If a block is given, the received data will be yielded as it + # is read in (not necessarily all in one go), or nil if EOF + # occurs before any data is received. Whether a block is given + # or not, all data read will be returned in a single string, or again + # nil if EOF occurs before any data is received. Note that + # received data includes the matched sequence we were looking for. + # + # +options+ can be either a regular expression or a hash of options. + # If a regular expression, this specifies the data to wait for. + # If a hash, this can specify the following options: + # + # Match:: a regular expression, specifying the data to wait for. + # Prompt:: as for Match; used only if Match is not specified. + # String:: as for Match, except a string that will be converted + # into a regular expression. Used only if Match and + # Prompt are not specified. + # Timeout:: the number of seconds to wait for data from the host + # before raising a Timeout::Error. If set to false, + # no timeout will occur. If not specified, the + # Timeout option value specified when this instance + # was created will be used, or, failing that, the + # default value of 10 seconds. + # Waittime:: the number of seconds to wait after matching against + # the input data to see if more data arrives. If more + # data arrives within this time, we will judge ourselves + # not to have matched successfully, and will continue + # trying to match. If not specified, the Waittime option + # value specified when this instance was created will be + # used, or, failing that, the default value of 0 seconds, + # which means not to wait for more input. + # FailEOF:: if true, when the remote end closes the connection then an + # EOFError will be raised. Otherwise, defaults to the old + # behaviour that the function will return whatever data + # has been received already, or nil if nothing was received. + # + def waitfor(options) # :yield: recvdata + time_out = @options["Timeout"] + waittime = @options["Waittime"] + fail_eof = @options["FailEOF"] + + if options.kind_of?(Hash) + prompt = if options.has_key?("Match") + options["Match"] + elsif options.has_key?("Prompt") + options["Prompt"] + elsif options.has_key?("String") + Regexp.new( Regexp.quote(options["String"]) ) + end + time_out = options["Timeout"] if options.has_key?("Timeout") + waittime = options["Waittime"] if options.has_key?("Waittime") + fail_eof = options["FailEOF"] if options.has_key?("FailEOF") + else + prompt = options + end + + if time_out == false + time_out = nil + end + + line = '' + buf = '' + rest = '' + until(prompt === line and not IO::select([@sock], nil, nil, waittime)) + unless IO::select([@sock], nil, nil, time_out) + raise Net::ReadTimeout, "timed out while waiting for more data" + end + begin + c = @sock.readpartial(1024 * 1024) + @dumplog.log_dump('<', c) if @options.has_key?("Dump_log") + if @options["Telnetmode"] + c = rest + c + if Integer(c.rindex(/#{IAC}#{SE}/no) || 0) < + Integer(c.rindex(/#{IAC}#{SB}/no) || 0) + buf = preprocess(c[0 ... c.rindex(/#{IAC}#{SB}/no)]) + rest = c[c.rindex(/#{IAC}#{SB}/no) .. -1] + elsif pt = c.rindex(/#{IAC}[^#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]?\z/no) || + c.rindex(/\r\z/no) + buf = preprocess(c[0 ... pt]) + rest = c[pt .. -1] + else + buf = preprocess(c) + rest = '' + end + else + # Not Telnetmode. + # + # We cannot use preprocess() on this data, because that + # method makes some Telnetmode-specific assumptions. + buf = rest + c + rest = '' + unless @options["Binmode"] + if pt = buf.rindex(/\r\z/no) + buf = buf[0 ... pt] + rest = buf[pt .. -1] + end + buf.gsub!(/#{EOL}/no, "\n") + end + end + @log.print(buf) if @options.has_key?("Output_log") + line += buf + yield buf if block_given? + rescue EOFError # End of file reached + raise if fail_eof + if line == '' + line = nil + yield nil if block_given? + end + break + end + end + line + end + + # Write +string+ to the host. + # + # Does not perform any conversions on +string+. Will log +string+ to the + # dumplog, if the Dump_log option is set. + def write(string) + length = string.length + while 0 < length + IO::select(nil, [@sock]) + @dumplog.log_dump('>', string[-length..-1]) if @options.has_key?("Dump_log") + length -= @sock.syswrite(string[-length..-1]) + end + end + + # Sends a string to the host. + # + # This does _not_ automatically append a newline to the string. Embedded + # newlines may be converted and telnet command sequences escaped + # depending upon the values of telnetmode, binmode, and telnet options + # set by the host. + def print(string) + string = string.gsub(/#{IAC}/no, IAC + IAC) if @options["Telnetmode"] + + if @options["Binmode"] + self.write(string) + else + if @telnet_option["BINARY"] and @telnet_option["SGA"] + # IAC WILL SGA IAC DO BIN send EOL --> CR + self.write(string.gsub(/\n/n, CR)) + elsif @telnet_option["SGA"] + # IAC WILL SGA send EOL --> CR+NULL + self.write(string.gsub(/\n/n, CR + NULL)) + else + # NONE send EOL --> CR+LF + self.write(string.gsub(/\n/n, EOL)) + end + end + end + + # Sends a string to the host. + # + # Same as #print(), but appends a newline to the string. + def puts(string) + self.print(string + "\n") + end + + # Send a command to the host. + # + # More exactly, sends a string to the host, and reads in all received + # data until is sees the prompt or other matched sequence. + # + # If a block is given, the received data will be yielded to it as + # it is read in. Whether a block is given or not, the received data + # will be return as a string. Note that the received data includes + # the prompt and in most cases the host's echo of our command. + # + # +options+ is either a String, specified the string or command to + # send to the host; or it is a hash of options. If a hash, the + # following options can be specified: + # + # String:: the command or other string to send to the host. + # Match:: a regular expression, the sequence to look for in + # the received data before returning. If not specified, + # the Prompt option value specified when this instance + # was created will be used, or, failing that, the default + # prompt of /[$%#>] \z/n. + # Timeout:: the seconds to wait for data from the host before raising + # a Timeout error. If not specified, the Timeout option + # value specified when this instance was created will be + # used, or, failing that, the default value of 10 seconds. + # + # The command or other string will have the newline sequence appended + # to it. + def cmd(options) # :yield: recvdata + match = @options["Prompt"] + time_out = @options["Timeout"] + fail_eof = @options["FailEOF"] + + if options.kind_of?(Hash) + string = options["String"] + match = options["Match"] if options.has_key?("Match") + time_out = options["Timeout"] if options.has_key?("Timeout") + fail_eof = options["FailEOF"] if options.has_key?("FailEOF") + else + string = options + end + + self.puts(string) + if block_given? + waitfor({"Prompt" => match, "Timeout" => time_out, "FailEOF" => fail_eof}){|c| yield c } + else + waitfor({"Prompt" => match, "Timeout" => time_out, "FailEOF" => fail_eof}) + end + end + + # Login to the host with a given username and password. + # + # The username and password can either be provided as two string + # arguments in that order, or as a hash with keys "Name" and + # "Password". + # + # This method looks for the strings "login" and "Password" from the + # host to determine when to send the username and password. If the + # login sequence does not follow this pattern (for instance, you + # are connecting to a service other than telnet), you will need + # to handle login yourself. + # + # The password can be omitted, either by only + # provided one String argument, which will be used as the username, + # or by providing a has that has no "Password" key. In this case, + # the method will not look for the "Password:" prompt; if it is + # sent, it will have to be dealt with by later calls. + # + # The method returns all data received during the login process from + # the host, including the echoed username but not the password (which + # the host should not echo). If a block is passed in, this received + # data is also yielded to the block as it is received. + def login(options, password = nil) # :yield: recvdata + login_prompt = /[Ll]ogin[: ]*\z/n + password_prompt = /[Pp]ass(?:word|phrase)[: ]*\z/n + if options.kind_of?(Hash) + username = options["Name"] + password = options["Password"] + login_prompt = options["LoginPrompt"] if options["LoginPrompt"] + password_prompt = options["PasswordPrompt"] if options["PasswordPrompt"] + else + username = options + end + + if block_given? + line = waitfor(login_prompt){|c| yield c } + if password + line += cmd({"String" => username, + "Match" => password_prompt}){|c| yield c } + line += cmd(password){|c| yield c } + else + line += cmd(username){|c| yield c } + end + else + line = waitfor(login_prompt) + if password + line += cmd({"String" => username, + "Match" => password_prompt}) + line += cmd(password) + else + line += cmd(username) + end + end + line + end + + # Closes the connection + def close + @sock.close + end + + end # class Telnet +end # module Net + diff --git a/ruby/lib/ruby/2.1.0/observer.rb b/ruby/lib/ruby/2.1.0/observer.rb new file mode 100644 index 0000000..99610f7 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/observer.rb @@ -0,0 +1,203 @@ +# +# Implementation of the _Observer_ object-oriented design pattern. The +# following documentation is copied, with modifications, from "Programming +# Ruby", by Hunt and Thomas; http://www.ruby-doc.org/docs/ProgrammingRuby/html/lib_patterns.html. +# +# See Observable for more info. + +# The Observer pattern (also known as publish/subscribe) provides a simple +# mechanism for one object to inform a set of interested third-party objects +# when its state changes. +# +# == Mechanism +# +# The notifying class mixes in the +Observable+ +# module, which provides the methods for managing the associated observer +# objects. +# +# The observable object must: +# * assert that it has +#changed+ +# * call +#notify_observers+ +# +# An observer subscribes to updates using Observable#add_observer, which also +# specifies the method called via #notify_observers. The default method for +# #notify_observers is #update. +# +# === Example +# +# The following example demonstrates this nicely. A +Ticker+, when run, +# continually receives the stock +Price+ for its @symbol. A +Warner+ +# is a general observer of the price, and two warners are demonstrated, a +# +WarnLow+ and a +WarnHigh+, which print a warning if the price is below or +# above their set limits, respectively. +# +# The +update+ callback allows the warners to run without being explicitly +# called. The system is set up with the +Ticker+ and several observers, and the +# observers do their duty without the top-level code having to interfere. +# +# Note that the contract between publisher and subscriber (observable and +# observer) is not declared or enforced. The +Ticker+ publishes a time and a +# price, and the warners receive that. But if you don't ensure that your +# contracts are correct, nothing else can warn you. +# +# require "observer" +# +# class Ticker ### Periodically fetch a stock price. +# include Observable +# +# def initialize(symbol) +# @symbol = symbol +# end +# +# def run +# last_price = nil +# loop do +# price = Price.fetch(@symbol) +# print "Current price: #{price}\n" +# if price != last_price +# changed # notify observers +# last_price = price +# notify_observers(Time.now, price) +# end +# sleep 1 +# end +# end +# end +# +# class Price ### A mock class to fetch a stock price (60 - 140). +# def self.fetch(symbol) +# 60 + rand(80) +# end +# end +# +# class Warner ### An abstract observer of Ticker objects. +# def initialize(ticker, limit) +# @limit = limit +# ticker.add_observer(self) +# end +# end +# +# class WarnLow < Warner +# def update(time, price) # callback for observer +# if price < @limit +# print "--- #{time.to_s}: Price below #@limit: #{price}\n" +# end +# end +# end +# +# class WarnHigh < Warner +# def update(time, price) # callback for observer +# if price > @limit +# print "+++ #{time.to_s}: Price above #@limit: #{price}\n" +# end +# end +# end +# +# ticker = Ticker.new("MSFT") +# WarnLow.new(ticker, 80) +# WarnHigh.new(ticker, 120) +# ticker.run +# +# Produces: +# +# Current price: 83 +# Current price: 75 +# --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75 +# Current price: 90 +# Current price: 134 +# +++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134 +# Current price: 134 +# Current price: 112 +# Current price: 79 +# --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79 +module Observable + + # + # Add +observer+ as an observer on this object. so that it will receive + # notifications. + # + # +observer+:: the object that will be notified of changes. + # +func+:: Symbol naming the method that will be called when this Observable + # has changes. + # + # This method must return true for +observer.respond_to?+ and will + # receive *arg when #notify_observers is called, where + # *arg is the value passed to #notify_observers by this + # Observable + def add_observer(observer, func=:update) + @observer_peers = {} unless defined? @observer_peers + unless observer.respond_to? func + raise NoMethodError, "observer does not respond to `#{func.to_s}'" + end + @observer_peers[observer] = func + end + + # + # Remove +observer+ as an observer on this object so that it will no longer + # receive notifications. + # + # +observer+:: An observer of this Observable + def delete_observer(observer) + @observer_peers.delete observer if defined? @observer_peers + end + + # + # Remove all observers associated with this object. + # + def delete_observers + @observer_peers.clear if defined? @observer_peers + end + + # + # Return the number of observers associated with this object. + # + def count_observers + if defined? @observer_peers + @observer_peers.size + else + 0 + end + end + + # + # Set the changed state of this object. Notifications will be sent only if + # the changed +state+ is +true+. + # + # +state+:: Boolean indicating the changed state of this Observable. + # + def changed(state=true) + @observer_state = state + end + + # + # Returns true if this object's state has been changed since the last + # #notify_observers call. + # + def changed? + if defined? @observer_state and @observer_state + true + else + false + end + end + + # + # Notify observers of a change in state *if* this object's changed state is + # +true+. + # + # This will invoke the method named in #add_observer, passing *arg. + # The changed state is then set to +false+. + # + # *arg:: Any arguments to pass to the observers. + def notify_observers(*arg) + if defined? @observer_state and @observer_state + if defined? @observer_peers + @observer_peers.each do |k, v| + k.send v, *arg + end + end + @observer_state = false + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/open-uri.rb b/ruby/lib/ruby/2.1.0/open-uri.rb new file mode 100644 index 0000000..264c850 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/open-uri.rb @@ -0,0 +1,789 @@ +require 'uri' +require 'stringio' +require 'time' + +module Kernel + private + alias open_uri_original_open open # :nodoc: + class << self + alias open_uri_original_open open # :nodoc: + end + + # Allows the opening of various resources including URIs. + # + # If the first argument responds to the 'open' method, 'open' is called on + # it with the rest of the arguments. + # + # If the first argument is a string that begins with xxx://, it is parsed by + # URI.parse. If the parsed object responds to the 'open' method, + # 'open' is called on it with the rest of the arguments. + # + # Otherwise, the original Kernel#open is called. + # + # OpenURI::OpenRead#open provides URI::HTTP#open, URI::HTTPS#open and + # URI::FTP#open, Kernel#open. + # + # We can accept URIs and strings that begin with http://, https:// and + # ftp://. In these cases, the opened file object is extended by OpenURI::Meta. + def open(name, *rest, &block) # :doc: + if name.respond_to?(:open) + name.open(*rest, &block) + elsif name.respond_to?(:to_str) && + %r{\A[A-Za-z][A-Za-z0-9+\-\.]*://} =~ name && + (uri = URI.parse(name)).respond_to?(:open) + uri.open(*rest, &block) + else + open_uri_original_open(name, *rest, &block) + end + end + module_function :open +end + +# OpenURI is an easy-to-use wrapper for Net::HTTP, Net::HTTPS and Net::FTP. +# +# == Example +# +# It is possible to open an http, https or ftp URL as though it were a file: +# +# open("http://www.ruby-lang.org/") {|f| +# f.each_line {|line| p line} +# } +# +# The opened file has several getter methods for its meta-information, as +# follows, since it is extended by OpenURI::Meta. +# +# open("http://www.ruby-lang.org/en") {|f| +# f.each_line {|line| p line} +# p f.base_uri # +# p f.content_type # "text/html" +# p f.charset # "iso-8859-1" +# p f.content_encoding # [] +# p f.last_modified # Thu Dec 05 02:45:02 UTC 2002 +# } +# +# Additional header fields can be specified by an optional hash argument. +# +# open("http://www.ruby-lang.org/en/", +# "User-Agent" => "Ruby/#{RUBY_VERSION}", +# "From" => "foo@bar.invalid", +# "Referer" => "http://www.ruby-lang.org/") {|f| +# # ... +# } +# +# The environment variables such as http_proxy, https_proxy and ftp_proxy +# are in effect by default. Here we disable proxy: +# +# open("http://www.ruby-lang.org/en/", :proxy => nil) {|f| +# # ... +# } +# +# See OpenURI::OpenRead.open and Kernel#open for more on available options. +# +# URI objects can be opened in a similar way. +# +# uri = URI.parse("http://www.ruby-lang.org/en/") +# uri.open {|f| +# # ... +# } +# +# URI objects can be read directly. The returned string is also extended by +# OpenURI::Meta. +# +# str = uri.read +# p str.base_uri +# +# Author:: Tanaka Akira + +module OpenURI + Options = { + :proxy => true, + :proxy_http_basic_authentication => true, + :progress_proc => true, + :content_length_proc => true, + :http_basic_authentication => true, + :read_timeout => true, + :ssl_ca_cert => nil, + :ssl_verify_mode => nil, + :ftp_active_mode => false, + :redirect => true, + } + + def OpenURI.check_options(options) # :nodoc: + options.each {|k, v| + next unless Symbol === k + unless Options.include? k + raise ArgumentError, "unrecognized option: #{k}" + end + } + end + + def OpenURI.scan_open_optional_arguments(*rest) # :nodoc: + if !rest.empty? && (String === rest.first || Integer === rest.first) + mode = rest.shift + if !rest.empty? && Integer === rest.first + perm = rest.shift + end + end + return mode, perm, rest + end + + def OpenURI.open_uri(name, *rest) # :nodoc: + uri = URI::Generic === name ? name : URI.parse(name) + mode, _, rest = OpenURI.scan_open_optional_arguments(*rest) + options = rest.shift if !rest.empty? && Hash === rest.first + raise ArgumentError.new("extra arguments") if !rest.empty? + options ||= {} + OpenURI.check_options(options) + + if /\Arb?(?:\Z|:([^:]+))/ =~ mode + encoding, = $1,Encoding.find($1) if $1 + mode = nil + end + + unless mode == nil || + mode == 'r' || mode == 'rb' || + mode == File::RDONLY + raise ArgumentError.new("invalid access mode #{mode} (#{uri.class} resource is read only.)") + end + + io = open_loop(uri, options) + io.set_encoding(encoding) if encoding + if block_given? + begin + yield io + ensure + if io.respond_to? :close! + io.close! # Tempfile + else + io.close + end + end + else + io + end + end + + def OpenURI.open_loop(uri, options) # :nodoc: + proxy_opts = [] + proxy_opts << :proxy_http_basic_authentication if options.include? :proxy_http_basic_authentication + proxy_opts << :proxy if options.include? :proxy + proxy_opts.compact! + if 1 < proxy_opts.length + raise ArgumentError, "multiple proxy options specified" + end + case proxy_opts.first + when :proxy_http_basic_authentication + opt_proxy, proxy_user, proxy_pass = options.fetch(:proxy_http_basic_authentication) + proxy_user = proxy_user.to_str + proxy_pass = proxy_pass.to_str + if opt_proxy == true + raise ArgumentError.new("Invalid authenticated proxy option: #{options[:proxy_http_basic_authentication].inspect}") + end + when :proxy + opt_proxy = options.fetch(:proxy) + proxy_user = nil + proxy_pass = nil + when nil + opt_proxy = true + proxy_user = nil + proxy_pass = nil + end + case opt_proxy + when true + find_proxy = lambda {|u| pxy = u.find_proxy; pxy ? [pxy, nil, nil] : nil} + when nil, false + find_proxy = lambda {|u| nil} + when String + opt_proxy = URI.parse(opt_proxy) + find_proxy = lambda {|u| [opt_proxy, proxy_user, proxy_pass]} + when URI::Generic + find_proxy = lambda {|u| [opt_proxy, proxy_user, proxy_pass]} + else + raise ArgumentError.new("Invalid proxy option: #{opt_proxy}") + end + + uri_set = {} + buf = nil + while true + redirect = catch(:open_uri_redirect) { + buf = Buffer.new + uri.buffer_open(buf, find_proxy.call(uri), options) + nil + } + if redirect + if redirect.relative? + # Although it violates RFC2616, Location: field may have relative + # URI. It is converted to absolute URI using uri as a base URI. + redirect = uri + redirect + end + if !options.fetch(:redirect, true) + raise HTTPRedirect.new(buf.io.status.join(' '), buf.io, redirect) + end + unless OpenURI.redirectable?(uri, redirect) + raise "redirection forbidden: #{uri} -> #{redirect}" + end + if options.include? :http_basic_authentication + # send authentication only for the URI directly specified. + options = options.dup + options.delete :http_basic_authentication + end + uri = redirect + raise "HTTP redirection loop: #{uri}" if uri_set.include? uri.to_s + uri_set[uri.to_s] = true + else + break + end + end + io = buf.io + io.base_uri = uri + io + end + + def OpenURI.redirectable?(uri1, uri2) # :nodoc: + # This test is intended to forbid a redirection from http://... to + # file:///etc/passwd, file:///dev/zero, etc. CVE-2011-1521 + # https to http redirect is also forbidden intentionally. + # It avoids sending secure cookie or referer by non-secure HTTP protocol. + # (RFC 2109 4.3.1, RFC 2965 3.3, RFC 2616 15.1.3) + # However this is ad hoc. It should be extensible/configurable. + uri1.scheme.downcase == uri2.scheme.downcase || + (/\A(?:http|ftp)\z/i =~ uri1.scheme && /\A(?:http|ftp)\z/i =~ uri2.scheme) + end + + def OpenURI.open_http(buf, target, proxy, options) # :nodoc: + if proxy + proxy_uri, proxy_user, proxy_pass = proxy + raise "Non-HTTP proxy URI: #{proxy_uri}" if proxy_uri.class != URI::HTTP + end + + if target.userinfo && "1.9.0" <= RUBY_VERSION + # don't raise for 1.8 because compatibility. + raise ArgumentError, "userinfo not supported. [RFC3986]" + end + + header = {} + options.each {|k, v| header[k] = v if String === k } + + require 'net/http' + klass = Net::HTTP + if URI::HTTP === target + # HTTP or HTTPS + if proxy + if proxy_user && proxy_pass + klass = Net::HTTP::Proxy(proxy_uri.hostname, proxy_uri.port, proxy_user, proxy_pass) + else + klass = Net::HTTP::Proxy(proxy_uri.hostname, proxy_uri.port) + end + end + target_host = target.hostname + target_port = target.port + request_uri = target.request_uri + else + # FTP over HTTP proxy + target_host = proxy_uri.hostname + target_port = proxy_uri.port + request_uri = target.to_s + if proxy_user && proxy_pass + header["Proxy-Authorization"] = 'Basic ' + ["#{proxy_user}:#{proxy_pass}"].pack('m').delete("\r\n") + end + end + + http = proxy ? klass.new(target_host, target_port) : klass.new(target_host, target_port, nil) + if target.class == URI::HTTPS + require 'net/https' + http.use_ssl = true + http.verify_mode = options[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_PEER + store = OpenSSL::X509::Store.new + if options[:ssl_ca_cert] + if File.directory? options[:ssl_ca_cert] + store.add_path options[:ssl_ca_cert] + else + store.add_file options[:ssl_ca_cert] + end + else + store.set_default_paths + end + http.cert_store = store + end + if options.include? :read_timeout + http.read_timeout = options[:read_timeout] + end + + resp = nil + http.start { + req = Net::HTTP::Get.new(request_uri, header) + if options.include? :http_basic_authentication + user, pass = options[:http_basic_authentication] + req.basic_auth user, pass + end + http.request(req) {|response| + resp = response + if options[:content_length_proc] && Net::HTTPSuccess === resp + if resp.key?('Content-Length') + options[:content_length_proc].call(resp['Content-Length'].to_i) + else + options[:content_length_proc].call(nil) + end + end + resp.read_body {|str| + buf << str + if options[:progress_proc] && Net::HTTPSuccess === resp + options[:progress_proc].call(buf.size) + end + } + } + } + io = buf.io + io.rewind + io.status = [resp.code, resp.message] + resp.each_name {|name| buf.io.meta_add_field2 name, resp.get_fields(name) } + case resp + when Net::HTTPSuccess + when Net::HTTPMovedPermanently, # 301 + Net::HTTPFound, # 302 + Net::HTTPSeeOther, # 303 + Net::HTTPTemporaryRedirect # 307 + begin + loc_uri = URI.parse(resp['location']) + rescue URI::InvalidURIError + raise OpenURI::HTTPError.new(io.status.join(' ') + ' (Invalid Location URI)', io) + end + throw :open_uri_redirect, loc_uri + else + raise OpenURI::HTTPError.new(io.status.join(' '), io) + end + end + + class HTTPError < StandardError + def initialize(message, io) + super(message) + @io = io + end + attr_reader :io + end + + # Raised on redirection, + # only occurs when +redirect+ option for HTTP is +false+. + class HTTPRedirect < HTTPError + def initialize(message, io, uri) + super(message, io) + @uri = uri + end + attr_reader :uri + end + + class Buffer # :nodoc: all + def initialize + @io = StringIO.new + @size = 0 + end + attr_reader :size + + StringMax = 10240 + def <<(str) + @io << str + @size += str.length + if StringIO === @io && StringMax < @size + require 'tempfile' + io = Tempfile.new('open-uri') + io.binmode + Meta.init io, @io if Meta === @io + io << @io.string + @io = io + end + end + + def io + Meta.init @io unless Meta === @io + @io + end + end + + # Mixin for holding meta-information. + module Meta + def Meta.init(obj, src=nil) # :nodoc: + obj.extend Meta + obj.instance_eval { + @base_uri = nil + @meta = {} # name to string. legacy. + @metas = {} # name to array of strings. + } + if src + obj.status = src.status + obj.base_uri = src.base_uri + src.metas.each {|name, values| + obj.meta_add_field2(name, values) + } + end + end + + # returns an Array that consists of status code and message. + attr_accessor :status + + # returns a URI that is the base of relative URIs in the data. + # It may differ from the URI supplied by a user due to redirection. + attr_accessor :base_uri + + # returns a Hash that represents header fields. + # The Hash keys are downcased for canonicalization. + # The Hash values are a field body. + # If there are multiple field with same field name, + # the field values are concatenated with a comma. + attr_reader :meta + + # returns a Hash that represents header fields. + # The Hash keys are downcased for canonicalization. + # The Hash value are an array of field values. + attr_reader :metas + + def meta_setup_encoding # :nodoc: + charset = self.charset + enc = nil + if charset + begin + enc = Encoding.find(charset) + rescue ArgumentError + end + end + enc = Encoding::ASCII_8BIT unless enc + if self.respond_to? :force_encoding + self.force_encoding(enc) + elsif self.respond_to? :string + self.string.force_encoding(enc) + else # Tempfile + self.set_encoding enc + end + end + + def meta_add_field2(name, values) # :nodoc: + name = name.downcase + @metas[name] = values + @meta[name] = values.join(', ') + meta_setup_encoding if name == 'content-type' + end + + def meta_add_field(name, value) # :nodoc: + meta_add_field2(name, [value]) + end + + # returns a Time that represents the Last-Modified field. + def last_modified + if vs = @metas['last-modified'] + v = vs.join(', ') + Time.httpdate(v) + else + nil + end + end + + # :stopdoc: + RE_LWS = /[\r\n\t ]+/n + RE_TOKEN = %r{[^\x00- ()<>@,;:\\"/\[\]?={}\x7f]+}n + RE_QUOTED_STRING = %r{"(?:[\r\n\t !#-\[\]-~\x80-\xff]|\\[\x00-\x7f])*"}n + RE_PARAMETERS = %r{(?:;#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?=#{RE_LWS}?(?:#{RE_TOKEN}|#{RE_QUOTED_STRING})#{RE_LWS}?)*}n + # :startdoc: + + def content_type_parse # :nodoc: + vs = @metas['content-type'] + # The last (?:;#{RE_LWS}?)? matches extra ";" which violates RFC2045. + if vs && %r{\A#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?/(#{RE_TOKEN})#{RE_LWS}?(#{RE_PARAMETERS})(?:;#{RE_LWS}?)?\z}no =~ vs.join(', ') + type = $1.downcase + subtype = $2.downcase + parameters = [] + $3.scan(/;#{RE_LWS}?(#{RE_TOKEN})#{RE_LWS}?=#{RE_LWS}?(?:(#{RE_TOKEN})|(#{RE_QUOTED_STRING}))/no) {|att, val, qval| + if qval + val = qval[1...-1].gsub(/[\r\n\t !#-\[\]-~\x80-\xff]+|(\\[\x00-\x7f])/n) { $1 ? $1[1,1] : $& } + end + parameters << [att.downcase, val] + } + ["#{type}/#{subtype}", *parameters] + else + nil + end + end + + # returns "type/subtype" which is MIME Content-Type. + # It is downcased for canonicalization. + # Content-Type parameters are stripped. + def content_type + type, *_ = content_type_parse + type || 'application/octet-stream' + end + + # returns a charset parameter in Content-Type field. + # It is downcased for canonicalization. + # + # If charset parameter is not given but a block is given, + # the block is called and its result is returned. + # It can be used to guess charset. + # + # If charset parameter and block is not given, + # nil is returned except text type in HTTP. + # In that case, "iso-8859-1" is returned as defined by RFC2616 3.7.1. + def charset + type, *parameters = content_type_parse + if pair = parameters.assoc('charset') + pair.last.downcase + elsif block_given? + yield + elsif type && %r{\Atext/} =~ type && + @base_uri && /\Ahttp\z/i =~ @base_uri.scheme + "iso-8859-1" # RFC2616 3.7.1 + else + nil + end + end + + # Returns a list of encodings in Content-Encoding field as an array of + # strings. + # + # The encodings are downcased for canonicalization. + def content_encoding + vs = @metas['content-encoding'] + if vs && %r{\A#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?(?:,#{RE_LWS}?#{RE_TOKEN}#{RE_LWS}?)*}o =~ (v = vs.join(', ')) + v.scan(RE_TOKEN).map {|content_coding| content_coding.downcase} + else + [] + end + end + end + + # Mixin for HTTP and FTP URIs. + module OpenRead + # OpenURI::OpenRead#open provides `open' for URI::HTTP and URI::FTP. + # + # OpenURI::OpenRead#open takes optional 3 arguments as: + # + # OpenURI::OpenRead#open([mode [, perm]] [, options]) [{|io| ... }] + # + # OpenURI::OpenRead#open returns an IO-like object if block is not given. + # Otherwise it yields the IO object and return the value of the block. + # The IO object is extended with OpenURI::Meta. + # + # +mode+ and +perm+ are the same as Kernel#open. + # + # However, +mode+ must be read mode because OpenURI::OpenRead#open doesn't + # support write mode (yet). + # Also +perm+ is ignored because it is meaningful only for file creation. + # + # +options+ must be a hash. + # + # Each option with a string key specifies an extra header field for HTTP. + # I.e., it is ignored for FTP without HTTP proxy. + # + # The hash may include other options, where keys are symbols: + # + # [:proxy] + # Synopsis: + # :proxy => "http://proxy.foo.com:8000/" + # :proxy => URI.parse("http://proxy.foo.com:8000/") + # :proxy => true + # :proxy => false + # :proxy => nil + # + # If :proxy option is specified, the value should be String, URI, + # boolean or nil. + # + # When String or URI is given, it is treated as proxy URI. + # + # When true is given or the option itself is not specified, + # environment variable `scheme_proxy' is examined. + # `scheme' is replaced by `http', `https' or `ftp'. + # + # When false or nil is given, the environment variables are ignored and + # connection will be made to a server directly. + # + # [:proxy_http_basic_authentication] + # Synopsis: + # :proxy_http_basic_authentication => + # ["http://proxy.foo.com:8000/", "proxy-user", "proxy-password"] + # :proxy_http_basic_authentication => + # [URI.parse("http://proxy.foo.com:8000/"), + # "proxy-user", "proxy-password"] + # + # If :proxy option is specified, the value should be an Array with 3 + # elements. It should contain a proxy URI, a proxy user name and a proxy + # password. The proxy URI should be a String, an URI or nil. The proxy + # user name and password should be a String. + # + # If nil is given for the proxy URI, this option is just ignored. + # + # If :proxy and :proxy_http_basic_authentication is specified, + # ArgumentError is raised. + # + # [:http_basic_authentication] + # Synopsis: + # :http_basic_authentication=>[user, password] + # + # If :http_basic_authentication is specified, + # the value should be an array which contains 2 strings: + # username and password. + # It is used for HTTP Basic authentication defined by RFC 2617. + # + # [:content_length_proc] + # Synopsis: + # :content_length_proc => lambda {|content_length| ... } + # + # If :content_length_proc option is specified, the option value procedure + # is called before actual transfer is started. + # It takes one argument, which is expected content length in bytes. + # + # If two or more transfer is done by HTTP redirection, the procedure + # is called only one for a last transfer. + # + # When expected content length is unknown, the procedure is called with + # nil. This happens when the HTTP response has no Content-Length header. + # + # [:progress_proc] + # Synopsis: + # :progress_proc => lambda {|size| ...} + # + # If :progress_proc option is specified, the proc is called with one + # argument each time when `open' gets content fragment from network. + # The argument +size+ is the accumulated transferred size in bytes. + # + # If two or more transfer is done by HTTP redirection, the procedure + # is called only one for a last transfer. + # + # :progress_proc and :content_length_proc are intended to be used for + # progress bar. + # For example, it can be implemented as follows using Ruby/ProgressBar. + # + # pbar = nil + # open("http://...", + # :content_length_proc => lambda {|t| + # if t && 0 < t + # pbar = ProgressBar.new("...", t) + # pbar.file_transfer_mode + # end + # }, + # :progress_proc => lambda {|s| + # pbar.set s if pbar + # }) {|f| ... } + # + # [:read_timeout] + # Synopsis: + # :read_timeout=>nil (no timeout) + # :read_timeout=>10 (10 second) + # + # :read_timeout option specifies a timeout of read for http connections. + # + # [:ssl_ca_cert] + # Synopsis: + # :ssl_ca_cert=>filename + # + # :ssl_ca_cert is used to specify CA certificate for SSL. + # If it is given, default certificates are not used. + # + # [:ssl_verify_mode] + # Synopsis: + # :ssl_verify_mode=>mode + # + # :ssl_verify_mode is used to specify openssl verify mode. + # + # [:ftp_active_mode] + # Synopsis: + # :ftp_active_mode=>bool + # + # :ftp_active_mode => true is used to make ftp active mode. + # Ruby 1.9 uses passive mode by default. + # Note that the active mode is default in Ruby 1.8 or prior. + # + # [:redirect] + # Synopsis: + # :redirect=>bool + # + # +:redirect+ is true by default. :redirect => false is used to + # disable all HTTP redirects. + # + # OpenURI::HTTPRedirect exception raised on redirection. + # Using +true+ also means that redirections between http and ftp are + # permitted. + # + def open(*rest, &block) + OpenURI.open_uri(self, *rest, &block) + end + + # OpenURI::OpenRead#read([options]) reads a content referenced by self and + # returns the content as string. + # The string is extended with OpenURI::Meta. + # The argument +options+ is same as OpenURI::OpenRead#open. + def read(options={}) + self.open(options) {|f| + str = f.read + Meta.init str, f + str + } + end + end +end + +module URI + class HTTP + def buffer_open(buf, proxy, options) # :nodoc: + OpenURI.open_http(buf, self, proxy, options) + end + + include OpenURI::OpenRead + end + + class FTP + def buffer_open(buf, proxy, options) # :nodoc: + if proxy + OpenURI.open_http(buf, self, proxy, options) + return + end + require 'net/ftp' + + path = self.path + path = path.sub(%r{\A/}, '%2F') # re-encode the beginning slash because uri library decodes it. + directories = path.split(%r{/}, -1) + directories.each {|d| + d.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/) { [$1].pack("H2") } + } + unless filename = directories.pop + raise ArgumentError, "no filename: #{self.inspect}" + end + directories.each {|d| + if /[\r\n]/ =~ d + raise ArgumentError, "invalid directory: #{d.inspect}" + end + } + if /[\r\n]/ =~ filename + raise ArgumentError, "invalid filename: #{filename.inspect}" + end + typecode = self.typecode + if typecode && /\A[aid]\z/ !~ typecode + raise ArgumentError, "invalid typecode: #{typecode.inspect}" + end + + # The access sequence is defined by RFC 1738 + ftp = Net::FTP.new + ftp.connect(self.hostname, self.port) + ftp.passive = true if !options[:ftp_active_mode] + # todo: extract user/passwd from .netrc. + user = 'anonymous' + passwd = nil + user, passwd = self.userinfo.split(/:/) if self.userinfo + ftp.login(user, passwd) + directories.each {|cwd| + ftp.voidcmd("CWD #{cwd}") + } + if typecode + # xxx: typecode D is not handled. + ftp.voidcmd("TYPE #{typecode.upcase}") + end + if options[:content_length_proc] + options[:content_length_proc].call(ftp.size(filename)) + end + ftp.retrbinary("RETR #{filename}", 4096) { |str| + buf << str + options[:progress_proc].call(buf.size) if options[:progress_proc] + } + ftp.close + buf.io.rewind + end + + include OpenURI::OpenRead + end +end diff --git a/ruby/lib/ruby/2.1.0/open3.rb b/ruby/lib/ruby/2.1.0/open3.rb new file mode 100644 index 0000000..a54e1b3 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/open3.rb @@ -0,0 +1,663 @@ +# +# = open3.rb: Popen, but with stderr, too +# +# Author:: Yukihiro Matsumoto +# Documentation:: Konrad Meyer +# +# Open3 gives you access to stdin, stdout, and stderr when running other +# programs. +# + +# +# Open3 grants you access to stdin, stdout, stderr and a thread to wait for the +# child process when running another program. +# You can specify various attributes, redirections, current directory, etc., of +# the program in the same way as for Process.spawn. +# +# - Open3.popen3 : pipes for stdin, stdout, stderr +# - Open3.popen2 : pipes for stdin, stdout +# - Open3.popen2e : pipes for stdin, merged stdout and stderr +# - Open3.capture3 : give a string for stdin; get strings for stdout, stderr +# - Open3.capture2 : give a string for stdin; get a string for stdout +# - Open3.capture2e : give a string for stdin; get a string for merged stdout and stderr +# - Open3.pipeline_rw : pipes for first stdin and last stdout of a pipeline +# - Open3.pipeline_r : pipe for last stdout of a pipeline +# - Open3.pipeline_w : pipe for first stdin of a pipeline +# - Open3.pipeline_start : run a pipeline without waiting +# - Open3.pipeline : run a pipeline and wait for its completion +# + +module Open3 + + # Open stdin, stdout, and stderr streams and start external executable. + # In addition, a thread to wait for the started process is created. + # The thread has a pid method and a thread variable :pid which is the pid of + # the started process. + # + # Block form: + # + # Open3.popen3([env,] cmd... [, opts]) {|stdin, stdout, stderr, wait_thr| + # pid = wait_thr.pid # pid of the started process. + # ... + # exit_status = wait_thr.value # Process::Status object returned. + # } + # + # Non-block form: + # + # stdin, stdout, stderr, wait_thr = Open3.popen3([env,] cmd... [, opts]) + # pid = wait_thr[:pid] # pid of the started process + # ... + # stdin.close # stdin, stdout and stderr should be closed explicitly in this form. + # stdout.close + # stderr.close + # exit_status = wait_thr.value # Process::Status object returned. + # + # The parameters env, cmd, and opts are passed to Process.spawn. + # A commandline string and a list of argument strings can be accepted as follows: + # + # Open3.popen3("echo abc") {|i, o, e, t| ... } + # Open3.popen3("echo", "abc") {|i, o, e, t| ... } + # Open3.popen3(["echo", "argv0"], "abc") {|i, o, e, t| ... } + # + # If the last parameter, opts, is a Hash, it is recognized as an option for Process.spawn. + # + # Open3.popen3("pwd", :chdir=>"/") {|i,o,e,t| + # p o.read.chomp #=> "/" + # } + # + # wait_thr.value waits for the termination of the process. + # The block form also waits for the process when it returns. + # + # Closing stdin, stdout and stderr does not wait for the process to complete. + # + # You should be careful to avoid deadlocks. + # Since pipes are fixed length buffers, + # Open3.popen3("prog") {|i, o, e, t| o.read } deadlocks if + # the program generates too much output on stderr. + # You should read stdout and stderr simultaneously (using threads or IO.select). + # However, if you don't need stderr output, you can use Open3.popen2. + # If merged stdout and stderr output is not a problem, you can use Open3.popen2e. + # If you really need stdout and stderr output as separate strings, you can consider Open3.capture3. + # + def popen3(*cmd, **opts, &block) + in_r, in_w = IO.pipe + opts[:in] = in_r + in_w.sync = true + + out_r, out_w = IO.pipe + opts[:out] = out_w + + err_r, err_w = IO.pipe + opts[:err] = err_w + + popen_run(cmd, opts, [in_r, out_w, err_w], [in_w, out_r, err_r], &block) + end + module_function :popen3 + + # Open3.popen2 is similar to Open3.popen3 except that it doesn't create a pipe for + # the standard error stream. + # + # Block form: + # + # Open3.popen2([env,] cmd... [, opts]) {|stdin, stdout, wait_thr| + # pid = wait_thr.pid # pid of the started process. + # ... + # exit_status = wait_thr.value # Process::Status object returned. + # } + # + # Non-block form: + # + # stdin, stdout, wait_thr = Open3.popen2([env,] cmd... [, opts]) + # ... + # stdin.close # stdin and stdout should be closed explicitly in this form. + # stdout.close + # + # See Process.spawn for the optional hash arguments _env_ and _opts_. + # + # Example: + # + # Open3.popen2("wc -c") {|i,o,t| + # i.print "answer to life the universe and everything" + # i.close + # p o.gets #=> "42\n" + # } + # + # Open3.popen2("bc -q") {|i,o,t| + # i.puts "obase=13" + # i.puts "6 * 9" + # p o.gets #=> "42\n" + # } + # + # Open3.popen2("dc") {|i,o,t| + # i.print "42P" + # i.close + # p o.read #=> "*" + # } + # + def popen2(*cmd, **opts, &block) + in_r, in_w = IO.pipe + opts[:in] = in_r + in_w.sync = true + + out_r, out_w = IO.pipe + opts[:out] = out_w + + popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block) + end + module_function :popen2 + + # Open3.popen2e is similar to Open3.popen3 except that it merges + # the standard output stream and the standard error stream. + # + # Block form: + # + # Open3.popen2e([env,] cmd... [, opts]) {|stdin, stdout_and_stderr, wait_thr| + # pid = wait_thr.pid # pid of the started process. + # ... + # exit_status = wait_thr.value # Process::Status object returned. + # } + # + # Non-block form: + # + # stdin, stdout_and_stderr, wait_thr = Open3.popen2e([env,] cmd... [, opts]) + # ... + # stdin.close # stdin and stdout_and_stderr should be closed explicitly in this form. + # stdout_and_stderr.close + # + # See Process.spawn for the optional hash arguments _env_ and _opts_. + # + # Example: + # # check gcc warnings + # source = "foo.c" + # Open3.popen2e("gcc", "-Wall", source) {|i,oe,t| + # oe.each {|line| + # if /warning/ =~ line + # ... + # end + # } + # } + # + def popen2e(*cmd, **opts, &block) + in_r, in_w = IO.pipe + opts[:in] = in_r + in_w.sync = true + + out_r, out_w = IO.pipe + opts[[:out, :err]] = out_w + + popen_run(cmd, opts, [in_r, out_w], [in_w, out_r], &block) + end + module_function :popen2e + + def popen_run(cmd, opts, child_io, parent_io) # :nodoc: + pid = spawn(*cmd, opts) + wait_thr = Process.detach(pid) + child_io.each {|io| io.close } + result = [*parent_io, wait_thr] + if defined? yield + begin + return yield(*result) + ensure + parent_io.each{|io| io.close unless io.closed?} + wait_thr.join + end + end + result + end + module_function :popen_run + class << self + private :popen_run + end + + # Open3.capture3 captures the standard output and the standard error of a command. + # + # stdout_str, stderr_str, status = Open3.capture3([env,] cmd... [, opts]) + # + # The arguments env, cmd and opts are passed to Open3.popen3 except + # opts[:stdin_data] and opts[:binmode]. See Process.spawn. + # + # If opts[:stdin_data] is specified, it is sent to the command's standard input. + # + # If opts[:binmode] is true, internal pipes are set to binary mode. + # + # Examples: + # + # # dot is a command of graphviz. + # graph = <<'End' + # digraph g { + # a -> b + # } + # End + # drawn_graph, dot_log = Open3.capture3("dot -v", :stdin_data=>graph) + # + # o, e, s = Open3.capture3("echo abc; sort >&2", :stdin_data=>"foo\nbar\nbaz\n") + # p o #=> "abc\n" + # p e #=> "bar\nbaz\nfoo\n" + # p s #=> # + # + # # generate a thumbnail image using the convert command of ImageMagick. + # # However, if the image is really stored in a file, + # # system("convert", "-thumbnail", "80", "png:#{filename}", "png:-") is better + # # because of reduced memory consumption. + # # But if the image is stored in a DB or generated by the gnuplot Open3.capture2 example, + # # Open3.capture3 should be considered. + # # + # image = File.read("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true) + # thumbnail, err, s = Open3.capture3("convert -thumbnail 80 png:- png:-", :stdin_data=>image, :binmode=>true) + # if s.success? + # STDOUT.binmode; print thumbnail + # end + # + def capture3(*cmd, stdin_data: '', binmode: false, **opts) + popen3(*cmd, opts) {|i, o, e, t| + if binmode + i.binmode + o.binmode + e.binmode + end + out_reader = Thread.new { o.read } + err_reader = Thread.new { e.read } + i.write stdin_data + i.close + [out_reader.value, err_reader.value, t.value] + } + end + module_function :capture3 + + # Open3.capture2 captures the standard output of a command. + # + # stdout_str, status = Open3.capture2([env,] cmd... [, opts]) + # + # The arguments env, cmd and opts are passed to Open3.popen3 except + # opts[:stdin_data] and opts[:binmode]. See Process.spawn. + # + # If opts[:stdin_data] is specified, it is sent to the command's standard input. + # + # If opts[:binmode] is true, internal pipes are set to binary mode. + # + # Example: + # + # # factor is a command for integer factorization. + # o, s = Open3.capture2("factor", :stdin_data=>"42") + # p o #=> "42: 2 3 7\n" + # + # # generate x**2 graph in png using gnuplot. + # gnuplot_commands = <<"End" + # set terminal png + # plot x**2, "-" with lines + # 1 14 + # 2 1 + # 3 8 + # 4 5 + # e + # End + # image, s = Open3.capture2("gnuplot", :stdin_data=>gnuplot_commands, :binmode=>true) + # + def capture2(*cmd, stdin_data: '', binmode: false, **opts) + popen2(*cmd, opts) {|i, o, t| + if binmode + i.binmode + o.binmode + end + out_reader = Thread.new { o.read } + i.write stdin_data + i.close + [out_reader.value, t.value] + } + end + module_function :capture2 + + # Open3.capture2e captures the standard output and the standard error of a command. + # + # stdout_and_stderr_str, status = Open3.capture2e([env,] cmd... [, opts]) + # + # The arguments env, cmd and opts are passed to Open3.popen3 except + # opts[:stdin_data] and opts[:binmode]. See Process.spawn. + # + # If opts[:stdin_data] is specified, it is sent to the command's standard input. + # + # If opts[:binmode] is true, internal pipes are set to binary mode. + # + # Example: + # + # # capture make log + # make_log, s = Open3.capture2e("make") + # + def capture2e(*cmd, stdin_data: '', binmode: false, **opts) + popen2e(*cmd, opts) {|i, oe, t| + if binmode + i.binmode + oe.binmode + end + outerr_reader = Thread.new { oe.read } + i.write stdin_data + i.close + [outerr_reader.value, t.value] + } + end + module_function :capture2e + + # Open3.pipeline_rw starts a list of commands as a pipeline with pipes + # which connect to stdin of the first command and stdout of the last command. + # + # Open3.pipeline_rw(cmd1, cmd2, ... [, opts]) {|first_stdin, last_stdout, wait_threads| + # ... + # } + # + # first_stdin, last_stdout, wait_threads = Open3.pipeline_rw(cmd1, cmd2, ... [, opts]) + # ... + # first_stdin.close + # last_stdout.close + # + # Each cmd is a string or an array. + # If it is an array, the elements are passed to Process.spawn. + # + # cmd: + # commandline command line string which is passed to a shell + # [env, commandline, opts] command line string which is passed to a shell + # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell) + # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell) + # + # Note that env and opts are optional, as for Process.spawn. + # + # The options to pass to Process.spawn are constructed by merging + # +opts+, the last hash element of the array, and + # specifications for the pipes between each of the commands. + # + # Example: + # + # Open3.pipeline_rw("tr -dc A-Za-z", "wc -c") {|i, o, ts| + # i.puts "All persons more than a mile high to leave the court." + # i.close + # p o.gets #=> "42\n" + # } + # + # Open3.pipeline_rw("sort", "cat -n") {|stdin, stdout, wait_thrs| + # stdin.puts "foo" + # stdin.puts "bar" + # stdin.puts "baz" + # stdin.close # send EOF to sort. + # p stdout.read #=> " 1\tbar\n 2\tbaz\n 3\tfoo\n" + # } + def pipeline_rw(*cmds, **opts, &block) + in_r, in_w = IO.pipe + opts[:in] = in_r + in_w.sync = true + + out_r, out_w = IO.pipe + opts[:out] = out_w + + pipeline_run(cmds, opts, [in_r, out_w], [in_w, out_r], &block) + end + module_function :pipeline_rw + + # Open3.pipeline_r starts a list of commands as a pipeline with a pipe + # which connects to stdout of the last command. + # + # Open3.pipeline_r(cmd1, cmd2, ... [, opts]) {|last_stdout, wait_threads| + # ... + # } + # + # last_stdout, wait_threads = Open3.pipeline_r(cmd1, cmd2, ... [, opts]) + # ... + # last_stdout.close + # + # Each cmd is a string or an array. + # If it is an array, the elements are passed to Process.spawn. + # + # cmd: + # commandline command line string which is passed to a shell + # [env, commandline, opts] command line string which is passed to a shell + # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell) + # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell) + # + # Note that env and opts are optional, as for Process.spawn. + # + # Example: + # + # Open3.pipeline_r("zcat /var/log/apache2/access.log.*.gz", + # [{"LANG"=>"C"}, "grep", "GET /favicon.ico"], + # "logresolve") {|o, ts| + # o.each_line {|line| + # ... + # } + # } + # + # Open3.pipeline_r("yes", "head -10") {|o, ts| + # p o.read #=> "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n" + # p ts[0].value #=> # + # p ts[1].value #=> # + # } + # + def pipeline_r(*cmds, **opts, &block) + out_r, out_w = IO.pipe + opts[:out] = out_w + + pipeline_run(cmds, opts, [out_w], [out_r], &block) + end + module_function :pipeline_r + + # Open3.pipeline_w starts a list of commands as a pipeline with a pipe + # which connects to stdin of the first command. + # + # Open3.pipeline_w(cmd1, cmd2, ... [, opts]) {|first_stdin, wait_threads| + # ... + # } + # + # first_stdin, wait_threads = Open3.pipeline_w(cmd1, cmd2, ... [, opts]) + # ... + # first_stdin.close + # + # Each cmd is a string or an array. + # If it is an array, the elements are passed to Process.spawn. + # + # cmd: + # commandline command line string which is passed to a shell + # [env, commandline, opts] command line string which is passed to a shell + # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell) + # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell) + # + # Note that env and opts are optional, as for Process.spawn. + # + # Example: + # + # Open3.pipeline_w("bzip2 -c", :out=>"/tmp/hello.bz2") {|i, ts| + # i.puts "hello" + # } + # + def pipeline_w(*cmds, **opts, &block) + in_r, in_w = IO.pipe + opts[:in] = in_r + in_w.sync = true + + pipeline_run(cmds, opts, [in_r], [in_w], &block) + end + module_function :pipeline_w + + # Open3.pipeline_start starts a list of commands as a pipeline. + # No pipes are created for stdin of the first command and + # stdout of the last command. + # + # Open3.pipeline_start(cmd1, cmd2, ... [, opts]) {|wait_threads| + # ... + # } + # + # wait_threads = Open3.pipeline_start(cmd1, cmd2, ... [, opts]) + # ... + # + # Each cmd is a string or an array. + # If it is an array, the elements are passed to Process.spawn. + # + # cmd: + # commandline command line string which is passed to a shell + # [env, commandline, opts] command line string which is passed to a shell + # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell) + # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell) + # + # Note that env and opts are optional, as for Process.spawn. + # + # Example: + # + # # Run xeyes in 10 seconds. + # Open3.pipeline_start("xeyes") {|ts| + # sleep 10 + # t = ts[0] + # Process.kill("TERM", t.pid) + # p t.value #=> # + # } + # + # # Convert pdf to ps and send it to a printer. + # # Collect error message of pdftops and lpr. + # pdf_file = "paper.pdf" + # printer = "printer-name" + # err_r, err_w = IO.pipe + # Open3.pipeline_start(["pdftops", pdf_file, "-"], + # ["lpr", "-P#{printer}"], + # :err=>err_w) {|ts| + # err_w.close + # p err_r.read # error messages of pdftops and lpr. + # } + # + def pipeline_start(*cmds, **opts, &block) + if block + pipeline_run(cmds, opts, [], [], &block) + else + ts, = pipeline_run(cmds, opts, [], []) + ts + end + end + module_function :pipeline_start + + # Open3.pipeline starts a list of commands as a pipeline. + # It waits for the completion of the commands. + # No pipes are created for stdin of the first command and + # stdout of the last command. + # + # status_list = Open3.pipeline(cmd1, cmd2, ... [, opts]) + # + # Each cmd is a string or an array. + # If it is an array, the elements are passed to Process.spawn. + # + # cmd: + # commandline command line string which is passed to a shell + # [env, commandline, opts] command line string which is passed to a shell + # [env, cmdname, arg1, ..., opts] command name and one or more arguments (no shell) + # [env, [cmdname, argv0], arg1, ..., opts] command name and arguments including argv[0] (no shell) + # + # Note that env and opts are optional, as Process.spawn. + # + # Example: + # + # fname = "/usr/share/man/man1/ruby.1.gz" + # p Open3.pipeline(["zcat", fname], "nroff -man", "less") + # #=> [#, + # # #, + # # #] + # + # fname = "/usr/share/man/man1/ls.1.gz" + # Open3.pipeline(["zcat", fname], "nroff -man", "colcrt") + # + # # convert PDF to PS and send to a printer by lpr + # pdf_file = "paper.pdf" + # printer = "printer-name" + # Open3.pipeline(["pdftops", pdf_file, "-"], + # ["lpr", "-P#{printer}"]) + # + # # count lines + # Open3.pipeline("sort", "uniq -c", :in=>"names.txt", :out=>"count") + # + # # cyclic pipeline + # r,w = IO.pipe + # w.print "ibase=14\n10\n" + # Open3.pipeline("bc", "tee /dev/tty", :in=>r, :out=>w) + # #=> 14 + # # 18 + # # 22 + # # 30 + # # 42 + # # 58 + # # 78 + # # 106 + # # 202 + # + def pipeline(*cmds, **opts) + pipeline_run(cmds, opts, [], []) {|ts| + ts.map {|t| t.value } + } + end + module_function :pipeline + + def pipeline_run(cmds, pipeline_opts, child_io, parent_io) # :nodoc: + if cmds.empty? + raise ArgumentError, "no commands" + end + + opts_base = pipeline_opts.dup + opts_base.delete :in + opts_base.delete :out + + wait_thrs = [] + r = nil + cmds.each_with_index {|cmd, i| + cmd_opts = opts_base.dup + if String === cmd + cmd = [cmd] + else + cmd_opts.update cmd.pop if Hash === cmd.last + end + if i == 0 + if !cmd_opts.include?(:in) + if pipeline_opts.include?(:in) + cmd_opts[:in] = pipeline_opts[:in] + end + end + else + cmd_opts[:in] = r + end + if i != cmds.length - 1 + r2, w2 = IO.pipe + cmd_opts[:out] = w2 + else + if !cmd_opts.include?(:out) + if pipeline_opts.include?(:out) + cmd_opts[:out] = pipeline_opts[:out] + end + end + end + pid = spawn(*cmd, cmd_opts) + wait_thrs << Process.detach(pid) + r.close if r + w2.close if w2 + r = r2 + } + result = parent_io + [wait_thrs] + child_io.each {|io| io.close } + if defined? yield + begin + return yield(*result) + ensure + parent_io.each{|io| io.close unless io.closed?} + wait_thrs.each {|t| t.join } + end + end + result + end + module_function :pipeline_run + class << self + private :pipeline_run + end + +end + +if $0 == __FILE__ + a = Open3.popen3("nroff -man") + Thread.start do + while line = gets + a[0].print line + end + a[0].close + end + while line = a[1].gets + print ":", line + end +end diff --git a/ruby/lib/ruby/2.1.0/openssl.rb b/ruby/lib/ruby/2.1.0/openssl.rb new file mode 100644 index 0000000..adcaee7 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/openssl.rb @@ -0,0 +1,24 @@ +=begin += $RCSfile$ -- Loader for all OpenSSL C-space and Ruby-space definitions + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2002 Michal Rokos + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: openssl.rb 32664 2011-07-25 06:30:07Z nahi $ +=end + +require 'openssl.so' + +require 'openssl/bn' +require 'openssl/cipher' +require 'openssl/config' +require 'openssl/digest' +require 'openssl/x509' +require 'openssl/ssl' diff --git a/ruby/lib/ruby/2.1.0/openssl/bn.rb b/ruby/lib/ruby/2.1.0/openssl/bn.rb new file mode 100644 index 0000000..3a57ff5 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/openssl/bn.rb @@ -0,0 +1,38 @@ +#-- +# +# $RCSfile$ +# +# = Ruby-space definitions that completes C-space funcs for BN +# +# = Info +# 'OpenSSL for Ruby 2' project +# Copyright (C) 2002 Michal Rokos +# All rights reserved. +# +# = Licence +# This program is licenced under the same licence as Ruby. +# (See the file 'LICENCE'.) +# +# = Version +# $Id: bn.rb 43663 2013-11-13 08:15:48Z zzak $ +# +#++ + +module OpenSSL + class BN + include Comparable + end # BN +end # OpenSSL + +## +# Add double dispatch to Integer +# +class Integer + # Casts an Integer as an OpenSSL::BN + # + # See `man bn` for more info. + def to_bn + OpenSSL::BN::new(self) + end +end # Integer + diff --git a/ruby/lib/ruby/2.1.0/openssl/buffering.rb b/ruby/lib/ruby/2.1.0/openssl/buffering.rb new file mode 100644 index 0000000..40bbd0f --- /dev/null +++ b/ruby/lib/ruby/2.1.0/openssl/buffering.rb @@ -0,0 +1,457 @@ +# coding: binary +#-- +#= $RCSfile$ -- Buffering mix-in module. +# +#= Info +# 'OpenSSL for Ruby 2' project +# Copyright (C) 2001 GOTOU YUUZOU +# All rights reserved. +# +#= Licence +# This program is licenced under the same licence as Ruby. +# (See the file 'LICENCE'.) +# +#= Version +# $Id: buffering.rb 43964 2013-12-03 01:44:41Z drbrain $ +#++ + +## +# OpenSSL IO buffering mix-in module. +# +# This module allows an OpenSSL::SSL::SSLSocket to behave like an IO. +# +# You typically won't use this module directly, you can see it implemented in +# OpenSSL::SSL::SSLSocket. + +module OpenSSL::Buffering + include Enumerable + + ## + # The "sync mode" of the SSLSocket. + # + # See IO#sync for full details. + + attr_accessor :sync + + ## + # Default size to read from or write to the SSLSocket for buffer operations. + + BLOCK_SIZE = 1024*16 + + ## + # Creates an instance of OpenSSL's buffering IO module. + + def initialize(*) + super + @eof = false + @rbuffer = "" + @sync = @io.sync + end + + # + # for reading. + # + private + + ## + # Fills the buffer from the underlying SSLSocket + + def fill_rbuff + begin + @rbuffer << self.sysread(BLOCK_SIZE) + rescue Errno::EAGAIN + retry + rescue EOFError + @eof = true + end + end + + ## + # Consumes +size+ bytes from the buffer + + def consume_rbuff(size=nil) + if @rbuffer.empty? + nil + else + size = @rbuffer.size unless size + ret = @rbuffer[0, size] + @rbuffer[0, size] = "" + ret + end + end + + public + + ## + # Reads +size+ bytes from the stream. If +buf+ is provided it must + # reference a string which will receive the data. + # + # See IO#read for full details. + + def read(size=nil, buf=nil) + if size == 0 + if buf + buf.clear + return buf + else + return "" + end + end + until @eof + break if size && size <= @rbuffer.size + fill_rbuff + end + ret = consume_rbuff(size) || "" + if buf + buf.replace(ret) + ret = buf + end + (size && ret.empty?) ? nil : ret + end + + ## + # Reads at most +maxlen+ bytes from the stream. If +buf+ is provided it + # must reference a string which will receive the data. + # + # See IO#readpartial for full details. + + def readpartial(maxlen, buf=nil) + if maxlen == 0 + if buf + buf.clear + return buf + else + return "" + end + end + if @rbuffer.empty? + begin + return sysread(maxlen, buf) + rescue Errno::EAGAIN + retry + end + end + ret = consume_rbuff(maxlen) + if buf + buf.replace(ret) + ret = buf + end + raise EOFError if ret.empty? + ret + end + + ## + # Reads at most +maxlen+ bytes in the non-blocking manner. + # + # When no data can be read without blocking it raises + # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable. + # + # IO::WaitReadable means SSL needs to read internally so read_nonblock + # should be called again when the underlying IO is readable. + # + # IO::WaitWritable means SSL needs to write internally so read_nonblock + # should be called again after the underlying IO is writable. + # + # OpenSSL::Buffering#read_nonblock needs two rescue clause as follows: + # + # # emulates blocking read (readpartial). + # begin + # result = ssl.read_nonblock(maxlen) + # rescue IO::WaitReadable + # IO.select([io]) + # retry + # rescue IO::WaitWritable + # IO.select(nil, [io]) + # retry + # end + # + # Note that one reason that read_nonblock writes to the underlying IO is + # when the peer requests a new TLS/SSL handshake. See openssl the FAQ for + # more details. http://www.openssl.org/support/faq.html + + def read_nonblock(maxlen, buf=nil, exception: true) + if maxlen == 0 + if buf + buf.clear + return buf + else + return "" + end + end + if @rbuffer.empty? + return sysread_nonblock(maxlen, buf, exception: exception) + end + ret = consume_rbuff(maxlen) + if buf + buf.replace(ret) + ret = buf + end + raise EOFError if ret.empty? + ret + end + + ## + # Reads the next "line+ from the stream. Lines are separated by +eol+. If + # +limit+ is provided the result will not be longer than the given number of + # bytes. + # + # +eol+ may be a String or Regexp. + # + # Unlike IO#gets the line read will not be assigned to +$_+. + # + # Unlike IO#gets the separator must be provided if a limit is provided. + + def gets(eol=$/, limit=nil) + idx = @rbuffer.index(eol) + until @eof + break if idx + fill_rbuff + idx = @rbuffer.index(eol) + end + if eol.is_a?(Regexp) + size = idx ? idx+$&.size : nil + else + size = idx ? idx+eol.size : nil + end + if limit and limit >= 0 + size = [size, limit].min + end + consume_rbuff(size) + end + + ## + # Executes the block for every line in the stream where lines are separated + # by +eol+. + # + # See also #gets + + def each(eol=$/) + while line = self.gets(eol) + yield line + end + end + alias each_line each + + ## + # Reads lines from the stream which are separated by +eol+. + # + # See also #gets + + def readlines(eol=$/) + ary = [] + while line = self.gets(eol) + ary << line + end + ary + end + + ## + # Reads a line from the stream which is separated by +eol+. + # + # Raises EOFError if at end of file. + + def readline(eol=$/) + raise EOFError if eof? + gets(eol) + end + + ## + # Reads one character from the stream. Returns nil if called at end of + # file. + + def getc + read(1) + end + + ## + # Calls the given block once for each byte in the stream. + + def each_byte # :yields: byte + while c = getc + yield(c.ord) + end + end + + ## + # Reads a one-character string from the stream. Raises an EOFError at end + # of file. + + def readchar + raise EOFError if eof? + getc + end + + ## + # Pushes character +c+ back onto the stream such that a subsequent buffered + # character read will return it. + # + # Unlike IO#getc multiple bytes may be pushed back onto the stream. + # + # Has no effect on unbuffered reads (such as #sysread). + + def ungetc(c) + @rbuffer[0,0] = c.chr + end + + ## + # Returns true if the stream is at file which means there is no more data to + # be read. + + def eof? + fill_rbuff if !@eof && @rbuffer.empty? + @eof && @rbuffer.empty? + end + alias eof eof? + + # + # for writing. + # + private + + ## + # Writes +s+ to the buffer. When the buffer is full or #sync is true the + # buffer is flushed to the underlying socket. + + def do_write(s) + @wbuffer = "" unless defined? @wbuffer + @wbuffer << s + @wbuffer.force_encoding(Encoding::BINARY) + @sync ||= false + if @sync or @wbuffer.size > BLOCK_SIZE or idx = @wbuffer.rindex($/) + remain = idx ? idx + $/.size : @wbuffer.length + nwritten = 0 + while remain > 0 + str = @wbuffer[nwritten,remain] + begin + nwrote = syswrite(str) + rescue Errno::EAGAIN + retry + end + remain -= nwrote + nwritten += nwrote + end + @wbuffer[0,nwritten] = "" + end + end + + public + + ## + # Writes +s+ to the stream. If the argument is not a string it will be + # converted using String#to_s. Returns the number of bytes written. + + def write(s) + do_write(s) + s.bytesize + end + + ## + # Writes +str+ in the non-blocking manner. + # + # If there is buffered data, it is flushed first. This may block. + # + # write_nonblock returns number of bytes written to the SSL connection. + # + # When no data can be written without blocking it raises + # OpenSSL::SSL::SSLError extended by IO::WaitReadable or IO::WaitWritable. + # + # IO::WaitReadable means SSL needs to read internally so write_nonblock + # should be called again after the underlying IO is readable. + # + # IO::WaitWritable means SSL needs to write internally so write_nonblock + # should be called again after underlying IO is writable. + # + # So OpenSSL::Buffering#write_nonblock needs two rescue clause as follows. + # + # # emulates blocking write. + # begin + # result = ssl.write_nonblock(str) + # rescue IO::WaitReadable + # IO.select([io]) + # retry + # rescue IO::WaitWritable + # IO.select(nil, [io]) + # retry + # end + # + # Note that one reason that write_nonblock reads from the underlying IO + # is when the peer requests a new TLS/SSL handshake. See the openssl FAQ + # for more details. http://www.openssl.org/support/faq.html + + def write_nonblock(s, exception: true) + flush + syswrite_nonblock(s, exception: exception) + end + + ## + # Writes +s+ to the stream. +s+ will be converted to a String using + # String#to_s. + + def << (s) + do_write(s) + self + end + + ## + # Writes +args+ to the stream along with a record separator. + # + # See IO#puts for full details. + + def puts(*args) + s = "" + if args.empty? + s << "\n" + end + args.each{|arg| + s << arg.to_s + if $/ && /\n\z/ !~ s + s << "\n" + end + } + do_write(s) + nil + end + + ## + # Writes +args+ to the stream. + # + # See IO#print for full details. + + def print(*args) + s = "" + args.each{ |arg| s << arg.to_s } + do_write(s) + nil + end + + ## + # Formats and writes to the stream converting parameters under control of + # the format string. + # + # See Kernel#sprintf for format string details. + + def printf(s, *args) + do_write(s % args) + nil + end + + ## + # Flushes buffered data to the SSLSocket. + + def flush + osync = @sync + @sync = true + do_write "" + return self + ensure + @sync = osync + end + + ## + # Closes the SSLSocket and flushes any unwritten data. + + def close + flush rescue nil + sysclose + end +end diff --git a/ruby/lib/ruby/2.1.0/openssl/cipher.rb b/ruby/lib/ruby/2.1.0/openssl/cipher.rb new file mode 100644 index 0000000..c32cea2 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/openssl/cipher.rb @@ -0,0 +1,65 @@ +#-- +# +# $RCSfile$ +# +# = Ruby-space predefined Cipher subclasses +# +# = Info +# 'OpenSSL for Ruby 2' project +# Copyright (C) 2002 Michal Rokos +# All rights reserved. +# +# = Licence +# This program is licenced under the same licence as Ruby. +# (See the file 'LICENCE'.) +# +# = Version +# $Id: cipher.rb 36895 2012-09-04 00:57:31Z nobu $ +# +#++ + +module OpenSSL + class Cipher + %w(AES CAST5 BF DES IDEA RC2 RC4 RC5).each{|name| + klass = Class.new(Cipher){ + define_method(:initialize){|*args| + cipher_name = args.inject(name){|n, arg| "#{n}-#{arg}" } + super(cipher_name) + } + } + const_set(name, klass) + } + + %w(128 192 256).each{|keylen| + klass = Class.new(Cipher){ + define_method(:initialize){|mode| + mode ||= "CBC" + cipher_name = "AES-#{keylen}-#{mode}" + super(cipher_name) + } + } + const_set("AES#{keylen}", klass) + } + + # Generate, set, and return a random key. + # You must call cipher.encrypt or cipher.decrypt before calling this method. + def random_key + str = OpenSSL::Random.random_bytes(self.key_len) + self.key = str + return str + end + + # Generate, set, and return a random iv. + # You must call cipher.encrypt or cipher.decrypt before calling this method. + def random_iv + str = OpenSSL::Random.random_bytes(self.iv_len) + self.iv = str + return str + end + + # This class is only provided for backwards compatibility. Use OpenSSL::Cipher in the future. + class Cipher < Cipher + # add warning + end + end # Cipher +end # OpenSSL diff --git a/ruby/lib/ruby/2.1.0/openssl/config.rb b/ruby/lib/ruby/2.1.0/openssl/config.rb new file mode 100644 index 0000000..5716d59 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/openssl/config.rb @@ -0,0 +1,472 @@ +=begin += Ruby-space definitions that completes C-space funcs for Config + += Info + Copyright (C) 2010 Hiroshi Nakamura + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + +=end + +require 'stringio' + +module OpenSSL + ## + # = OpenSSL::Config + # + # Configuration for the openssl library. + # + # Many system's installation of openssl library will depend on your system + # configuration. See the value of OpenSSL::Config::DEFAULT_CONFIG_FILE for + # the location of the file for your host. + # + # See also http://www.openssl.org/docs/apps/config.html + class Config + include Enumerable + + class << self + + ## + # Parses a given +string+ as a blob that contains configuration for openssl. + # + # If the source of the IO is a file, then consider using #parse_config. + def parse(string) + c = new() + parse_config(StringIO.new(string)).each do |section, hash| + c[section] = hash + end + c + end + + ## + # load is an alias to ::new + alias load new + + ## + # Parses the configuration data read from +io+, see also #parse. + # + # Raises a ConfigError on invalid configuration data. + def parse_config(io) + begin + parse_config_lines(io) + rescue ConfigError => e + e.message.replace("error in line #{io.lineno}: " + e.message) + raise + end + end + + def get_key_string(data, section, key) # :nodoc: + if v = data[section] && data[section][key] + return v + elsif section == 'ENV' + if v = ENV[key] + return v + end + end + if v = data['default'] && data['default'][key] + return v + end + end + + private + + def parse_config_lines(io) + section = 'default' + data = {section => {}} + while definition = get_definition(io) + definition = clear_comments(definition) + next if definition.empty? + if definition[0] == ?[ + if /\[([^\]]*)\]/ =~ definition + section = $1.strip + data[section] ||= {} + else + raise ConfigError, "missing close square bracket" + end + else + if /\A([^:\s]*)(?:::([^:\s]*))?\s*=(.*)\z/ =~ definition + if $2 + section = $1 + key = $2 + else + key = $1 + end + value = unescape_value(data, section, $3) + (data[section] ||= {})[key] = value.strip + else + raise ConfigError, "missing equal sign" + end + end + end + data + end + + # escape with backslash + QUOTE_REGEXP_SQ = /\A([^'\\]*(?:\\.[^'\\]*)*)'/ + # escape with backslash and doubled dq + QUOTE_REGEXP_DQ = /\A([^"\\]*(?:""[^"\\]*|\\.[^"\\]*)*)"/ + # escaped char map + ESCAPE_MAP = { + "r" => "\r", + "n" => "\n", + "b" => "\b", + "t" => "\t", + } + + def unescape_value(data, section, value) + scanned = [] + while m = value.match(/['"\\$]/) + scanned << m.pre_match + c = m[0] + value = m.post_match + case c + when "'" + if m = value.match(QUOTE_REGEXP_SQ) + scanned << m[1].gsub(/\\(.)/, '\\1') + value = m.post_match + else + break + end + when '"' + if m = value.match(QUOTE_REGEXP_DQ) + scanned << m[1].gsub(/""/, '').gsub(/\\(.)/, '\\1') + value = m.post_match + else + break + end + when "\\" + c = value.slice!(0, 1) + scanned << (ESCAPE_MAP[c] || c) + when "$" + ref, value = extract_reference(value) + refsec = section + if ref.index('::') + refsec, ref = ref.split('::', 2) + end + if v = get_key_string(data, refsec, ref) + scanned << v + else + raise ConfigError, "variable has no value" + end + else + raise 'must not reaced' + end + end + scanned << value + scanned.join + end + + def extract_reference(value) + rest = '' + if m = value.match(/\(([^)]*)\)|\{([^}]*)\}/) + value = m[1] || m[2] + rest = m.post_match + elsif [?(, ?{].include?(value[0]) + raise ConfigError, "no close brace" + end + if m = value.match(/[a-zA-Z0-9_]*(?:::[a-zA-Z0-9_]*)?/) + return m[0], m.post_match + rest + else + raise + end + end + + def clear_comments(line) + # FCOMMENT + if m = line.match(/\A([\t\n\f ]*);.*\z/) + return m[1] + end + # COMMENT + scanned = [] + while m = line.match(/[#'"\\]/) + scanned << m.pre_match + c = m[0] + line = m.post_match + case c + when '#' + line = nil + break + when "'", '"' + regexp = (c == "'") ? QUOTE_REGEXP_SQ : QUOTE_REGEXP_DQ + scanned << c + if m = line.match(regexp) + scanned << m[0] + line = m.post_match + else + scanned << line + line = nil + break + end + when "\\" + scanned << c + scanned << line.slice!(0, 1) + else + raise 'must not reaced' + end + end + scanned << line + scanned.join + end + + def get_definition(io) + if line = get_line(io) + while /[^\\]\\\z/ =~ line + if extra = get_line(io) + line += extra + else + break + end + end + return line.strip + end + end + + def get_line(io) + if line = io.gets + line.gsub(/[\r\n]*/, '') + end + end + end + + ## + # Creates an instance of OpenSSL's configuration class. + # + # This can be used in contexts like OpenSSL::X509::ExtensionFactory.config= + # + # If the optional +filename+ parameter is provided, then it is read in and + # parsed via #parse_config. + # + # This can raise IO exceptions based on the access, or availability of the + # file. A ConfigError exception may be raised depending on the validity of + # the data being configured. + # + def initialize(filename = nil) + @data = {} + if filename + File.open(filename.to_s) do |file| + Config.parse_config(file).each do |section, hash| + self[section] = hash + end + end + end + end + + ## + # Gets the value of +key+ from the given +section+ + # + # Given the following configurating file being loaded: + # + # config = OpenSSL::Config.load('foo.cnf') + # #=> # + # puts config.to_s + # #=> [ default ] + # # foo=bar + # + # You can get a specific value from the config if you know the +section+ + # and +key+ like so: + # + # config.get_value('default','foo') + # #=> "bar" + # + def get_value(section, key) + if section.nil? + raise TypeError.new('nil not allowed') + end + section = 'default' if section.empty? + get_key_string(section, key) + end + + ## + # + # *Deprecated* + # + # Use #get_value instead + def value(arg1, arg2 = nil) # :nodoc: + warn('Config#value is deprecated; use Config#get_value') + if arg2.nil? + section, key = 'default', arg1 + else + section, key = arg1, arg2 + end + section ||= 'default' + section = 'default' if section.empty? + get_key_string(section, key) + end + + ## + # Set the target +key+ with a given +value+ under a specific +section+. + # + # Given the following configurating file being loaded: + # + # config = OpenSSL::Config.load('foo.cnf') + # #=> # + # puts config.to_s + # #=> [ default ] + # # foo=bar + # + # You can set the value of +foo+ under the +default+ section to a new + # value: + # + # config.add_value('default', 'foo', 'buzz') + # #=> "buzz" + # puts config.to_s + # #=> [ default ] + # # foo=buzz + # + def add_value(section, key, value) + check_modify + (@data[section] ||= {})[key] = value + end + + ## + # Get a specific +section+ from the current configuration + # + # Given the following configurating file being loaded: + # + # config = OpenSSL::Config.load('foo.cnf') + # #=> # + # puts config.to_s + # #=> [ default ] + # # foo=bar + # + # You can get a hash of the specific section like so: + # + # config['default'] + # #=> {"foo"=>"bar"} + # + def [](section) + @data[section] || {} + end + + ## + # Deprecated + # + # Use #[] instead + def section(name) # :nodoc: + warn('Config#section is deprecated; use Config#[]') + @data[name] || {} + end + + ## + # Sets a specific +section+ name with a Hash +pairs+ + # + # Given the following configuration being created: + # + # config = OpenSSL::Config.new + # #=> # + # config['default'] = {"foo"=>"bar","baz"=>"buz"} + # #=> {"foo"=>"bar", "baz"=>"buz"} + # puts config.to_s + # #=> [ default ] + # # foo=bar + # # baz=buz + # + # It's important to note that this will essentially merge any of the keys + # in +pairs+ with the existing +section+. For example: + # + # config['default'] + # #=> {"foo"=>"bar", "baz"=>"buz"} + # config['default'] = {"foo" => "changed"} + # #=> {"foo"=>"changed"} + # config['default'] + # #=> {"foo"=>"changed", "baz"=>"buz"} + # + def []=(section, pairs) + check_modify + @data[section] ||= {} + pairs.each do |key, value| + self.add_value(section, key, value) + end + end + + ## + # Get the names of all sections in the current configuration + def sections + @data.keys + end + + ## + # Get the parsable form of the current configuration + # + # Given the following configuration being created: + # + # config = OpenSSL::Config.new + # #=> # + # config['default'] = {"foo"=>"bar","baz"=>"buz"} + # #=> {"foo"=>"bar", "baz"=>"buz"} + # puts config.to_s + # #=> [ default ] + # # foo=bar + # # baz=buz + # + # You can parse get the serialized configuration using #to_s and then parse + # it later: + # + # serialized_config = config.to_s + # # much later... + # new_config = OpenSSL::Config.parse(serialized_config) + # #=> # + # puts new_config + # #=> [ default ] + # foo=bar + # baz=buz + # + def to_s + ary = [] + @data.keys.sort.each do |section| + ary << "[ #{section} ]\n" + @data[section].keys.each do |key| + ary << "#{key}=#{@data[section][key]}\n" + end + ary << "\n" + end + ary.join + end + + ## + # For a block. + # + # Receive the section and its pairs for the current configuration. + # + # config.each do |section, key, value| + # # ... + # end + # + def each + @data.each do |section, hash| + hash.each do |key, value| + yield [section, key, value] + end + end + end + + ## + # String representation of this configuration object, including the class + # name and its sections. + def inspect + "#<#{self.class.name} sections=#{sections.inspect}>" + end + + protected + + def data # :nodoc: + @data + end + + private + + def initialize_copy(other) + @data = other.data.dup + end + + def check_modify + raise TypeError.new("Insecure: can't modify OpenSSL config") if frozen? + end + + def get_key_string(section, key) + Config.get_key_string(@data, section, key) + end + end +end diff --git a/ruby/lib/ruby/2.1.0/openssl/digest.rb b/ruby/lib/ruby/2.1.0/openssl/digest.rb new file mode 100644 index 0000000..f63892a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/openssl/digest.rb @@ -0,0 +1,88 @@ +#-- +# +# $RCSfile$ +# +# = Ruby-space predefined Digest subclasses +# +# = Info +# 'OpenSSL for Ruby 2' project +# Copyright (C) 2002 Michal Rokos +# All rights reserved. +# +# = Licence +# This program is licenced under the same licence as Ruby. +# (See the file 'LICENCE'.) +# +# = Version +# $Id: digest.rb 44116 2013-12-10 07:16:03Z nobu $ +# +#++ + +module OpenSSL + class Digest + + alg = %w(DSS DSS1 MD2 MD4 MD5 MDC2 RIPEMD160 SHA SHA1) + if OPENSSL_VERSION_NUMBER > 0x00908000 + alg += %w(SHA224 SHA256 SHA384 SHA512) + end + + # Return the +data+ hash computed with +name+ Digest. +name+ is either the + # long name or short name of a supported digest algorithm. + # + # === Examples + # + # OpenSSL::Digest.digest("SHA256", "abc") + # + # which is equivalent to: + # + # OpenSSL::Digest::SHA256.digest("abc") + + def self.digest(name, data) + super(data, name) + end + + alg.each{|name| + klass = Class.new(self) { + define_method(:initialize, ->(data = nil) {super(name, data)}) + } + singleton = (class << klass; self; end) + singleton.class_eval{ + define_method(:digest){|data| new.digest(data) } + define_method(:hexdigest){|data| new.hexdigest(data) } + } + const_set(name, klass) + } + + # Deprecated. + # + # This class is only provided for backwards compatibility. + class Digest < Digest # :nodoc: + # Deprecated. + # + # See OpenSSL::Digest.new + def initialize(*args) + warn('Digest::Digest is deprecated; use Digest') + super(*args) + end + end + + end # Digest + + # Returns a Digest subclass by +name+. + # + # require 'openssl' + # + # OpenSSL::Digest("MD5") + # # => OpenSSL::Digest::MD5 + # + # Digest("Foo") + # # => NameError: wrong constant name Foo + + def Digest(name) + OpenSSL::Digest.const_get(name) + end + + module_function :Digest + +end # OpenSSL + diff --git a/ruby/lib/ruby/2.1.0/openssl/ssl.rb b/ruby/lib/ruby/2.1.0/openssl/ssl.rb new file mode 100644 index 0000000..8624147 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/openssl/ssl.rb @@ -0,0 +1,211 @@ +=begin += $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL + += Info + 'OpenSSL for Ruby 2' project + Copyright (C) 2001 GOTOU YUUZOU + All rights reserved. + += Licence + This program is licenced under the same licence as Ruby. + (See the file 'LICENCE'.) + += Version + $Id: ssl.rb 46613 2014-06-29 17:26:54Z nagachika $ +=end + +require "openssl/buffering" +require "fcntl" + +module OpenSSL + module SSL + class SSLContext + DEFAULT_PARAMS = { + :ssl_version => "SSLv23", + :verify_mode => OpenSSL::SSL::VERIFY_PEER, + :ciphers => "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW", + :options => defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS) ? + OpenSSL::SSL::OP_ALL & ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS : + OpenSSL::SSL::OP_ALL, + } + + DEFAULT_CERT_STORE = OpenSSL::X509::Store.new + DEFAULT_CERT_STORE.set_default_paths + if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL) + DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL + end + + ## + # Sets the parameters for this SSL context to the values in +params+. + # The keys in +params+ must be assignment methods on SSLContext. + # + # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and + # cert_store are not set then the system default certificate store is + # used. + + def set_params(params={}) + params = DEFAULT_PARAMS.merge(params) + params.each{|name, value| self.__send__("#{name}=", value) } + if self.verify_mode != OpenSSL::SSL::VERIFY_NONE + unless self.ca_file or self.ca_path or self.cert_store + self.cert_store = DEFAULT_CERT_STORE + end + end + return params + end + end + + module SocketForwarder + def addr + to_io.addr + end + + def peeraddr + to_io.peeraddr + end + + def setsockopt(level, optname, optval) + to_io.setsockopt(level, optname, optval) + end + + def getsockopt(level, optname) + to_io.getsockopt(level, optname) + end + + def fcntl(*args) + to_io.fcntl(*args) + end + + def closed? + to_io.closed? + end + + def do_not_reverse_lookup=(flag) + to_io.do_not_reverse_lookup = flag + end + end + + module Nonblock + def initialize(*args) + flag = File::NONBLOCK + flag |= @io.fcntl(Fcntl::F_GETFL) if defined?(Fcntl::F_GETFL) + @io.fcntl(Fcntl::F_SETFL, flag) + super + end + end + + def verify_certificate_identity(cert, hostname) + should_verify_common_name = true + cert.extensions.each{|ext| + next if ext.oid != "subjectAltName" + ostr = OpenSSL::ASN1.decode(ext.to_der).value.last + sequence = OpenSSL::ASN1.decode(ostr.value) + sequence.value.each{|san| + case san.tag + when 2 # dNSName in GeneralName (RFC5280) + should_verify_common_name = false + reg = Regexp.escape(san.value).gsub(/\\\*/, "[^.]+") + return true if /\A#{reg}\z/i =~ hostname + when 7 # iPAddress in GeneralName (RFC5280) + should_verify_common_name = false + # follows GENERAL_NAME_print() in x509v3/v3_alt.c + if san.value.size == 4 + return true if san.value.unpack('C*').join('.') == hostname + elsif san.value.size == 16 + return true if san.value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname + end + end + } + } + if should_verify_common_name + cert.subject.to_a.each{|oid, value| + if oid == "CN" + reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+") + return true if /\A#{reg}\z/i =~ hostname + end + } + end + return false + end + module_function :verify_certificate_identity + + class SSLSocket + include Buffering + include SocketForwarder + include Nonblock + + def post_connection_check(hostname) + unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname) + raise SSLError, "hostname \"#{hostname}\" does not match the server certificate" + end + return true + end + + def session + SSL::Session.new(self) + rescue SSL::Session::SessionError + nil + end + end + + ## + # SSLServer represents a TCP/IP server socket with Secure Sockets Layer. + class SSLServer + include SocketForwarder + # When true then #accept works exactly the same as TCPServer#accept + attr_accessor :start_immediately + + # Creates a new instance of SSLServer. + # * +srv+ is an instance of TCPServer. + # * +ctx+ is an instance of OpenSSL::SSL::SSLContext. + def initialize(svr, ctx) + @svr = svr + @ctx = ctx + unless ctx.session_id_context + # see #6137 - session id may not exceed 32 bytes + prng = ::Random.new($0.hash) + session_id = prng.bytes(16).unpack('H*')[0] + @ctx.session_id_context = session_id + end + @start_immediately = true + end + + # Returns the TCPServer passed to the SSLServer when initialized. + def to_io + @svr + end + + # See TCPServer#listen for details. + def listen(backlog=5) + @svr.listen(backlog) + end + + # See BasicSocket#shutdown for details. + def shutdown(how=Socket::SHUT_RDWR) + @svr.shutdown(how) + end + + # Works similar to TCPServer#accept. + def accept + # Socket#accept returns [socket, addrinfo]. + # TCPServer#accept returns a socket. + # The following comma strips addrinfo. + sock, = @svr.accept + begin + ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx) + ssl.sync_close = true + ssl.accept if @start_immediately + ssl + rescue SSLError => ex + sock.close + raise ex + end + end + + # See IO#close for details. + def close + @svr.close + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/openssl/x509.rb b/ruby/lib/ruby/2.1.0/openssl/x509.rb new file mode 100644 index 0000000..519553a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/openssl/x509.rb @@ -0,0 +1,162 @@ +#-- +# +# $RCSfile$ +# +# = Ruby-space definitions that completes C-space funcs for X509 and subclasses +# +# = Info +# 'OpenSSL for Ruby 2' project +# Copyright (C) 2002 Michal Rokos +# All rights reserved. +# +# = Licence +# This program is licenced under the same licence as Ruby. +# (See the file 'LICENCE'.) +# +# = Version +# $Id: x509.rb 36895 2012-09-04 00:57:31Z nobu $ +# +#++ + +module OpenSSL + module X509 + class ExtensionFactory + def create_extension(*arg) + if arg.size > 1 + create_ext(*arg) + else + send("create_ext_from_"+arg[0].class.name.downcase, arg[0]) + end + end + + def create_ext_from_array(ary) + raise ExtensionError, "unexpected array form" if ary.size > 3 + create_ext(ary[0], ary[1], ary[2]) + end + + def create_ext_from_string(str) # "oid = critical, value" + oid, value = str.split(/=/, 2) + oid.strip! + value.strip! + create_ext(oid, value) + end + + def create_ext_from_hash(hash) + create_ext(hash["oid"], hash["value"], hash["critical"]) + end + end + + class Extension + def to_s # "oid = critical, value" + str = self.oid + str << " = " + str << "critical, " if self.critical? + str << self.value.gsub(/\n/, ", ") + end + + def to_h # {"oid"=>sn|ln, "value"=>value, "critical"=>true|false} + {"oid"=>self.oid,"value"=>self.value,"critical"=>self.critical?} + end + + def to_a + [ self.oid, self.value, self.critical? ] + end + end + + class Name + module RFC2253DN + Special = ',=+<>#;' + HexChar = /[0-9a-fA-F]/ + HexPair = /#{HexChar}#{HexChar}/ + HexString = /#{HexPair}+/ + Pair = /\\(?:[#{Special}]|\\|"|#{HexPair})/ + StringChar = /[^#{Special}\\"]/ + QuoteChar = /[^\\"]/ + AttributeType = /[a-zA-Z][0-9a-zA-Z]*|[0-9]+(?:\.[0-9]+)*/ + AttributeValue = / + (?!["#])((?:#{StringChar}|#{Pair})*)| + \#(#{HexString})| + "((?:#{QuoteChar}|#{Pair})*)" + /x + TypeAndValue = /\A(#{AttributeType})=#{AttributeValue}/ + + module_function + + def expand_pair(str) + return nil unless str + return str.gsub(Pair){ + pair = $& + case pair.size + when 2 then pair[1,1] + when 3 then Integer("0x#{pair[1,2]}").chr + else raise OpenSSL::X509::NameError, "invalid pair: #{str}" + end + } + end + + def expand_hexstring(str) + return nil unless str + der = str.gsub(HexPair){$&.to_i(16).chr } + a1 = OpenSSL::ASN1.decode(der) + return a1.value, a1.tag + end + + def expand_value(str1, str2, str3) + value = expand_pair(str1) + value, tag = expand_hexstring(str2) unless value + value = expand_pair(str3) unless value + return value, tag + end + + def scan(dn) + str = dn + ary = [] + while true + if md = TypeAndValue.match(str) + remain = md.post_match + type = md[1] + value, tag = expand_value(md[2], md[3], md[4]) rescue nil + if value + type_and_value = [type, value] + type_and_value.push(tag) if tag + ary.unshift(type_and_value) + if remain.length > 2 && remain[0] == ?, + str = remain[1..-1] + next + elsif remain.length > 2 && remain[0] == ?+ + raise OpenSSL::X509::NameError, + "multi-valued RDN is not supported: #{dn}" + elsif remain.empty? + break + end + end + end + msg_dn = dn[0, dn.length - str.length] + " =>" + str + raise OpenSSL::X509::NameError, "malformed RDN: #{msg_dn}" + end + return ary + end + end + + class << self + def parse_rfc2253(str, template=OBJECT_TYPE_TEMPLATE) + ary = OpenSSL::X509::Name::RFC2253DN.scan(str) + self.new(ary, template) + end + + def parse_openssl(str, template=OBJECT_TYPE_TEMPLATE) + ary = str.scan(/\s*([^\/,]+)\s*/).collect{|i| i[0].split("=", 2) } + self.new(ary, template) + end + + alias parse parse_openssl + end + end + + class StoreContext + def cleanup + warn "(#{caller.first}) OpenSSL::X509::StoreContext#cleanup is deprecated with no replacement" if $VERBOSE + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/optparse.rb b/ruby/lib/ruby/2.1.0/optparse.rb new file mode 100644 index 0000000..6c4560f --- /dev/null +++ b/ruby/lib/ruby/2.1.0/optparse.rb @@ -0,0 +1,1956 @@ +# +# optparse.rb - command-line option analysis with the OptionParser class. +# +# Author:: Nobu Nakada +# Documentation:: Nobu Nakada and Gavin Sinclair. +# +# See OptionParser for documentation. +# + + +#-- +# == Developer Documentation (not for RDoc output) +# +# === Class tree +# +# - OptionParser:: front end +# - OptionParser::Switch:: each switches +# - OptionParser::List:: options list +# - OptionParser::ParseError:: errors on parsing +# - OptionParser::AmbiguousOption +# - OptionParser::NeedlessArgument +# - OptionParser::MissingArgument +# - OptionParser::InvalidOption +# - OptionParser::InvalidArgument +# - OptionParser::AmbiguousArgument +# +# === Object relationship diagram +# +# +--------------+ +# | OptionParser |<>-----+ +# +--------------+ | +--------+ +# | ,-| Switch | +# on_head -------->+---------------+ / +--------+ +# accept/reject -->| List |<|>- +# | |<|>- +----------+ +# on ------------->+---------------+ `-| argument | +# : : | class | +# +---------------+ |==========| +# on_tail -------->| | |pattern | +# +---------------+ |----------| +# OptionParser.accept ->| DefaultList | |converter | +# reject |(shared between| +----------+ +# | all instances)| +# +---------------+ +# +#++ +# +# == OptionParser +# +# === Introduction +# +# OptionParser is a class for command-line option analysis. It is much more +# advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented +# solution. +# +# === Features +# +# 1. The argument specification and the code to handle it are written in the +# same place. +# 2. It can output an option summary; you don't need to maintain this string +# separately. +# 3. Optional and mandatory arguments are specified very gracefully. +# 4. Arguments can be automatically converted to a specified class. +# 5. Arguments can be restricted to a certain set. +# +# All of these features are demonstrated in the examples below. See +# #make_switch for full documentation. +# +# === Minimal example +# +# require 'optparse' +# +# options = {} +# OptionParser.new do |opts| +# opts.banner = "Usage: example.rb [options]" +# +# opts.on("-v", "--[no-]verbose", "Run verbosely") do |v| +# options[:verbose] = v +# end +# end.parse! +# +# p options +# p ARGV +# +# === Complete example +# +# The following example is a complete Ruby program. You can run it and see the +# effect of specifying various options. This is probably the best way to learn +# the features of +optparse+. +# +# require 'optparse' +# require 'optparse/time' +# require 'ostruct' +# require 'pp' +# +# class OptparseExample +# +# CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary] +# CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" } +# +# # +# # Return a structure describing the options. +# # +# def self.parse(args) +# # The options specified on the command line will be collected in *options*. +# # We set default values here. +# options = OpenStruct.new +# options.library = [] +# options.inplace = false +# options.encoding = "utf8" +# options.transfer_type = :auto +# options.verbose = false +# +# opt_parser = OptionParser.new do |opts| +# opts.banner = "Usage: example.rb [options]" +# +# opts.separator "" +# opts.separator "Specific options:" +# +# # Mandatory argument. +# opts.on("-r", "--require LIBRARY", +# "Require the LIBRARY before executing your script") do |lib| +# options.library << lib +# end +# +# # Optional argument; multi-line description. +# opts.on("-i", "--inplace [EXTENSION]", +# "Edit ARGV files in place", +# " (make backup if EXTENSION supplied)") do |ext| +# options.inplace = true +# options.extension = ext || '' +# options.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot. +# end +# +# # Cast 'delay' argument to a Float. +# opts.on("--delay N", Float, "Delay N seconds before executing") do |n| +# options.delay = n +# end +# +# # Cast 'time' argument to a Time object. +# opts.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time| +# options.time = time +# end +# +# # Cast to octal integer. +# opts.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger, +# "Specify record separator (default \\0)") do |rs| +# options.record_separator = rs +# end +# +# # List of arguments. +# opts.on("--list x,y,z", Array, "Example 'list' of arguments") do |list| +# options.list = list +# end +# +# # Keyword completion. We are specifying a specific set of arguments (CODES +# # and CODE_ALIASES - notice the latter is a Hash), and the user may provide +# # the shortest unambiguous text. +# code_list = (CODE_ALIASES.keys + CODES).join(',') +# opts.on("--code CODE", CODES, CODE_ALIASES, "Select encoding", +# " (#{code_list})") do |encoding| +# options.encoding = encoding +# end +# +# # Optional argument with keyword completion. +# opts.on("--type [TYPE]", [:text, :binary, :auto], +# "Select transfer type (text, binary, auto)") do |t| +# options.transfer_type = t +# end +# +# # Boolean switch. +# opts.on("-v", "--[no-]verbose", "Run verbosely") do |v| +# options.verbose = v +# end +# +# opts.separator "" +# opts.separator "Common options:" +# +# # No argument, shows at tail. This will print an options summary. +# # Try it and see! +# opts.on_tail("-h", "--help", "Show this message") do +# puts opts +# exit +# end +# +# # Another typical switch to print the version. +# opts.on_tail("--version", "Show version") do +# puts ::Version.join('.') +# exit +# end +# end +# +# opt_parser.parse!(args) +# options +# end # parse() +# +# end # class OptparseExample +# +# options = OptparseExample.parse(ARGV) +# pp options +# pp ARGV +# +# === Shell Completion +# +# For modern shells (e.g. bash, zsh, etc.), you can use shell +# completion for command line options. +# +# === Further documentation +# +# The above examples should be enough to learn how to use this class. If you +# have any questions, file a ticket at http://bugs.ruby-lang.org. +# +class OptionParser + # :stopdoc: + NoArgument = [NO_ARGUMENT = :NONE, nil].freeze + RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze + OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze + # :startdoc: + + # + # Keyword completion module. This allows partial arguments to be specified + # and resolved against a list of acceptable values. + # + module Completion + def self.regexp(key, icase) + Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), icase) + end + + def self.candidate(key, icase = false, pat = nil, &block) + pat ||= Completion.regexp(key, icase) + candidates = [] + block.call do |k, *v| + (if Regexp === k + kn = nil + k === key + else + kn = defined?(k.id2name) ? k.id2name : k + pat === kn + end) or next + v << k if v.empty? + candidates << [k, v, kn] + end + candidates + end + + def candidate(key, icase = false, pat = nil) + Completion.candidate(key, icase, pat, &method(:each)) + end + + public + def complete(key, icase = false, pat = nil) + candidates = candidate(key, icase, pat, &method(:each)).sort_by {|k, v, kn| kn.size} + if candidates.size == 1 + canon, sw, * = candidates[0] + elsif candidates.size > 1 + canon, sw, cn = candidates.shift + candidates.each do |k, v, kn| + next if sw == v + if String === cn and String === kn + if cn.rindex(kn, 0) + canon, sw, cn = k, v, kn + next + elsif kn.rindex(cn, 0) + next + end + end + throw :ambiguous, key + end + end + if canon + block_given? or return key, *sw + yield(key, *sw) + end + end + + def convert(opt = nil, val = nil, *) + val + end + end + + + # + # Map from option/keyword string to object with completion. + # + class OptionMap < Hash + include Completion + end + + + # + # Individual switch class. Not important to the user. + # + # Defined within Switch are several Switch-derived classes: NoArgument, + # RequiredArgument, etc. + # + class Switch + attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block + + # + # Guesses argument style from +arg+. Returns corresponding + # OptionParser::Switch class (OptionalArgument, etc.). + # + def self.guess(arg) + case arg + when "" + t = self + when /\A=?\[/ + t = Switch::OptionalArgument + when /\A\s+\[/ + t = Switch::PlacedArgument + else + t = Switch::RequiredArgument + end + self >= t or incompatible_argument_styles(arg, t) + t + end + + def self.incompatible_argument_styles(arg, t) + raise(ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}", + ParseError.filter_backtrace(caller(2))) + end + + def self.pattern + NilClass + end + + def initialize(pattern = nil, conv = nil, + short = nil, long = nil, arg = nil, + desc = ([] if short or long), block = Proc.new) + raise if Array === pattern + @pattern, @conv, @short, @long, @arg, @desc, @block = + pattern, conv, short, long, arg, desc, block + end + + # + # Parses +arg+ and returns rest of +arg+ and matched portion to the + # argument pattern. Yields when the pattern doesn't match substring. + # + def parse_arg(arg) + pattern or return nil, [arg] + unless m = pattern.match(arg) + yield(InvalidArgument, arg) + return arg, [] + end + if String === m + m = [s = m] + else + m = m.to_a + s = m[0] + return nil, m unless String === s + end + raise InvalidArgument, arg unless arg.rindex(s, 0) + return nil, m if s.length == arg.length + yield(InvalidArgument, arg) # didn't match whole arg + return arg[s.length..-1], m + end + private :parse_arg + + # + # Parses argument, converts and returns +arg+, +block+ and result of + # conversion. Yields at semi-error condition instead of raising an + # exception. + # + def conv_arg(arg, val = []) + if conv + val = conv.call(*val) + else + val = proc {|v| v}.call(*val) + end + return arg, block, val + end + private :conv_arg + + # + # Produces the summary text. Each line of the summary is yielded to the + # block (without newline). + # + # +sdone+:: Already summarized short style options keyed hash. + # +ldone+:: Already summarized long style options keyed hash. + # +width+:: Width of left side (option part). In other words, the right + # side (description part) starts after +width+ columns. + # +max+:: Maximum width of left side -> the options are filled within + # +max+ columns. + # +indent+:: Prefix string indents all summarized lines. + # + def summarize(sdone = [], ldone = [], width = 1, max = width - 1, indent = "") + sopts, lopts = [], [], nil + @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short + @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long + return if sopts.empty? and lopts.empty? # completely hidden + + left = [sopts.join(', ')] + right = desc.dup + + while s = lopts.shift + l = left[-1].length + s.length + l += arg.length if left.size == 1 && arg + l < max or sopts.empty? or left << '' + left[-1] << if left[-1].empty? then ' ' * 4 else ', ' end << s + end + + if arg + left[0] << (left[1] ? arg.sub(/\A(\[?)=/, '\1') + ',' : arg) + end + mlen = left.collect {|ss| ss.length}.max.to_i + while mlen > width and l = left.shift + mlen = left.collect {|ss| ss.length}.max.to_i if l.length == mlen + if l.length < width and (r = right[0]) and !r.empty? + l = l.to_s.ljust(width) + ' ' + r + right.shift + end + yield(indent + l) + end + + while begin l = left.shift; r = right.shift; l or r end + l = l.to_s.ljust(width) + ' ' + r if r and !r.empty? + yield(indent + l) + end + + self + end + + def add_banner(to) # :nodoc: + unless @short or @long + s = desc.join + to << " [" + s + "]..." unless s.empty? + end + to + end + + def match_nonswitch?(str) # :nodoc: + @pattern =~ str unless @short or @long + end + + # + # Main name of the switch. + # + def switch_name + (long.first || short.first).sub(/\A-+(?:\[no-\])?/, '') + end + + def compsys(sdone, ldone) # :nodoc: + sopts, lopts = [], [] + @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short + @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long + return if sopts.empty? and lopts.empty? # completely hidden + + (sopts+lopts).each do |opt| + # "(-x -c -r)-l[left justify]" \ + if /^--\[no-\](.+)$/ =~ opt + o = $1 + yield("--#{o}", desc.join("")) + yield("--no-#{o}", desc.join("")) + else + yield("#{opt}", desc.join("")) + end + end + end + + # + # Switch that takes no arguments. + # + class NoArgument < self + + # + # Raises an exception if any arguments given. + # + def parse(arg, argv) + yield(NeedlessArgument, arg) if arg + conv_arg(arg) + end + + def self.incompatible_argument_styles(*) + end + + def self.pattern + Object + end + end + + # + # Switch that takes an argument. + # + class RequiredArgument < self + + # + # Raises an exception if argument is not present. + # + def parse(arg, argv) + unless arg + raise MissingArgument if argv.empty? + arg = argv.shift + end + conv_arg(*parse_arg(arg, &method(:raise))) + end + end + + # + # Switch that can omit argument. + # + class OptionalArgument < self + + # + # Parses argument if given, or uses default value. + # + def parse(arg, argv, &error) + if arg + conv_arg(*parse_arg(arg, &error)) + else + conv_arg(arg) + end + end + end + + # + # Switch that takes an argument, which does not begin with '-'. + # + class PlacedArgument < self + + # + # Returns nil if argument is not present or begins with '-'. + # + def parse(arg, argv, &error) + if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0])) + return nil, block, nil + end + opt = (val = parse_arg(val, &error))[1] + val = conv_arg(*val) + if opt and !arg + argv.shift + else + val[0] = nil + end + val + end + end + end + + # + # Simple option list providing mapping from short and/or long option + # string to OptionParser::Switch and mapping from acceptable argument to + # matching pattern and converter pair. Also provides summary feature. + # + class List + # Map from acceptable argument types to pattern and converter pairs. + attr_reader :atype + + # Map from short style option switches to actual switch objects. + attr_reader :short + + # Map from long style option switches to actual switch objects. + attr_reader :long + + # List of all switches and summary string. + attr_reader :list + + # + # Just initializes all instance variables. + # + def initialize + @atype = {} + @short = OptionMap.new + @long = OptionMap.new + @list = [] + end + + # + # See OptionParser.accept. + # + def accept(t, pat = /.*/m, &block) + if pat + pat.respond_to?(:match) or + raise TypeError, "has no `match'", ParseError.filter_backtrace(caller(2)) + else + pat = t if t.respond_to?(:match) + end + unless block + block = pat.method(:convert).to_proc if pat.respond_to?(:convert) + end + @atype[t] = [pat, block] + end + + # + # See OptionParser.reject. + # + def reject(t) + @atype.delete(t) + end + + # + # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+. + # + # +sw+:: OptionParser::Switch instance to be added. + # +sopts+:: Short style option list. + # +lopts+:: Long style option list. + # +nlopts+:: Negated long style options list. + # + def update(sw, sopts, lopts, nsw = nil, nlopts = nil) + sopts.each {|o| @short[o] = sw} if sopts + lopts.each {|o| @long[o] = sw} if lopts + nlopts.each {|o| @long[o] = nsw} if nsw and nlopts + used = @short.invert.update(@long.invert) + @list.delete_if {|o| Switch === o and !used[o]} + end + private :update + + # + # Inserts +switch+ at the head of the list, and associates short, long + # and negated long options. Arguments are: + # + # +switch+:: OptionParser::Switch instance to be inserted. + # +short_opts+:: List of short style options. + # +long_opts+:: List of long style options. + # +nolong_opts+:: List of long style options with "no-" prefix. + # + # prepend(switch, short_opts, long_opts, nolong_opts) + # + def prepend(*args) + update(*args) + @list.unshift(args[0]) + end + + # + # Appends +switch+ at the tail of the list, and associates short, long + # and negated long options. Arguments are: + # + # +switch+:: OptionParser::Switch instance to be inserted. + # +short_opts+:: List of short style options. + # +long_opts+:: List of long style options. + # +nolong_opts+:: List of long style options with "no-" prefix. + # + # append(switch, short_opts, long_opts, nolong_opts) + # + def append(*args) + update(*args) + @list.push(args[0]) + end + + # + # Searches +key+ in +id+ list. The result is returned or yielded if a + # block is given. If it isn't found, nil is returned. + # + def search(id, key) + if list = __send__(id) + val = list.fetch(key) {return nil} + block_given? ? yield(val) : val + end + end + + # + # Searches list +id+ for +opt+ and the optional patterns for completion + # +pat+. If +icase+ is true, the search is case insensitive. The result + # is returned or yielded if a block is given. If it isn't found, nil is + # returned. + # + def complete(id, opt, icase = false, *pat, &block) + __send__(id).complete(opt, icase, *pat, &block) + end + + # + # Iterates over each option, passing the option to the +block+. + # + def each_option(&block) + list.each(&block) + end + + # + # Creates the summary table, passing each line to the +block+ (without + # newline). The arguments +args+ are passed along to the summarize + # method which is called on every option. + # + def summarize(*args, &block) + sum = [] + list.reverse_each do |opt| + if opt.respond_to?(:summarize) # perhaps OptionParser::Switch + s = [] + opt.summarize(*args) {|l| s << l} + sum.concat(s.reverse) + elsif !opt or opt.empty? + sum << "" + elsif opt.respond_to?(:each_line) + sum.concat([*opt.each_line].reverse) + else + sum.concat([*opt.each].reverse) + end + end + sum.reverse_each(&block) + end + + def add_banner(to) # :nodoc: + list.each do |opt| + if opt.respond_to?(:add_banner) + opt.add_banner(to) + end + end + to + end + + def compsys(*args, &block) # :nodoc: + list.each do |opt| + if opt.respond_to?(:compsys) + opt.compsys(*args, &block) + end + end + end + end + + # + # Hash with completion search feature. See OptionParser::Completion. + # + class CompletingHash < Hash + include Completion + + # + # Completion for hash key. + # + def match(key) + *values = fetch(key) { + raise AmbiguousArgument, catch(:ambiguous) {return complete(key)} + } + return key, *values + end + end + + # :stopdoc: + + # + # Enumeration of acceptable argument styles. Possible values are: + # + # NO_ARGUMENT:: The switch takes no arguments. (:NONE) + # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED) + # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL) + # + # Use like --switch=argument (long style) or -Xargument (short style). For + # short style, only portion matched to argument pattern is treated as + # argument. + # + ArgumentStyle = {} + NoArgument.each {|el| ArgumentStyle[el] = Switch::NoArgument} + RequiredArgument.each {|el| ArgumentStyle[el] = Switch::RequiredArgument} + OptionalArgument.each {|el| ArgumentStyle[el] = Switch::OptionalArgument} + ArgumentStyle.freeze + + # + # Switches common used such as '--', and also provides default + # argument classes + # + DefaultList = List.new + DefaultList.short['-'] = Switch::NoArgument.new {} + DefaultList.long[''] = Switch::NoArgument.new {throw :terminate} + + + COMPSYS_HEADER = <<'XXX' # :nodoc: + +typeset -A opt_args +local context state line + +_arguments -s -S \ +XXX + + def compsys(to, name = File.basename($0)) # :nodoc: + to << "#compdef #{name}\n" + to << COMPSYS_HEADER + visit(:compsys, {}, {}) {|o, d| + to << %Q[ "#{o}[#{d.gsub(/[\"\[\]]/, '\\\\\&')}]" \\\n] + } + to << " '*:file:_files' && return 0\n" + end + + # + # Default options for ARGV, which never appear in option summary. + # + Officious = {} + + # + # --help + # Shows option summary. + # + Officious['help'] = proc do |parser| + Switch::NoArgument.new do |arg| + puts parser.help + exit + end + end + + # + # --*-completion-bash=WORD + # Shows candidates for command line completion. + # + Officious['*-completion-bash'] = proc do |parser| + Switch::RequiredArgument.new do |arg| + puts parser.candidate(arg) + exit + end + end + + # + # --*-completion-zsh[=NAME:FILE] + # Creates zsh completion file. + # + Officious['*-completion-zsh'] = proc do |parser| + Switch::OptionalArgument.new do |arg| + parser.compsys(STDOUT, arg) + exit + end + end + + # + # --version + # Shows version string if Version is defined. + # + Officious['version'] = proc do |parser| + Switch::OptionalArgument.new do |pkg| + if pkg + begin + require 'optparse/version' + rescue LoadError + else + show_version(*pkg.split(/,/)) or + abort("#{parser.program_name}: no version found in package #{pkg}") + exit + end + end + v = parser.ver or abort("#{parser.program_name}: version unknown") + puts v + exit + end + end + + # :startdoc: + + # + # Class methods + # + + # + # Initializes a new instance and evaluates the optional block in context + # of the instance. Arguments +args+ are passed to #new, see there for + # description of parameters. + # + # This method is *deprecated*, its behavior corresponds to the older #new + # method. + # + def self.with(*args, &block) + opts = new(*args) + opts.instance_eval(&block) + opts + end + + # + # Returns an incremented value of +default+ according to +arg+. + # + def self.inc(arg, default = nil) + case arg + when Integer + arg.nonzero? + when nil + default.to_i + 1 + end + end + def inc(*args) + self.class.inc(*args) + end + + # + # Initializes the instance and yields itself if called with a block. + # + # +banner+:: Banner message. + # +width+:: Summary width. + # +indent+:: Summary indent. + # + def initialize(banner = nil, width = 32, indent = ' ' * 4) + @stack = [DefaultList, List.new, List.new] + @program_name = nil + @banner = banner + @summary_width = width + @summary_indent = indent + @default_argv = ARGV + add_officious + yield self if block_given? + end + + def add_officious # :nodoc: + list = base() + Officious.each do |opt, block| + list.long[opt] ||= block.call(self) + end + end + + # + # Terminates option parsing. Optional parameter +arg+ is a string pushed + # back to be the first non-option argument. + # + def terminate(arg = nil) + self.class.terminate(arg) + end + def self.terminate(arg = nil) + throw :terminate, arg + end + + @stack = [DefaultList] + def self.top() DefaultList end + + # + # Directs to accept specified class +t+. The argument string is passed to + # the block in which it should be converted to the desired class. + # + # +t+:: Argument class specifier, any object including Class. + # +pat+:: Pattern for argument, defaults to +t+ if it responds to match. + # + # accept(t, pat, &block) + # + def accept(*args, &blk) top.accept(*args, &blk) end + # + # See #accept. + # + def self.accept(*args, &blk) top.accept(*args, &blk) end + + # + # Directs to reject specified class argument. + # + # +t+:: Argument class specifier, any object including Class. + # + # reject(t) + # + def reject(*args, &blk) top.reject(*args, &blk) end + # + # See #reject. + # + def self.reject(*args, &blk) top.reject(*args, &blk) end + + # + # Instance methods + # + + # Heading banner preceding summary. + attr_writer :banner + + # Program name to be emitted in error message and default banner, + # defaults to $0. + attr_writer :program_name + + # Width for option list portion of summary. Must be Numeric. + attr_accessor :summary_width + + # Indentation for summary. Must be String (or have + String method). + attr_accessor :summary_indent + + # Strings to be parsed in default. + attr_accessor :default_argv + + # + # Heading banner preceding summary. + # + def banner + unless @banner + @banner = "Usage: #{program_name} [options]" + visit(:add_banner, @banner) + end + @banner + end + + # + # Program name to be emitted in error message and default banner, defaults + # to $0. + # + def program_name + @program_name || File.basename($0, '.*') + end + + # for experimental cascading :-) + alias set_banner banner= + alias set_program_name program_name= + alias set_summary_width summary_width= + alias set_summary_indent summary_indent= + + # Version + attr_writer :version + # Release code + attr_writer :release + + # + # Version + # + def version + @version || (defined?(::Version) && ::Version) + end + + # + # Release code + # + def release + @release || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE) + end + + # + # Returns version string from program_name, version and release. + # + def ver + if v = version + str = "#{program_name} #{[v].join('.')}" + str << " (#{v})" if v = release + str + end + end + + def warn(mesg = $!) + super("#{program_name}: #{mesg}") + end + + def abort(mesg = $!) + super("#{program_name}: #{mesg}") + end + + # + # Subject of #on / #on_head, #accept / #reject + # + def top + @stack[-1] + end + + # + # Subject of #on_tail. + # + def base + @stack[1] + end + + # + # Pushes a new List. + # + def new + @stack.push(List.new) + if block_given? + yield self + else + self + end + end + + # + # Removes the last List. + # + def remove + @stack.pop + end + + # + # Puts option summary into +to+ and returns +to+. Yields each line if + # a block is given. + # + # +to+:: Output destination, which must have method <<. Defaults to []. + # +width+:: Width of left side, defaults to @summary_width. + # +max+:: Maximum length allowed for left side, defaults to +width+ - 1. + # +indent+:: Indentation, defaults to @summary_indent. + # + def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk) + blk ||= proc {|l| to << (l.index($/, -1) ? l : l + $/)} + visit(:summarize, {}, {}, width, max, indent, &blk) + to + end + + # + # Returns option summary string. + # + def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end + alias to_s help + + # + # Returns option summary list. + # + def to_a; summarize("#{banner}".split(/^/)) end + + # + # Checks if an argument is given twice, in which case an ArgumentError is + # raised. Called from OptionParser#switch only. + # + # +obj+:: New argument. + # +prv+:: Previously specified argument. + # +msg+:: Exception message. + # + def notwice(obj, prv, msg) + unless !prv or prv == obj + raise(ArgumentError, "argument #{msg} given twice: #{obj}", + ParseError.filter_backtrace(caller(2))) + end + obj + end + private :notwice + + SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a} # :nodoc: + # + # Creates an OptionParser::Switch from the parameters. The parsed argument + # value is passed to the given block, where it can be processed. + # + # See at the beginning of OptionParser for some full examples. + # + # +opts+ can include the following elements: + # + # [Argument style:] + # One of the following: + # :NONE, :REQUIRED, :OPTIONAL + # + # [Argument pattern:] + # Acceptable option argument format, must be pre-defined with + # OptionParser.accept or OptionParser#accept, or Regexp. This can appear + # once or assigned as String if not present, otherwise causes an + # ArgumentError. Examples: + # Float, Time, Array + # + # [Possible argument values:] + # Hash or Array. + # [:text, :binary, :auto] + # %w[iso-2022-jp shift_jis euc-jp utf8 binary] + # { "jis" => "iso-2022-jp", "sjis" => "shift_jis" } + # + # [Long style switch:] + # Specifies a long style switch which takes a mandatory, optional or no + # argument. It's a string of the following form: + # "--switch=MANDATORY" or "--switch MANDATORY" + # "--switch[=OPTIONAL]" + # "--switch" + # + # [Short style switch:] + # Specifies short style switch which takes a mandatory, optional or no + # argument. It's a string of the following form: + # "-xMANDATORY" + # "-x[OPTIONAL]" + # "-x" + # There is also a special form which matches character range (not full + # set of regular expression): + # "-[a-z]MANDATORY" + # "-[a-z][OPTIONAL]" + # "-[a-z]" + # + # [Argument style and description:] + # Instead of specifying mandatory or optional arguments directly in the + # switch parameter, this separate parameter can be used. + # "=MANDATORY" + # "=[OPTIONAL]" + # + # [Description:] + # Description string for the option. + # "Run verbosely" + # + # [Handler:] + # Handler for the parsed argument value. Either give a block or pass a + # Proc or Method as an argument. + # + def make_switch(opts, block = nil) + short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], [] + ldesc, sdesc, desc, arg = [], [], [] + default_style = Switch::NoArgument + default_pattern = nil + klass = nil + q, a = nil + + opts.each do |o| + # argument class + next if search(:atype, o) do |pat, c| + klass = notwice(o, klass, 'type') + if not_style and not_style != Switch::NoArgument + not_pattern, not_conv = pat, c + else + default_pattern, conv = pat, c + end + end + + # directly specified pattern(any object possible to match) + if (!(String === o || Symbol === o)) and o.respond_to?(:match) + pattern = notwice(o, pattern, 'pattern') + if pattern.respond_to?(:convert) + conv = pattern.method(:convert).to_proc + else + conv = SPLAT_PROC + end + next + end + + # anything others + case o + when Proc, Method + block = notwice(o, block, 'block') + when Array, Hash + case pattern + when CompletingHash + when nil + pattern = CompletingHash.new + conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert) + else + raise ArgumentError, "argument pattern given twice" + end + o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}} + when Module + raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4)) + when *ArgumentStyle.keys + style = notwice(ArgumentStyle[o], style, 'style') + when /^--no-([^\[\]=\s]*)(.+)?/ + q, a = $1, $2 + o = notwice(a ? Object : TrueClass, klass, 'type') + not_pattern, not_conv = search(:atype, o) unless not_style + not_style = (not_style || default_style).guess(arg = a) if a + default_style = Switch::NoArgument + default_pattern, conv = search(:atype, FalseClass) unless default_pattern + ldesc << "--no-#{q}" + long << 'no-' + (q = q.downcase) + nolong << q + when /^--\[no-\]([^\[\]=\s]*)(.+)?/ + q, a = $1, $2 + o = notwice(a ? Object : TrueClass, klass, 'type') + if a + default_style = default_style.guess(arg = a) + default_pattern, conv = search(:atype, o) unless default_pattern + end + ldesc << "--[no-]#{q}" + long << (o = q.downcase) + not_pattern, not_conv = search(:atype, FalseClass) unless not_style + not_style = Switch::NoArgument + nolong << 'no-' + o + when /^--([^\[\]=\s]*)(.+)?/ + q, a = $1, $2 + if a + o = notwice(NilClass, klass, 'type') + default_style = default_style.guess(arg = a) + default_pattern, conv = search(:atype, o) unless default_pattern + end + ldesc << "--#{q}" + long << (o = q.downcase) + when /^-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/ + q, a = $1, $2 + o = notwice(Object, klass, 'type') + if a + default_style = default_style.guess(arg = a) + default_pattern, conv = search(:atype, o) unless default_pattern + end + sdesc << "-#{q}" + short << Regexp.new(q) + when /^-(.)(.+)?/ + q, a = $1, $2 + if a + o = notwice(NilClass, klass, 'type') + default_style = default_style.guess(arg = a) + default_pattern, conv = search(:atype, o) unless default_pattern + end + sdesc << "-#{q}" + short << q + when /^=/ + style = notwice(default_style.guess(arg = o), style, 'style') + default_pattern, conv = search(:atype, Object) unless default_pattern + else + desc.push(o) + end + end + + default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern + if !(short.empty? and long.empty?) + s = (style || default_style).new(pattern || default_pattern, + conv, sdesc, ldesc, arg, desc, block) + elsif !block + if style or pattern + raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller) + end + s = desc + else + short << pattern + s = (style || default_style).new(pattern, + conv, nil, nil, arg, desc, block) + end + return s, short, long, + (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style), + nolong + end + + def define(*opts, &block) + top.append(*(sw = make_switch(opts, block))) + sw[0] + end + + # + # Add option switch and handler. See #make_switch for an explanation of + # parameters. + # + def on(*opts, &block) + define(*opts, &block) + self + end + alias def_option define + + def define_head(*opts, &block) + top.prepend(*(sw = make_switch(opts, block))) + sw[0] + end + + # + # Add option switch like with #on, but at head of summary. + # + def on_head(*opts, &block) + define_head(*opts, &block) + self + end + alias def_head_option define_head + + def define_tail(*opts, &block) + base.append(*(sw = make_switch(opts, block))) + sw[0] + end + + # + # Add option switch like with #on, but at tail of summary. + # + def on_tail(*opts, &block) + define_tail(*opts, &block) + self + end + alias def_tail_option define_tail + + # + # Add separator in summary. + # + def separator(string) + top.append(string, nil, nil) + end + + # + # Parses command line arguments +argv+ in order. When a block is given, + # each non-option argument is yielded. + # + # Returns the rest of +argv+ left unparsed. + # + def order(*argv, &block) + argv = argv[0].dup if argv.size == 1 and Array === argv[0] + order!(argv, &block) + end + + # + # Same as #order, but removes switches destructively. + # Non-option arguments remain in +argv+. + # + def order!(argv = default_argv, &nonopt) + parse_in_order(argv, &nonopt) + end + + def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc: + opt, arg, val, rest = nil + nonopt ||= proc {|a| throw :terminate, a} + argv.unshift(arg) if arg = catch(:terminate) { + while arg = argv.shift + case arg + # long option + when /\A--([^=]*)(?:=(.*))?/m + opt, rest = $1, $2 + begin + sw, = complete(:long, opt, true) + rescue ParseError + raise $!.set_option(arg, true) + end + begin + opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)} + val = cb.call(val) if cb + setter.call(sw.switch_name, val) if setter + rescue ParseError + raise $!.set_option(arg, rest) + end + + # short option + when /\A-(.)((=).*|.+)?/m + opt, has_arg, eq, val, rest = $1, $3, $3, $2, $2 + begin + sw, = search(:short, opt) + unless sw + begin + sw, = complete(:short, opt) + # short option matched. + val = arg.sub(/\A-/, '') + has_arg = true + rescue InvalidOption + # if no short options match, try completion with long + # options. + sw, = complete(:long, opt) + eq ||= !rest + end + end + rescue ParseError + raise $!.set_option(arg, true) + end + begin + opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq} + raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}" + argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-') + val = cb.call(val) if cb + setter.call(sw.switch_name, val) if setter + rescue ParseError + raise $!.set_option(arg, arg.length > 2) + end + + # non-option argument + else + catch(:prune) do + visit(:each_option) do |sw0| + sw = sw0 + sw.block.call(arg) if Switch === sw and sw.match_nonswitch?(arg) + end + nonopt.call(arg) + end + end + end + + nil + } + + visit(:search, :short, nil) {|sw| sw.block.call(*argv) if !sw.pattern} + + argv + end + private :parse_in_order + + # + # Parses command line arguments +argv+ in permutation mode and returns + # list of non-option arguments. + # + def permute(*argv) + argv = argv[0].dup if argv.size == 1 and Array === argv[0] + permute!(argv) + end + + # + # Same as #permute, but removes switches destructively. + # Non-option arguments remain in +argv+. + # + def permute!(argv = default_argv) + nonopts = [] + order!(argv, &nonopts.method(:<<)) + argv[0, 0] = nonopts + argv + end + + # + # Parses command line arguments +argv+ in order when environment variable + # POSIXLY_CORRECT is set, and in permutation mode otherwise. + # + def parse(*argv) + argv = argv[0].dup if argv.size == 1 and Array === argv[0] + parse!(argv) + end + + # + # Same as #parse, but removes switches destructively. + # Non-option arguments remain in +argv+. + # + def parse!(argv = default_argv) + if ENV.include?('POSIXLY_CORRECT') + order!(argv) + else + permute!(argv) + end + end + + # + # Wrapper method for getopts.rb. + # + # params = ARGV.getopts("ab:", "foo", "bar:") + # # params[:a] = true # -a + # # params[:b] = "1" # -b1 + # # params[:foo] = "1" # --foo + # # params[:bar] = "x" # --bar x + # + def getopts(*args) + argv = Array === args.first ? args.shift : default_argv + single_options, *long_options = *args + + result = {} + + single_options.scan(/(.)(:)?/) do |opt, val| + if val + result[opt] = nil + define("-#{opt} VAL") + else + result[opt] = false + define("-#{opt}") + end + end if single_options + + long_options.each do |arg| + opt, val = arg.split(':', 2) + if val + result[opt] = val.empty? ? nil : val + define("--#{opt} VAL") + else + result[opt] = false + define("--#{opt}") + end + end + + parse_in_order(argv, result.method(:[]=)) + result + end + + # + # See #getopts. + # + def self.getopts(*args) + new.getopts(*args) + end + + # + # Traverses @stack, sending each element method +id+ with +args+ and + # +block+. + # + def visit(id, *args, &block) + @stack.reverse_each do |el| + el.send(id, *args, &block) + end + nil + end + private :visit + + # + # Searches +key+ in @stack for +id+ hash and returns or yields the result. + # + def search(id, key) + block_given = block_given? + visit(:search, id, key) do |k| + return block_given ? yield(k) : k + end + end + private :search + + # + # Completes shortened long style option switch and returns pair of + # canonical switch and switch descriptor OptionParser::Switch. + # + # +id+:: Searching table. + # +opt+:: Searching key. + # +icase+:: Search case insensitive if true. + # +pat+:: Optional pattern for completion. + # + def complete(typ, opt, icase = false, *pat) + if pat.empty? + search(typ, opt) {|sw| return [sw, opt]} # exact match or... + end + raise AmbiguousOption, catch(:ambiguous) { + visit(:complete, typ, opt, icase, *pat) {|o, *sw| return sw} + raise InvalidOption, opt + } + end + private :complete + + def candidate(word) + list = [] + case word + when /\A--/ + word, arg = word.split(/=/, 2) + argpat = Completion.regexp(arg, false) if arg and !arg.empty? + long = true + when /\A-(!-)/ + short = true + when /\A-/ + long = short = true + end + pat = Completion.regexp(word, true) + visit(:each_option) do |opt| + next unless Switch === opt + opts = (long ? opt.long : []) + (short ? opt.short : []) + opts = Completion.candidate(word, true, pat, &opts.method(:each)).map(&:first) if pat + if /\A=/ =~ opt.arg + opts.map! {|sw| sw + "="} + if arg and CompletingHash === opt.pattern + if opts = opt.pattern.candidate(arg, false, argpat) + opts.map!(&:last) + end + end + end + list.concat(opts) + end + list + end + + # + # Loads options from file names as +filename+. Does nothing when the file + # is not present. Returns whether successfully loaded. + # + # +filename+ defaults to basename of the program without suffix in a + # directory ~/.options. + # + def load(filename = nil) + begin + filename ||= File.expand_path(File.basename($0, '.*'), '~/.options') + rescue + return false + end + begin + parse(*IO.readlines(filename).each {|s| s.chomp!}) + true + rescue Errno::ENOENT, Errno::ENOTDIR + false + end + end + + # + # Parses environment variable +env+ or its uppercase with splitting like a + # shell. + # + # +env+ defaults to the basename of the program. + # + def environment(env = File.basename($0, '.*')) + env = ENV[env] || ENV[env.upcase] or return + require 'shellwords' + parse(*Shellwords.shellwords(env)) + end + + # + # Acceptable argument classes + # + + # + # Any string and no conversion. This is fall-back. + # + accept(Object) {|s,|s or s.nil?} + + accept(NilClass) {|s,|s} + + # + # Any non-empty string, and no conversion. + # + accept(String, /.+/m) {|s,*|s} + + # + # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal + # for 0x, and decimal for others; with optional sign prefix. Converts to + # Integer. + # + decimal = '\d+(?:_\d+)*' + binary = 'b[01]+(?:_[01]+)*' + hex = 'x[\da-f]+(?:_[\da-f]+)*' + octal = "0(?:[0-7]+(?:_[0-7]+)*|#{binary}|#{hex})?" + integer = "#{octal}|#{decimal}" + + accept(Integer, %r"\A[-+]?(?:#{integer})\z"io) {|s,| + begin + Integer(s) + rescue ArgumentError + raise OptionParser::InvalidArgument, s + end if s + } + + # + # Float number format, and converts to Float. + # + float = "(?:#{decimal}(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?" + floatpat = %r"\A[-+]?#{float}\z"io + accept(Float, floatpat) {|s,| s.to_f if s} + + # + # Generic numeric format, converts to Integer for integer format, Float + # for float format, and Rational for rational format. + # + real = "[-+]?(?:#{octal}|#{float})" + accept(Numeric, /\A(#{real})(?:\/(#{real}))?\z/io) {|s, d, n| + if n + Rational(d, n) + elsif s + eval(s) + end + } + + # + # Decimal integer format, to be converted to Integer. + # + DecimalInteger = /\A[-+]?#{decimal}\z/io + accept(DecimalInteger, DecimalInteger) {|s,| + begin + Integer(s) + rescue ArgumentError + raise OptionParser::InvalidArgument, s + end if s + } + + # + # Ruby/C like octal/hexadecimal/binary integer format, to be converted to + # Integer. + # + OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))\z/io + accept(OctalInteger, OctalInteger) {|s,| + begin + Integer(s, 8) + rescue ArgumentError + raise OptionParser::InvalidArgument, s + end if s + } + + # + # Decimal integer/float number format, to be converted to Integer for + # integer format, Float for float format. + # + DecimalNumeric = floatpat # decimal integer is allowed as float also. + accept(DecimalNumeric, floatpat) {|s,| + begin + eval(s) + rescue SyntaxError + raise OptionParser::InvalidArgument, s + end if s + } + + # + # Boolean switch, which means whether it is present or not, whether it is + # absent or not with prefix no-, or it takes an argument + # yes/no/true/false/+/-. + # + yesno = CompletingHash.new + %w[- no false].each {|el| yesno[el] = false} + %w[+ yes true].each {|el| yesno[el] = true} + yesno['nil'] = false # should be nil? + accept(TrueClass, yesno) {|arg, val| val == nil or val} + # + # Similar to TrueClass, but defaults to false. + # + accept(FalseClass, yesno) {|arg, val| val != nil and val} + + # + # List of strings separated by ",". + # + accept(Array) do |s,| + if s + s = s.split(',').collect {|ss| ss unless ss.empty?} + end + s + end + + # + # Regular expression with options. + # + accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o| + f = 0 + if o + f |= Regexp::IGNORECASE if /i/ =~ o + f |= Regexp::MULTILINE if /m/ =~ o + f |= Regexp::EXTENDED if /x/ =~ o + k = o.delete("imx") + k = nil if k.empty? + end + Regexp.new(s || all, f, k) + end + + # + # Exceptions + # + + # + # Base class of exceptions from OptionParser. + # + class ParseError < RuntimeError + # Reason which caused the error. + Reason = 'parse error'.freeze + + def initialize(*args) + @args = args + @reason = nil + end + + attr_reader :args + attr_writer :reason + + # + # Pushes back erred argument(s) to +argv+. + # + def recover(argv) + argv[0, 0] = @args + argv + end + + def self.filter_backtrace(array) + unless $DEBUG + array.delete_if(&%r"\A#{Regexp.quote(__FILE__)}:"o.method(:=~)) + end + array + end + + def set_backtrace(array) + super(self.class.filter_backtrace(array)) + end + + def set_option(opt, eq) + if eq + @args[0] = opt + else + @args.unshift(opt) + end + self + end + + # + # Returns error reason. Override this for I18N. + # + def reason + @reason || self.class::Reason + end + + def inspect + "#<#{self.class.to_s}: #{args.join(' ')}>" + end + + # + # Default stringizing method to emit standard error message. + # + def message + reason + ': ' + args.join(' ') + end + + alias to_s message + end + + # + # Raises when ambiguously completable string is encountered. + # + class AmbiguousOption < ParseError + const_set(:Reason, 'ambiguous option'.freeze) + end + + # + # Raises when there is an argument for a switch which takes no argument. + # + class NeedlessArgument < ParseError + const_set(:Reason, 'needless argument'.freeze) + end + + # + # Raises when a switch with mandatory argument has no argument. + # + class MissingArgument < ParseError + const_set(:Reason, 'missing argument'.freeze) + end + + # + # Raises when switch is undefined. + # + class InvalidOption < ParseError + const_set(:Reason, 'invalid option'.freeze) + end + + # + # Raises when the given argument does not match required format. + # + class InvalidArgument < ParseError + const_set(:Reason, 'invalid argument'.freeze) + end + + # + # Raises when the given argument word can't be completed uniquely. + # + class AmbiguousArgument < InvalidArgument + const_set(:Reason, 'ambiguous argument'.freeze) + end + + # + # Miscellaneous + # + + # + # Extends command line arguments array (ARGV) to parse itself. + # + module Arguable + + # + # Sets OptionParser object, when +opt+ is +false+ or +nil+, methods + # OptionParser::Arguable#options and OptionParser::Arguable#options= are + # undefined. Thus, there is no ways to access the OptionParser object + # via the receiver object. + # + def options=(opt) + unless @optparse = opt + class << self + undef_method(:options) + undef_method(:options=) + end + end + end + + # + # Actual OptionParser object, automatically created if nonexistent. + # + # If called with a block, yields the OptionParser object and returns the + # result of the block. If an OptionParser::ParseError exception occurs + # in the block, it is rescued, a error message printed to STDERR and + # +nil+ returned. + # + def options + @optparse ||= OptionParser.new + @optparse.default_argv = self + block_given? or return @optparse + begin + yield @optparse + rescue ParseError + @optparse.warn $! + nil + end + end + + # + # Parses +self+ destructively in order and returns +self+ containing the + # rest arguments left unparsed. + # + def order!(&blk) options.order!(self, &blk) end + + # + # Parses +self+ destructively in permutation mode and returns +self+ + # containing the rest arguments left unparsed. + # + def permute!() options.permute!(self) end + + # + # Parses +self+ destructively and returns +self+ containing the + # rest arguments left unparsed. + # + def parse!() options.parse!(self) end + + # + # Substitution of getopts is possible as follows. Also see + # OptionParser#getopts. + # + # def getopts(*args) + # ($OPT = ARGV.getopts(*args)).each do |opt, val| + # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val" + # end + # rescue OptionParser::ParseError + # end + # + def getopts(*args) + options.getopts(self, *args) + end + + # + # Initializes instance variable. + # + def self.extend_object(obj) + super + obj.instance_eval {@optparse = nil} + end + def initialize(*args) + super + @optparse = nil + end + end + + # + # Acceptable argument classes. Now contains DecimalInteger, OctalInteger + # and DecimalNumeric. See Acceptable argument classes (in source code). + # + module Acceptables + const_set(:DecimalInteger, OptionParser::DecimalInteger) + const_set(:OctalInteger, OptionParser::OctalInteger) + const_set(:DecimalNumeric, OptionParser::DecimalNumeric) + end +end + +# ARGV is arguable by OptionParser +ARGV.extend(OptionParser::Arguable) diff --git a/ruby/lib/ruby/2.1.0/optparse/ac.rb b/ruby/lib/ruby/2.1.0/optparse/ac.rb new file mode 100644 index 0000000..6a86260 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/optparse/ac.rb @@ -0,0 +1,50 @@ +require 'optparse' + +class OptionParser::AC < OptionParser + private + + def _check_ac_args(name, block) + unless /\A\w[-\w]*\z/ =~ name + raise ArgumentError, name + end + unless block + raise ArgumentError, "no block given", ParseError.filter_backtrace(caller) + end + end + + def _ac_arg_enable(prefix, name, help_string, block) + _check_ac_args(name, block) + + sdesc = [] + ldesc = ["--#{prefix}-#{name}"] + desc = [help_string] + q = name.downcase + enable = Switch::NoArgument.new(nil, proc {true}, sdesc, ldesc, nil, desc, block) + disable = Switch::NoArgument.new(nil, proc {false}, sdesc, ldesc, nil, desc, block) + top.append(enable, [], ["enable-" + q], disable, ['disable-' + q]) + enable + end + + public + + def ac_arg_enable(name, help_string, &block) + _ac_arg_enable("enable", name, help_string, block) + end + + def ac_arg_disable(name, help_string, &block) + _ac_arg_enable("disable", name, help_string, block) + end + + def ac_arg_with(name, help_string, &block) + _check_ac_args(name, block) + + sdesc = [] + ldesc = ["--with-#{name}"] + desc = [help_string] + q = name.downcase + with = Switch::PlacedArgument.new(*search(:atype, String), sdesc, ldesc, nil, desc, block) + without = Switch::NoArgument.new(nil, proc {}, sdesc, ldesc, nil, desc, block) + top.append(with, [], ["with-" + q], without, ['without-' + q]) + with + end +end diff --git a/ruby/lib/ruby/2.1.0/optparse/date.rb b/ruby/lib/ruby/2.1.0/optparse/date.rb new file mode 100644 index 0000000..d680559 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/optparse/date.rb @@ -0,0 +1,17 @@ +require 'optparse' +require 'date' + +OptionParser.accept(DateTime) do |s,| + begin + DateTime.parse(s) if s + rescue ArgumentError + raise OptionParser::InvalidArgument, s + end +end +OptionParser.accept(Date) do |s,| + begin + Date.parse(s) if s + rescue ArgumentError + raise OptionParser::InvalidArgument, s + end +end diff --git a/ruby/lib/ruby/2.1.0/optparse/shellwords.rb b/ruby/lib/ruby/2.1.0/optparse/shellwords.rb new file mode 100644 index 0000000..0422d7c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/optparse/shellwords.rb @@ -0,0 +1,6 @@ +# -*- ruby -*- + +require 'shellwords' +require 'optparse' + +OptionParser.accept(Shellwords) {|s,| Shellwords.shellwords(s)} diff --git a/ruby/lib/ruby/2.1.0/optparse/time.rb b/ruby/lib/ruby/2.1.0/optparse/time.rb new file mode 100644 index 0000000..402cadc --- /dev/null +++ b/ruby/lib/ruby/2.1.0/optparse/time.rb @@ -0,0 +1,10 @@ +require 'optparse' +require 'time' + +OptionParser.accept(Time) do |s,| + begin + (Time.httpdate(s) rescue Time.parse(s)) if s + rescue + raise OptionParser::InvalidArgument, s + end +end diff --git a/ruby/lib/ruby/2.1.0/optparse/uri.rb b/ruby/lib/ruby/2.1.0/optparse/uri.rb new file mode 100644 index 0000000..024dc69 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/optparse/uri.rb @@ -0,0 +1,6 @@ +# -*- ruby -*- + +require 'optparse' +require 'uri' + +OptionParser.accept(URI) {|s,| URI.parse(s) if s} diff --git a/ruby/lib/ruby/2.1.0/optparse/version.rb b/ruby/lib/ruby/2.1.0/optparse/version.rb new file mode 100644 index 0000000..76ed564 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/optparse/version.rb @@ -0,0 +1,70 @@ +# OptionParser internal utility + +class << OptionParser + def show_version(*pkgs) + progname = ARGV.options.program_name + result = false + show = proc do |klass, cname, version| + str = "#{progname}" + unless klass == ::Object and cname == :VERSION + version = version.join(".") if Array === version + str << ": #{klass}" unless klass == Object + str << " version #{version}" + end + [:Release, :RELEASE].find do |rel| + if klass.const_defined?(rel) + str << " (#{klass.const_get(rel)})" + end + end + puts str + result = true + end + if pkgs.size == 1 and pkgs[0] == "all" + self.search_const(::Object, /\AV(?:ERSION|ersion)\z/) do |klass, cname, version| + unless cname[1] == ?e and klass.const_defined?(:Version) + show.call(klass, cname.intern, version) + end + end + else + pkgs.each do |pkg| + begin + pkg = pkg.split(/::|\//).inject(::Object) {|m, c| m.const_get(c)} + v = case + when pkg.const_defined?(:Version) + pkg.const_get(n = :Version) + when pkg.const_defined?(:VERSION) + pkg.const_get(n = :VERSION) + else + n = nil + "unknown" + end + show.call(pkg, n, v) + rescue NameError + end + end + end + result + end + + def each_const(path, base = ::Object) + path.split(/::|\//).inject(base) do |klass, name| + raise NameError, path unless Module === klass + klass.constants.grep(/#{name}/i) do |c| + klass.const_defined?(c) or next + c = klass.const_get(c) + end + end + end + + def search_const(klass, name) + klasses = [klass] + while klass = klasses.shift + klass.constants.each do |cname| + klass.const_defined?(cname) or next + const = klass.const_get(cname) + yield klass, cname, const if name === cname + klasses << const if Module === const and const != ::Object + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/ostruct.rb b/ruby/lib/ruby/2.1.0/ostruct.rb new file mode 100644 index 0000000..f51eb7b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/ostruct.rb @@ -0,0 +1,286 @@ +# +# = ostruct.rb: OpenStruct implementation +# +# Author:: Yukihiro Matsumoto +# Documentation:: Gavin Sinclair +# +# OpenStruct allows the creation of data objects with arbitrary attributes. +# See OpenStruct for an example. +# + +# +# An OpenStruct is a data structure, similar to a Hash, that allows the +# definition of arbitrary attributes with their accompanying values. This is +# accomplished by using Ruby's metaprogramming to define methods on the class +# itself. +# +# == Examples: +# +# require 'ostruct' +# +# person = OpenStruct.new +# person.name = "John Smith" +# person.age = 70 +# person.pension = 300 +# +# puts person.name # -> "John Smith" +# puts person.age # -> 70 +# puts person.address # -> nil +# +# An OpenStruct employs a Hash internally to store the methods and values and +# can even be initialized with one: +# +# australia = OpenStruct.new(:country => "Australia", :population => 20_000_000) +# p australia # -> +# +# Hash keys with spaces or characters that would normally not be able to use for +# method calls (e.g. ()[]*) will not be immediately available on the +# OpenStruct object as a method for retrieval or assignment, but can be still be +# reached through the Object#send method. +# +# measurements = OpenStruct.new("length (in inches)" => 24) +# measurements.send("length (in inches)") # -> 24 +# +# data_point = OpenStruct.new(:queued? => true) +# data_point.queued? # -> true +# data_point.send("queued?=",false) +# data_point.queued? # -> false +# +# Removing the presence of a method requires the execution the delete_field +# method as setting the property value to +nil+ will not remove the method. +# +# first_pet = OpenStruct.new(:name => 'Rowdy', :owner => 'John Smith') +# first_pet.owner = nil +# second_pet = OpenStruct.new(:name => 'Rowdy') +# +# first_pet == second_pet # -> false +# +# first_pet.delete_field(:owner) +# first_pet == second_pet # -> true +# +# +# == Implementation: +# +# An OpenStruct utilizes Ruby's method lookup structure to find and define the +# necessary methods for properties. This is accomplished through the method +# method_missing and define_method. +# +# This should be a consideration if there is a concern about the performance of +# the objects that are created, as there is much more overhead in the setting +# of these properties compared to using a Hash or a Struct. +# +class OpenStruct + # + # Creates a new OpenStruct object. By default, the resulting OpenStruct + # object will have no attributes. + # + # The optional +hash+, if given, will generate attributes and values + # (can be a Hash, an OpenStruct or a Struct). + # For example: + # + # require 'ostruct' + # hash = { "country" => "Australia", :population => 20_000_000 } + # data = OpenStruct.new(hash) + # + # p data # -> + # + def initialize(hash=nil) + @table = {} + if hash + hash.each_pair do |k, v| + k = k.to_sym + @table[k] = v + new_ostruct_member(k) + end + end + end + + # Duplicate an OpenStruct object members. + def initialize_copy(orig) + super + @table = @table.dup + @table.each_key{|key| new_ostruct_member(key)} + end + + # + # Converts the OpenStruct to a hash with keys representing + # each attribute (as symbols) and their corresponding values + # Example: + # + # require 'ostruct' + # data = OpenStruct.new("country" => "Australia", :population => 20_000_000) + # data.to_h # => {:country => "Australia", :population => 20000000 } + # + def to_h + @table.dup + end + + # + # Yields all attributes (as a symbol) along with the corresponding values + # or returns an enumerator if not block is given. + # Example: + # + # require 'ostruct' + # data = OpenStruct.new("country" => "Australia", :population => 20_000_000) + # data.each_pair.to_a # => [[:country, "Australia"], [:population, 20000000]] + # + def each_pair + return to_enum(__method__) { @table.size } unless block_given? + @table.each_pair{|p| yield p} + end + + # + # Provides marshalling support for use by the Marshal library. + # + def marshal_dump + @table + end + + # + # Provides marshalling support for use by the Marshal library. + # + def marshal_load(x) + @table = x + @table.each_key{|key| new_ostruct_member(key)} + end + + # + # Used internally to check if the OpenStruct is able to be + # modified before granting access to the internal Hash table to be modified. + # + def modifiable + begin + @modifiable = true + rescue + raise RuntimeError, "can't modify frozen #{self.class}", caller(3) + end + @table + end + protected :modifiable + + # + # Used internally to defined properties on the + # OpenStruct. It does this by using the metaprogramming function + # define_singleton_method for both the getter method and the setter method. + # + def new_ostruct_member(name) + name = name.to_sym + unless respond_to?(name) + define_singleton_method(name) { @table[name] } + define_singleton_method("#{name}=") { |x| modifiable[name] = x } + end + name + end + protected :new_ostruct_member + + def method_missing(mid, *args) # :nodoc: + mname = mid.id2name + len = args.length + if mname.chomp!('=') + if len != 1 + raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1) + end + modifiable[new_ostruct_member(mname)] = args[0] + elsif len == 0 + @table[mid] + else + err = NoMethodError.new "undefined method `#{mid}' for #{self}", mid, args + err.set_backtrace caller(1) + raise err + end + end + + # Returns the value of a member. + # + # person = OpenStruct.new('name' => 'John Smith', 'age' => 70) + # person[:age] # => 70, same as ostruct.age + # + def [](name) + @table[name.to_sym] + end + + # + # Sets the value of a member. + # + # person = OpenStruct.new('name' => 'John Smith', 'age' => 70) + # person[:age] = 42 # => equivalent to ostruct.age = 42 + # person.age # => 42 + # + def []=(name, value) + modifiable[new_ostruct_member(name)] = value + end + + # + # Remove the named field from the object. Returns the value that the field + # contained if it was defined. + # + # require 'ostruct' + # + # person = OpenStruct.new('name' => 'John Smith', 'age' => 70) + # + # person.delete_field('name') # => 'John Smith' + # + def delete_field(name) + sym = name.to_sym + singleton_class.__send__(:remove_method, sym, "#{sym}=") + @table.delete sym + end + + InspectKey = :__inspect_key__ # :nodoc: + + # + # Returns a string containing a detailed summary of the keys and values. + # + def inspect + str = "#<#{self.class}" + + ids = (Thread.current[InspectKey] ||= []) + if ids.include?(object_id) + return str << ' ...>' + end + + ids << object_id + begin + first = true + for k,v in @table + str << "," unless first + first = false + str << " #{k}=#{v.inspect}" + end + return str << '>' + ensure + ids.pop + end + end + alias :to_s :inspect + + attr_reader :table # :nodoc: + protected :table + + # + # Compares this object and +other+ for equality. An OpenStruct is equal to + # +other+ when +other+ is an OpenStruct and the two objects' Hash tables are + # equal. + # + def ==(other) + return false unless other.kind_of?(OpenStruct) + @table == other.table + end + + # + # Compares this object and +other+ for equality. An OpenStruct is eql? to + # +other+ when +other+ is an OpenStruct and the two objects' Hash tables are + # eql?. + # + def eql?(other) + return false unless other.kind_of?(OpenStruct) + @table.eql?(other.table) + end + + # Compute a hash-code for this OpenStruct. + # Two hashes with the same content will have the same hash code + # (and will be eql?). + def hash + @table.hash + end +end diff --git a/ruby/lib/ruby/2.1.0/pathname.rb b/ruby/lib/ruby/2.1.0/pathname.rb new file mode 100644 index 0000000..8bce81e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/pathname.rb @@ -0,0 +1,574 @@ +# +# = pathname.rb +# +# Object-Oriented Pathname Class +# +# Author:: Tanaka Akira +# Documentation:: Author and Gavin Sinclair +# +# For documentation, see class Pathname. +# + +require 'pathname.so' + +class Pathname + + # :stopdoc: + if RUBY_VERSION < "1.9" + TO_PATH = :to_str + else + # to_path is implemented so Pathname objects are usable with File.open, etc. + TO_PATH = :to_path + end + + SAME_PATHS = if File::FNM_SYSCASE.nonzero? + # Avoid #zero? here because #casecmp can return nil. + proc {|a, b| a.casecmp(b) == 0} + else + proc {|a, b| a == b} + end + + + if File::ALT_SEPARATOR + SEPARATOR_LIST = "#{Regexp.quote File::ALT_SEPARATOR}#{Regexp.quote File::SEPARATOR}" + SEPARATOR_PAT = /[#{SEPARATOR_LIST}]/ + else + SEPARATOR_LIST = "#{Regexp.quote File::SEPARATOR}" + SEPARATOR_PAT = /#{Regexp.quote File::SEPARATOR}/ + end + + # :startdoc: + + # chop_basename(path) -> [pre-basename, basename] or nil + def chop_basename(path) # :nodoc: + base = File.basename(path) + if /\A#{SEPARATOR_PAT}?\z/o =~ base + return nil + else + return path[0, path.rindex(base)], base + end + end + private :chop_basename + + # split_names(path) -> prefix, [name, ...] + def split_names(path) # :nodoc: + names = [] + while r = chop_basename(path) + path, basename = r + names.unshift basename + end + return path, names + end + private :split_names + + def prepend_prefix(prefix, relpath) # :nodoc: + if relpath.empty? + File.dirname(prefix) + elsif /#{SEPARATOR_PAT}/o =~ prefix + prefix = File.dirname(prefix) + prefix = File.join(prefix, "") if File.basename(prefix + 'a') != 'a' + prefix + relpath + else + prefix + relpath + end + end + private :prepend_prefix + + # Returns clean pathname of +self+ with consecutive slashes and useless dots + # removed. The filesystem is not accessed. + # + # If +consider_symlink+ is +true+, then a more conservative algorithm is used + # to avoid breaking symbolic linkages. This may retain more +..+ + # entries than absolutely necessary, but without accessing the filesystem, + # this can't be avoided. + # + # See Pathname#realpath. + # + def cleanpath(consider_symlink=false) + if consider_symlink + cleanpath_conservative + else + cleanpath_aggressive + end + end + + # + # Clean the path simply by resolving and removing excess +.+ and +..+ entries. + # Nothing more, nothing less. + # + def cleanpath_aggressive # :nodoc: + path = @path + names = [] + pre = path + while r = chop_basename(pre) + pre, base = r + case base + when '.' + when '..' + names.unshift base + else + if names[0] == '..' + names.shift + else + names.unshift base + end + end + end + pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR + if /#{SEPARATOR_PAT}/o =~ File.basename(pre) + names.shift while names[0] == '..' + end + self.class.new(prepend_prefix(pre, File.join(*names))) + end + private :cleanpath_aggressive + + # has_trailing_separator?(path) -> bool + def has_trailing_separator?(path) # :nodoc: + if r = chop_basename(path) + pre, basename = r + pre.length + basename.length < path.length + else + false + end + end + private :has_trailing_separator? + + # add_trailing_separator(path) -> path + def add_trailing_separator(path) # :nodoc: + if File.basename(path + 'a') == 'a' + path + else + File.join(path, "") # xxx: Is File.join is appropriate to add separator? + end + end + private :add_trailing_separator + + def del_trailing_separator(path) # :nodoc: + if r = chop_basename(path) + pre, basename = r + pre + basename + elsif /#{SEPARATOR_PAT}+\z/o =~ path + $` + File.dirname(path)[/#{SEPARATOR_PAT}*\z/o] + else + path + end + end + private :del_trailing_separator + + def cleanpath_conservative # :nodoc: + path = @path + names = [] + pre = path + while r = chop_basename(pre) + pre, base = r + names.unshift base if base != '.' + end + pre.tr!(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR + if /#{SEPARATOR_PAT}/o =~ File.basename(pre) + names.shift while names[0] == '..' + end + if names.empty? + self.class.new(File.dirname(pre)) + else + if names.last != '..' && File.basename(path) == '.' + names << '.' + end + result = prepend_prefix(pre, File.join(*names)) + if /\A(?:\.|\.\.)\z/ !~ names.last && has_trailing_separator?(path) + self.class.new(add_trailing_separator(result)) + else + self.class.new(result) + end + end + end + private :cleanpath_conservative + + # Returns the parent directory. + # + # This is same as self + '..'. + def parent + self + '..' + end + + # Returns +true+ if +self+ points to a mountpoint. + def mountpoint? + begin + stat1 = self.lstat + stat2 = self.parent.lstat + stat1.dev == stat2.dev && stat1.ino == stat2.ino || + stat1.dev != stat2.dev + rescue Errno::ENOENT + false + end + end + + # + # Predicate method for root directories. Returns +true+ if the + # pathname consists of consecutive slashes. + # + # It doesn't access the filesystem. So it may return +false+ for some + # pathnames which points to roots such as /usr/... + # + def root? + !!(chop_basename(@path) == nil && /#{SEPARATOR_PAT}/o =~ @path) + end + + # Predicate method for testing whether a path is absolute. + # + # It returns +true+ if the pathname begins with a slash. + # + # p = Pathname.new('/im/sure') + # p.absolute? + # #=> true + # + # p = Pathname.new('not/so/sure') + # p.absolute? + # #=> false + def absolute? + !relative? + end + + # The opposite of Pathname#absolute? + # + # It returns +false+ if the pathname begins with a slash. + # + # p = Pathname.new('/im/sure') + # p.relative? + # #=> false + # + # p = Pathname.new('not/so/sure') + # p.relative? + # #=> true + def relative? + path = @path + while r = chop_basename(path) + path, = r + end + path == '' + end + + # + # Iterates over each component of the path. + # + # Pathname.new("/usr/bin/ruby").each_filename {|filename| ... } + # # yields "usr", "bin", and "ruby". + # + # Returns an Enumerator if no block was given. + # + # enum = Pathname.new("/usr/bin/ruby").each_filename + # # ... do stuff ... + # enum.each { |e| ... } + # # yields "usr", "bin", and "ruby". + # + def each_filename # :yield: filename + return to_enum(__method__) unless block_given? + _, names = split_names(@path) + names.each {|filename| yield filename } + nil + end + + # Iterates over and yields a new Pathname object + # for each element in the given path in descending order. + # + # Pathname.new('/path/to/some/file.rb').descend {|v| p v} + # # + # # + # # + # # + # # + # + # Pathname.new('path/to/some/file.rb').descend {|v| p v} + # # + # # + # # + # # + # + # It doesn't access the filesystem. + # + def descend + vs = [] + ascend {|v| vs << v } + vs.reverse_each {|v| yield v } + nil + end + + # Iterates over and yields a new Pathname object + # for each element in the given path in ascending order. + # + # Pathname.new('/path/to/some/file.rb').ascend {|v| p v} + # # + # # + # # + # # + # # + # + # Pathname.new('path/to/some/file.rb').ascend {|v| p v} + # # + # # + # # + # # + # + # It doesn't access the filesystem. + # + def ascend + path = @path + yield self + while r = chop_basename(path) + path, = r + break if path.empty? + yield self.class.new(del_trailing_separator(path)) + end + end + + # + # Appends a pathname fragment to +self+ to produce a new Pathname object. + # + # p1 = Pathname.new("/usr") # Pathname:/usr + # p2 = p1 + "bin/ruby" # Pathname:/usr/bin/ruby + # p3 = p1 + "/etc/passwd" # Pathname:/etc/passwd + # + # This method doesn't access the file system; it is pure string manipulation. + # + def +(other) + other = Pathname.new(other) unless Pathname === other + Pathname.new(plus(@path, other.to_s)) + end + + def plus(path1, path2) # -> path # :nodoc: + prefix2 = path2 + index_list2 = [] + basename_list2 = [] + while r2 = chop_basename(prefix2) + prefix2, basename2 = r2 + index_list2.unshift prefix2.length + basename_list2.unshift basename2 + end + return path2 if prefix2 != '' + prefix1 = path1 + while true + while !basename_list2.empty? && basename_list2.first == '.' + index_list2.shift + basename_list2.shift + end + break unless r1 = chop_basename(prefix1) + prefix1, basename1 = r1 + next if basename1 == '.' + if basename1 == '..' || basename_list2.empty? || basename_list2.first != '..' + prefix1 = prefix1 + basename1 + break + end + index_list2.shift + basename_list2.shift + end + r1 = chop_basename(prefix1) + if !r1 && /#{SEPARATOR_PAT}/o =~ File.basename(prefix1) + while !basename_list2.empty? && basename_list2.first == '..' + index_list2.shift + basename_list2.shift + end + end + if !basename_list2.empty? + suffix2 = path2[index_list2.first..-1] + r1 ? File.join(prefix1, suffix2) : prefix1 + suffix2 + else + r1 ? prefix1 : File.dirname(prefix1) + end + end + private :plus + + # + # Joins the given pathnames onto +self+ to create a new Pathname object. + # + # path0 = Pathname.new("/usr") # Pathname:/usr + # path0 = path0.join("bin/ruby") # Pathname:/usr/bin/ruby + # # is the same as + # path1 = Pathname.new("/usr") + "bin/ruby" # Pathname:/usr/bin/ruby + # path0 == path1 + # #=> true + # + def join(*args) + args.unshift self + result = args.pop + result = Pathname.new(result) unless Pathname === result + return result if result.absolute? + args.reverse_each {|arg| + arg = Pathname.new(arg) unless Pathname === arg + result = arg + result + return result if result.absolute? + } + result + end + + # + # Returns the children of the directory (files and subdirectories, not + # recursive) as an array of Pathname objects. + # + # By default, the returned pathnames will have enough information to access + # the files. If you set +with_directory+ to +false+, then the returned + # pathnames will contain the filename only. + # + # For example: + # pn = Pathname("/usr/lib/ruby/1.8") + # pn.children + # # -> [ Pathname:/usr/lib/ruby/1.8/English.rb, + # Pathname:/usr/lib/ruby/1.8/Env.rb, + # Pathname:/usr/lib/ruby/1.8/abbrev.rb, ... ] + # pn.children(false) + # # -> [ Pathname:English.rb, Pathname:Env.rb, Pathname:abbrev.rb, ... ] + # + # Note that the results never contain the entries +.+ and +..+ in + # the directory because they are not children. + # + def children(with_directory=true) + with_directory = false if @path == '.' + result = [] + Dir.foreach(@path) {|e| + next if e == '.' || e == '..' + if with_directory + result << self.class.new(File.join(@path, e)) + else + result << self.class.new(e) + end + } + result + end + + # Iterates over the children of the directory + # (files and subdirectories, not recursive). + # + # It yields Pathname object for each child. + # + # By default, the yielded pathnames will have enough information to access + # the files. + # + # If you set +with_directory+ to +false+, then the returned pathnames will + # contain the filename only. + # + # Pathname("/usr/local").each_child {|f| p f } + # #=> # + # # # + # # # + # # # + # # # + # # # + # # # + # # # + # + # Pathname("/usr/local").each_child(false) {|f| p f } + # #=> # + # # # + # # # + # # # + # # # + # # # + # # # + # # # + # + # Note that the results never contain the entries +.+ and +..+ in + # the directory because they are not children. + # + # See Pathname#children + # + def each_child(with_directory=true, &b) + children(with_directory).each(&b) + end + + # + # Returns a relative path from the given +base_directory+ to the receiver. + # + # If +self+ is absolute, then +base_directory+ must be absolute too. + # + # If +self+ is relative, then +base_directory+ must be relative too. + # + # This method doesn't access the filesystem. It assumes no symlinks. + # + # ArgumentError is raised when it cannot find a relative path. + # + def relative_path_from(base_directory) + dest_directory = self.cleanpath.to_s + base_directory = base_directory.cleanpath.to_s + dest_prefix = dest_directory + dest_names = [] + while r = chop_basename(dest_prefix) + dest_prefix, basename = r + dest_names.unshift basename if basename != '.' + end + base_prefix = base_directory + base_names = [] + while r = chop_basename(base_prefix) + base_prefix, basename = r + base_names.unshift basename if basename != '.' + end + unless SAME_PATHS[dest_prefix, base_prefix] + raise ArgumentError, "different prefix: #{dest_prefix.inspect} and #{base_directory.inspect}" + end + while !dest_names.empty? && + !base_names.empty? && + SAME_PATHS[dest_names.first, base_names.first] + dest_names.shift + base_names.shift + end + if base_names.include? '..' + raise ArgumentError, "base_directory has ..: #{base_directory.inspect}" + end + base_names.fill('..') + relpath_names = base_names + dest_names + if relpath_names.empty? + Pathname.new('.') + else + Pathname.new(File.join(*relpath_names)) + end + end +end + + +class Pathname # * Find * + # + # Iterates over the directory tree in a depth first manner, yielding a + # Pathname for each file under "this" directory. + # + # Returns an Enumerator if no block is given. + # + # Since it is implemented by the standard library module Find, Find.prune can + # be used to control the traversal. + # + # If +self+ is +.+, yielded pathnames begin with a filename in the + # current directory, not +./+. + # + # See Find.find + # + def find # :yield: pathname + return to_enum(__method__) unless block_given? + require 'find' + if @path == '.' + Find.find(@path) {|f| yield self.class.new(f.sub(%r{\A\./}, '')) } + else + Find.find(@path) {|f| yield self.class.new(f) } + end + end +end + + +class Pathname # * FileUtils * + # Creates a full path, including any intermediate directories that don't yet + # exist. + # + # See FileUtils.mkpath and FileUtils.mkdir_p + def mkpath + require 'fileutils' + FileUtils.mkpath(@path) + nil + end + + # Recursively deletes a directory, including all directories beneath it. + # + # See FileUtils.rm_r + def rmtree + # The name "rmtree" is borrowed from File::Path of Perl. + # File::Path provides "mkpath" and "rmtree". + require 'fileutils' + FileUtils.rm_r(@path) + nil + end +end + diff --git a/ruby/lib/ruby/2.1.0/pp.rb b/ruby/lib/ruby/2.1.0/pp.rb new file mode 100644 index 0000000..0091ddf --- /dev/null +++ b/ruby/lib/ruby/2.1.0/pp.rb @@ -0,0 +1,544 @@ +require 'prettyprint' + +module Kernel + # Returns a pretty printed object as a string. + # + # In order to use this method you must first require the PP module: + # + # require 'pp' + # + # See the PP module for more information. + def pretty_inspect + PP.pp(self, '') + end + + private + # prints arguments in pretty form. + # + # pp returns argument(s). + def pp(*objs) # :nodoc: + objs.each {|obj| + PP.pp(obj) + } + objs.size <= 1 ? objs.first : objs + end + module_function :pp # :nodoc: +end + +## +# A pretty-printer for Ruby objects. +# +# All examples assume you have loaded the PP class with: +# require 'pp' +# +## +# == What PP Does +# +# Standard output by #p returns this: +# #, @group_queue=#], []]>, @buffer=[], @newline="\n", @group_stack=[#], @buffer_width=0, @indent=0, @maxwidth=79, @output_width=2, @output=#> +# +# Pretty-printed output returns this: +# #, +# @group_queue= +# #], +# []]>, +# @group_stack= +# [#], +# @indent=0, +# @maxwidth=79, +# @newline="\n", +# @output=#, +# @output_width=2> +# +## +# == Usage +# +# pp(obj) #=> obj +# pp obj #=> obj +# pp(obj1, obj2, ...) #=> [obj1, obj2, ...] +# pp() #=> nil +# +# Output obj(s) to $> in pretty printed format. +# +# It returns obj(s). +# +## +# == Output Customization +# +# To define a customized pretty printing function for your classes, +# redefine method #pretty_print(pp) in the class. +# +# #pretty_print takes the +pp+ argument, which is an instance of the PP class. +# The method uses #text, #breakable, #nest, #group and #pp to print the +# object. +# +## +# == Pretty-Print JSON +# +# To pretty-print JSON refer to JSON#pretty_generate. +# +## +# == Author +# Tanaka Akira + +class PP < PrettyPrint + # Outputs +obj+ to +out+ in pretty printed format of + # +width+ columns in width. + # + # If +out+ is omitted, $> is assumed. + # If +width+ is omitted, 79 is assumed. + # + # PP.pp returns +out+. + def PP.pp(obj, out=$>, width=79) + q = PP.new(out, width) + q.guard_inspect_key {q.pp obj} + q.flush + #$pp = q + out << "\n" + end + + # Outputs +obj+ to +out+ like PP.pp but with no indent and + # newline. + # + # PP.singleline_pp returns +out+. + def PP.singleline_pp(obj, out=$>) + q = SingleLine.new(out) + q.guard_inspect_key {q.pp obj} + q.flush + out + end + + # :stopdoc: + def PP.mcall(obj, mod, meth, *args, &block) + mod.instance_method(meth).bind(obj).call(*args, &block) + end + # :startdoc: + + @sharing_detection = false + class << self + # Returns the sharing detection flag as a boolean value. + # It is false by default. + attr_accessor :sharing_detection + end + + module PPMethods + + # Yields to a block + # and preserves the previous set of objects being printed. + def guard_inspect_key + if Thread.current[:__recursive_key__] == nil + Thread.current[:__recursive_key__] = {}.taint + end + + if Thread.current[:__recursive_key__][:inspect] == nil + Thread.current[:__recursive_key__][:inspect] = {}.taint + end + + save = Thread.current[:__recursive_key__][:inspect] + + begin + Thread.current[:__recursive_key__][:inspect] = {}.taint + yield + ensure + Thread.current[:__recursive_key__][:inspect] = save + end + end + + # Check whether the object_id +id+ is in the current buffer of objects + # to be pretty printed. Used to break cycles in chains of objects to be + # pretty printed. + def check_inspect_key(id) + Thread.current[:__recursive_key__] && + Thread.current[:__recursive_key__][:inspect] && + Thread.current[:__recursive_key__][:inspect].include?(id) + end + + # Adds the object_id +id+ to the set of objects being pretty printed, so + # as to not repeat objects. + def push_inspect_key(id) + Thread.current[:__recursive_key__][:inspect][id] = true + end + + # Removes an object from the set of objects being pretty printed. + def pop_inspect_key(id) + Thread.current[:__recursive_key__][:inspect].delete id + end + + # Adds +obj+ to the pretty printing buffer + # using Object#pretty_print or Object#pretty_print_cycle. + # + # Object#pretty_print_cycle is used when +obj+ is already + # printed, a.k.a the object reference chain has a cycle. + def pp(obj) + id = obj.object_id + + if check_inspect_key(id) + group {obj.pretty_print_cycle self} + return + end + + begin + push_inspect_key(id) + group {obj.pretty_print self} + ensure + pop_inspect_key(id) unless PP.sharing_detection + end + end + + # A convenience method which is same as follows: + # + # group(1, '#<' + obj.class.name, '>') { ... } + def object_group(obj, &block) # :yield: + group(1, '#<' + obj.class.name, '>', &block) + end + + # A convenience method, like object_group, but also reformats the Object's + # object_id. + def object_address_group(obj, &block) + str = Kernel.instance_method(:to_s).bind(obj).call + str.chomp!('>') + group(1, str, '>', &block) + end + + # A convenience method which is same as follows: + # + # text ',' + # breakable + def comma_breakable + text ',' + breakable + end + + # Adds a separated list. + # The list is separated by comma with breakable space, by default. + # + # #seplist iterates the +list+ using +iter_method+. + # It yields each object to the block given for #seplist. + # The procedure +separator_proc+ is called between each yields. + # + # If the iteration is zero times, +separator_proc+ is not called at all. + # + # If +separator_proc+ is nil or not given, + # +lambda { comma_breakable }+ is used. + # If +iter_method+ is not given, :each is used. + # + # For example, following 3 code fragments has similar effect. + # + # q.seplist([1,2,3]) {|v| xxx v } + # + # q.seplist([1,2,3], lambda { q.comma_breakable }, :each) {|v| xxx v } + # + # xxx 1 + # q.comma_breakable + # xxx 2 + # q.comma_breakable + # xxx 3 + def seplist(list, sep=nil, iter_method=:each) # :yield: element + sep ||= lambda { comma_breakable } + first = true + list.__send__(iter_method) {|*v| + if first + first = false + else + sep.call + end + yield(*v) + } + end + + # A present standard failsafe for pretty printing any given Object + def pp_object(obj) + object_address_group(obj) { + seplist(obj.pretty_print_instance_variables, lambda { text ',' }) {|v| + breakable + v = v.to_s if Symbol === v + text v + text '=' + group(1) { + breakable '' + pp(obj.instance_eval(v)) + } + } + } + end + + # A pretty print for a Hash + def pp_hash(obj) + group(1, '{', '}') { + seplist(obj, nil, :each_pair) {|k, v| + group { + pp k + text '=>' + group(1) { + breakable '' + pp v + } + } + } + } + end + end + + include PPMethods + + class SingleLine < PrettyPrint::SingleLine # :nodoc: + include PPMethods + end + + module ObjectMixin # :nodoc: + # 1. specific pretty_print + # 2. specific inspect + # 3. generic pretty_print + + # A default pretty printing method for general objects. + # It calls #pretty_print_instance_variables to list instance variables. + # + # If +self+ has a customized (redefined) #inspect method, + # the result of self.inspect is used but it obviously has no + # line break hints. + # + # This module provides predefined #pretty_print methods for some of + # the most commonly used built-in classes for convenience. + def pretty_print(q) + method_method = Object.instance_method(:method).bind(self) + begin + inspect_method = method_method.call(:inspect) + rescue NameError + end + if inspect_method && /\(Kernel\)#/ !~ inspect_method.inspect + q.text self.inspect + elsif !inspect_method && self.respond_to?(:inspect) + q.text self.inspect + else + q.pp_object(self) + end + end + + # A default pretty printing method for general objects that are + # detected as part of a cycle. + def pretty_print_cycle(q) + q.object_address_group(self) { + q.breakable + q.text '...' + } + end + + # Returns a sorted array of instance variable names. + # + # This method should return an array of names of instance variables as symbols or strings as: + # +[:@a, :@b]+. + def pretty_print_instance_variables + instance_variables.sort + end + + # Is #inspect implementation using #pretty_print. + # If you implement #pretty_print, it can be used as follows. + # + # alias inspect pretty_print_inspect + # + # However, doing this requires that every class that #inspect is called on + # implement #pretty_print, or a RuntimeError will be raised. + def pretty_print_inspect + if /\(PP::ObjectMixin\)#/ =~ Object.instance_method(:method).bind(self).call(:pretty_print).inspect + raise "pretty_print is not overridden for #{self.class}" + end + PP.singleline_pp(self, '') + end + end +end + +class Array # :nodoc: + def pretty_print(q) # :nodoc: + q.group(1, '[', ']') { + q.seplist(self) {|v| + q.pp v + } + } + end + + def pretty_print_cycle(q) # :nodoc: + q.text(empty? ? '[]' : '[...]') + end +end + +class Hash # :nodoc: + def pretty_print(q) # :nodoc: + q.pp_hash self + end + + def pretty_print_cycle(q) # :nodoc: + q.text(empty? ? '{}' : '{...}') + end +end + +class << ENV # :nodoc: + def pretty_print(q) # :nodoc: + h = {} + ENV.keys.sort.each {|k| + h[k] = ENV[k] + } + q.pp_hash h + end +end + +class Struct # :nodoc: + def pretty_print(q) # :nodoc: + q.group(1, sprintf("#') { + q.seplist(PP.mcall(self, Struct, :members), lambda { q.text "," }) {|member| + q.breakable + q.text member.to_s + q.text '=' + q.group(1) { + q.breakable '' + q.pp self[member] + } + } + } + end + + def pretty_print_cycle(q) # :nodoc: + q.text sprintf("#", PP.mcall(self, Kernel, :class).name) + end +end + +class Range # :nodoc: + def pretty_print(q) # :nodoc: + q.pp self.begin + q.breakable '' + q.text(self.exclude_end? ? '...' : '..') + q.breakable '' + q.pp self.end + end +end + +class File < IO # :nodoc: + class Stat # :nodoc: + def pretty_print(q) # :nodoc: + require 'etc.so' + q.object_group(self) { + q.breakable + q.text sprintf("dev=0x%x", self.dev); q.comma_breakable + q.text "ino="; q.pp self.ino; q.comma_breakable + q.group { + m = self.mode + q.text sprintf("mode=0%o", m) + q.breakable + q.text sprintf("(%s %c%c%c%c%c%c%c%c%c)", + self.ftype, + (m & 0400 == 0 ? ?- : ?r), + (m & 0200 == 0 ? ?- : ?w), + (m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) : + (m & 04000 == 0 ? ?x : ?s)), + (m & 0040 == 0 ? ?- : ?r), + (m & 0020 == 0 ? ?- : ?w), + (m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) : + (m & 02000 == 0 ? ?x : ?s)), + (m & 0004 == 0 ? ?- : ?r), + (m & 0002 == 0 ? ?- : ?w), + (m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) : + (m & 01000 == 0 ? ?x : ?t))) + } + q.comma_breakable + q.text "nlink="; q.pp self.nlink; q.comma_breakable + q.group { + q.text "uid="; q.pp self.uid + begin + pw = Etc.getpwuid(self.uid) + rescue ArgumentError + end + if pw + q.breakable; q.text "(#{pw.name})" + end + } + q.comma_breakable + q.group { + q.text "gid="; q.pp self.gid + begin + gr = Etc.getgrgid(self.gid) + rescue ArgumentError + end + if gr + q.breakable; q.text "(#{gr.name})" + end + } + q.comma_breakable + q.group { + q.text sprintf("rdev=0x%x", self.rdev) + q.breakable + q.text sprintf('(%d, %d)', self.rdev_major, self.rdev_minor) + } + q.comma_breakable + q.text "size="; q.pp self.size; q.comma_breakable + q.text "blksize="; q.pp self.blksize; q.comma_breakable + q.text "blocks="; q.pp self.blocks; q.comma_breakable + q.group { + t = self.atime + q.text "atime="; q.pp t + q.breakable; q.text "(#{t.tv_sec})" + } + q.comma_breakable + q.group { + t = self.mtime + q.text "mtime="; q.pp t + q.breakable; q.text "(#{t.tv_sec})" + } + q.comma_breakable + q.group { + t = self.ctime + q.text "ctime="; q.pp t + q.breakable; q.text "(#{t.tv_sec})" + } + } + end + end +end + +class MatchData # :nodoc: + def pretty_print(q) # :nodoc: + nc = [] + self.regexp.named_captures.each {|name, indexes| + indexes.each {|i| nc[i] = name } + } + q.object_group(self) { + q.breakable + q.seplist(0...self.size, lambda { q.breakable }) {|i| + if i == 0 + q.pp self[i] + else + if nc[i] + q.text nc[i] + else + q.pp i + end + q.text ':' + q.pp self[i] + end + } + } + end +end + +class Object < BasicObject # :nodoc: + include PP::ObjectMixin +end + +[Numeric, Symbol, FalseClass, TrueClass, NilClass, Module].each {|c| + c.class_eval { + def pretty_print_cycle(q) + q.text inspect + end + } +} + +[Numeric, FalseClass, TrueClass, Module].each {|c| + c.class_eval { + def pretty_print(q) + q.text inspect + end + } +} diff --git a/ruby/lib/ruby/2.1.0/prettyprint.rb b/ruby/lib/ruby/2.1.0/prettyprint.rb new file mode 100644 index 0000000..5b1da33 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/prettyprint.rb @@ -0,0 +1,576 @@ +# This class implements a pretty printing algorithm. It finds line breaks and +# nice indentations for grouped structure. +# +# By default, the class assumes that primitive elements are strings and each +# byte in the strings have single column in width. But it can be used for +# other situations by giving suitable arguments for some methods: +# * newline object and space generation block for PrettyPrint.new +# * optional width argument for PrettyPrint#text +# * PrettyPrint#breakable +# +# There are several candidate uses: +# * text formatting using proportional fonts +# * multibyte characters which has columns different to number of bytes +# * non-string formatting +# +# == Bugs +# * Box based formatting? +# * Other (better) model/algorithm? +# +# Report any bugs at http://bugs.ruby-lang.org +# +# == References +# Christian Lindig, Strictly Pretty, March 2000, +# http://www.st.cs.uni-sb.de/~lindig/papers/#pretty +# +# Philip Wadler, A prettier printer, March 1998, +# http://homepages.inf.ed.ac.uk/wadler/topics/language-design.html#prettier +# +# == Author +# Tanaka Akira +# +class PrettyPrint + + # This is a convenience method which is same as follows: + # + # begin + # q = PrettyPrint.new(output, maxwidth, newline, &genspace) + # ... + # q.flush + # output + # end + # + def PrettyPrint.format(output='', maxwidth=79, newline="\n", genspace=lambda {|n| ' ' * n}) + q = PrettyPrint.new(output, maxwidth, newline, &genspace) + yield q + q.flush + output + end + + # This is similar to PrettyPrint::format but the result has no breaks. + # + # +maxwidth+, +newline+ and +genspace+ are ignored. + # + # The invocation of +breakable+ in the block doesn't break a line and is + # treated as just an invocation of +text+. + # + def PrettyPrint.singleline_format(output='', maxwidth=nil, newline=nil, genspace=nil) + q = SingleLine.new(output) + yield q + output + end + + # Creates a buffer for pretty printing. + # + # +output+ is an output target. If it is not specified, '' is assumed. It + # should have a << method which accepts the first argument +obj+ of + # PrettyPrint#text, the first argument +sep+ of PrettyPrint#breakable, the + # first argument +newline+ of PrettyPrint.new, and the result of a given + # block for PrettyPrint.new. + # + # +maxwidth+ specifies maximum line length. If it is not specified, 79 is + # assumed. However actual outputs may overflow +maxwidth+ if long + # non-breakable texts are provided. + # + # +newline+ is used for line breaks. "\n" is used if it is not specified. + # + # The block is used to generate spaces. {|width| ' ' * width} is used if it + # is not given. + # + def initialize(output='', maxwidth=79, newline="\n", &genspace) + @output = output + @maxwidth = maxwidth + @newline = newline + @genspace = genspace || lambda {|n| ' ' * n} + + @output_width = 0 + @buffer_width = 0 + @buffer = [] + + root_group = Group.new(0) + @group_stack = [root_group] + @group_queue = GroupQueue.new(root_group) + @indent = 0 + end + + # The output object. + # + # This defaults to '', and should accept the << method + attr_reader :output + + # The maximum width of a line, before it is separated in to a newline + # + # This defaults to 79, and should be a Fixnum + attr_reader :maxwidth + + # The value that is appended to +output+ to add a new line. + # + # This defaults to "\n", and should be String + attr_reader :newline + + # A lambda or Proc, that takes one argument, of a Fixnum, and returns + # the corresponding number of spaces. + # + # By default this is: + # lambda {|n| ' ' * n} + attr_reader :genspace + + # The number of spaces to be indented + attr_reader :indent + + # The PrettyPrint::GroupQueue of groups in stack to be pretty printed + attr_reader :group_queue + + # Returns the group most recently added to the stack. + # + # Contrived example: + # out = "" + # => "" + # q = PrettyPrint.new(out) + # => #, @output_width=0, @buffer_width=0, @buffer=[], @group_stack=[#], @group_queue=#]]>, @indent=0> + # q.group { + # q.text q.current_group.inspect + # q.text q.newline + # q.group(q.current_group.depth + 1) { + # q.text q.current_group.inspect + # q.text q.newline + # q.group(q.current_group.depth + 1) { + # q.text q.current_group.inspect + # q.text q.newline + # q.group(q.current_group.depth + 1) { + # q.text q.current_group.inspect + # q.text q.newline + # } + # } + # } + # } + # => 284 + # puts out + # # + # # + # # + # # + def current_group + @group_stack.last + end + + # first? is a predicate to test the call is a first call to first? with + # current group. + # + # It is useful to format comma separated values as: + # + # q.group(1, '[', ']') { + # xxx.each {|yyy| + # unless q.first? + # q.text ',' + # q.breakable + # end + # ... pretty printing yyy ... + # } + # } + # + # first? is obsoleted in 1.8.2. + # + def first? + warn "PrettyPrint#first? is obsoleted at 1.8.2." + current_group.first? + end + + # Breaks the buffer into lines that are shorter than #maxwidth + def break_outmost_groups + while @maxwidth < @output_width + @buffer_width + return unless group = @group_queue.deq + until group.breakables.empty? + data = @buffer.shift + @output_width = data.output(@output, @output_width) + @buffer_width -= data.width + end + while !@buffer.empty? && Text === @buffer.first + text = @buffer.shift + @output_width = text.output(@output, @output_width) + @buffer_width -= text.width + end + end + end + + # This adds +obj+ as a text of +width+ columns in width. + # + # If +width+ is not specified, obj.length is used. + # + def text(obj, width=obj.length) + if @buffer.empty? + @output << obj + @output_width += width + else + text = @buffer.last + unless Text === text + text = Text.new + @buffer << text + end + text.add(obj, width) + @buffer_width += width + break_outmost_groups + end + end + + # This is similar to #breakable except + # the decision to break or not is determined individually. + # + # Two #fill_breakable under a group may cause 4 results: + # (break,break), (break,non-break), (non-break,break), (non-break,non-break). + # This is different to #breakable because two #breakable under a group + # may cause 2 results: + # (break,break), (non-break,non-break). + # + # The text +sep+ is inserted if a line is not broken at this point. + # + # If +sep+ is not specified, " " is used. + # + # If +width+ is not specified, +sep.length+ is used. You will have to + # specify this when +sep+ is a multibyte character, for example. + # + def fill_breakable(sep=' ', width=sep.length) + group { breakable sep, width } + end + + # This says "you can break a line here if necessary", and a +width+\-column + # text +sep+ is inserted if a line is not broken at the point. + # + # If +sep+ is not specified, " " is used. + # + # If +width+ is not specified, +sep.length+ is used. You will have to + # specify this when +sep+ is a multibyte character, for example. + # + def breakable(sep=' ', width=sep.length) + group = @group_stack.last + if group.break? + flush + @output << @newline + @output << @genspace.call(@indent) + @output_width = @indent + @buffer_width = 0 + else + @buffer << Breakable.new(sep, width, self) + @buffer_width += width + break_outmost_groups + end + end + + # Groups line break hints added in the block. The line break hints are all + # to be used or not. + # + # If +indent+ is specified, the method call is regarded as nested by + # nest(indent) { ... }. + # + # If +open_obj+ is specified, text open_obj, open_width is called + # before grouping. If +close_obj+ is specified, text close_obj, + # close_width is called after grouping. + # + def group(indent=0, open_obj='', close_obj='', open_width=open_obj.length, close_width=close_obj.length) + text open_obj, open_width + group_sub { + nest(indent) { + yield + } + } + text close_obj, close_width + end + + # Takes a block and queues a new group that is indented 1 level further. + def group_sub + group = Group.new(@group_stack.last.depth + 1) + @group_stack.push group + @group_queue.enq group + begin + yield + ensure + @group_stack.pop + if group.breakables.empty? + @group_queue.delete group + end + end + end + + # Increases left margin after newline with +indent+ for line breaks added in + # the block. + # + def nest(indent) + @indent += indent + begin + yield + ensure + @indent -= indent + end + end + + # outputs buffered data. + # + def flush + @buffer.each {|data| + @output_width = data.output(@output, @output_width) + } + @buffer.clear + @buffer_width = 0 + end + + # The Text class is the means by which to collect strings from objects. + # + # This class is intended for internal use of the PrettyPrint buffers. + class Text # :nodoc: + + # Creates a new text object. + # + # This constructor takes no arguments. + # + # The workflow is to append a PrettyPrint::Text object to the buffer, and + # being able to call the buffer.last() to reference it. + # + # As there are objects, use PrettyPrint::Text#add to include the objects + # and the width to utilized by the String version of this object. + def initialize + @objs = [] + @width = 0 + end + + # The total width of the objects included in this Text object. + attr_reader :width + + # Render the String text of the objects that have been added to this Text object. + # + # Output the text to +out+, and increment the width to +output_width+ + def output(out, output_width) + @objs.each {|obj| out << obj} + output_width + @width + end + + # Include +obj+ in the objects to be pretty printed, and increment + # this Text object's total width by +width+ + def add(obj, width) + @objs << obj + @width += width + end + end + + # The Breakable class is used for breaking up object information + # + # This class is intended for internal use of the PrettyPrint buffers. + class Breakable # :nodoc: + + # Create a new Breakable object. + # + # Arguments: + # * +sep+ String of the separator + # * +width+ Fixnum width of the +sep+ + # * +q+ parent PrettyPrint object, to base from + def initialize(sep, width, q) + @obj = sep + @width = width + @pp = q + @indent = q.indent + @group = q.current_group + @group.breakables.push self + end + + # Holds the separator String + # + # The +sep+ argument from ::new + attr_reader :obj + + # The width of +obj+ / +sep+ + attr_reader :width + + # The number of spaces to indent. + # + # This is inferred from +q+ within PrettyPrint, passed in ::new + attr_reader :indent + + # Render the String text of the objects that have been added to this + # Breakable object. + # + # Output the text to +out+, and increment the width to +output_width+ + def output(out, output_width) + @group.breakables.shift + if @group.break? + out << @pp.newline + out << @pp.genspace.call(@indent) + @indent + else + @pp.group_queue.delete @group if @group.breakables.empty? + out << @obj + output_width + @width + end + end + end + + # The Group class is used for making indentation easier. + # + # While this class does neither the breaking into newlines nor indentation, + # it is used in a stack (as well as a queue) within PrettyPrint, to group + # objects. + # + # For information on using groups, see PrettyPrint#group + # + # This class is intended for internal use of the PrettyPrint buffers. + class Group # :nodoc: + # Create a Group object + # + # Arguments: + # * +depth+ - this group's relation to previous groups + def initialize(depth) + @depth = depth + @breakables = [] + @break = false + end + + # This group's relation to previous groups + attr_reader :depth + + # Array to hold the Breakable objects for this Group + attr_reader :breakables + + # Makes a break for this Group, and returns true + def break + @break = true + end + + # Boolean of whether this Group has made a break + def break? + @break + end + + # Boolean of whether this Group has been queried for being first + # + # This is used as a predicate, and ought to be called first. + def first? + if defined? @first + false + else + @first = false + true + end + end + end + + # The GroupQueue class is used for managing the queue of Group to be pretty + # printed. + # + # This queue groups the Group objects, based on their depth. + # + # This class is intended for internal use of the PrettyPrint buffers. + class GroupQueue # :nodoc: + # Create a GroupQueue object + # + # Arguments: + # * +groups+ - one or more PrettyPrint::Group objects + def initialize(*groups) + @queue = [] + groups.each {|g| enq g} + end + + # Enqueue +group+ + # + # This does not strictly append the group to the end of the queue, + # but instead adds it in line, base on the +group.depth+ + def enq(group) + depth = group.depth + @queue << [] until depth < @queue.length + @queue[depth] << group + end + + # Returns the outer group of the queue + def deq + @queue.each {|gs| + (gs.length-1).downto(0) {|i| + unless gs[i].breakables.empty? + group = gs.slice!(i, 1).first + group.break + return group + end + } + gs.each {|group| group.break} + gs.clear + } + return nil + end + + # Remote +group+ from this queue + def delete(group) + @queue[group.depth].delete(group) + end + end + + # PrettyPrint::SingleLine is used by PrettyPrint.singleline_format + # + # It is passed to be similar to a PrettyPrint object itself, by responding to: + # * #text + # * #breakable + # * #nest + # * #group + # * #flush + # * #first? + # + # but instead, the output has no line breaks + # + class SingleLine + # Create a PrettyPrint::SingleLine object + # + # Arguments: + # * +output+ - String (or similar) to store rendered text. Needs to respond to '<<' + # * +maxwidth+ - Argument position expected to be here for compatibility. + # This argument is a noop. + # * +newline+ - Argument position expected to be here for compatibility. + # This argument is a noop. + def initialize(output, maxwidth=nil, newline=nil) + @output = output + @first = [true] + end + + # Add +obj+ to the text to be output. + # + # +width+ argument is here for compatibility. It is a noop argument. + def text(obj, width=nil) + @output << obj + end + + # Appends +sep+ to the text to be output. By default +sep+ is ' ' + # + # +width+ argument is here for compatibility. It is a noop argument. + def breakable(sep=' ', width=nil) + @output << sep + end + + # Takes +indent+ arg, but does nothing with it. + # + # Yields to a block. + def nest(indent) # :nodoc: + yield + end + + # Opens a block for grouping objects to be pretty printed. + # + # Arguments: + # * +indent+ - noop argument. Present for compatibility. + # * +open_obj+ - text appended before the &blok. Default is '' + # * +close_obj+ - text appended after the &blok. Default is '' + # * +open_width+ - noop argument. Present for compatibility. + # * +close_width+ - noop argument. Present for compatibility. + def group(indent=nil, open_obj='', close_obj='', open_width=nil, close_width=nil) + @first.push true + @output << open_obj + yield + @output << close_obj + @first.pop + end + + # Method present for compatibility, but is a noop + def flush # :nodoc: + end + + # This is used as a predicate, and ought to be called first. + def first? + result = @first[-1] + @first[-1] = false + result + end + end +end diff --git a/ruby/lib/ruby/2.1.0/prime.rb b/ruby/lib/ruby/2.1.0/prime.rb new file mode 100644 index 0000000..f359c12 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/prime.rb @@ -0,0 +1,490 @@ +# +# = prime.rb +# +# Prime numbers and factorization library. +# +# Copyright:: +# Copyright (c) 1998-2008 Keiju ISHITSUKA(SHL Japan Inc.) +# Copyright (c) 2008 Yuki Sonoda (Yugui) +# +# Documentation:: +# Yuki Sonoda +# + +require "singleton" +require "forwardable" + +class Integer + # Re-composes a prime factorization and returns the product. + # + # See Prime#int_from_prime_division for more details. + def Integer.from_prime_division(pd) + Prime.int_from_prime_division(pd) + end + + # Returns the factorization of +self+. + # + # See Prime#prime_division for more details. + def prime_division(generator = Prime::Generator23.new) + Prime.prime_division(self, generator) + end + + # Returns true if +self+ is a prime number, false for a composite. + def prime? + Prime.prime?(self) + end + + # Iterates the given block over all prime numbers. + # + # See +Prime+#each for more details. + def Integer.each_prime(ubound, &block) # :yields: prime + Prime.each(ubound, &block) + end +end + +# +# The set of all prime numbers. +# +# == Example +# +# Prime.each(100) do |prime| +# p prime #=> 2, 3, 5, 7, 11, ...., 97 +# end +# +# Prime is Enumerable: +# +# Prime.first 5 # => [2, 3, 5, 7, 11] +# +# == Retrieving the instance +# +# +Prime+.new is obsolete. Now +Prime+ has the default instance and you can +# access it as +Prime+.instance. +# +# For convenience, each instance method of +Prime+.instance can be accessed +# as a class method of +Prime+. +# +# e.g. +# Prime.instance.prime?(2) #=> true +# Prime.prime?(2) #=> true +# +# == Generators +# +# A "generator" provides an implementation of enumerating pseudo-prime +# numbers and it remembers the position of enumeration and upper bound. +# Furthermore, it is an external iterator of prime enumeration which is +# compatible with an Enumerator. +# +# +Prime+::+PseudoPrimeGenerator+ is the base class for generators. +# There are few implementations of generator. +# +# [+Prime+::+EratosthenesGenerator+] +# Uses eratosthenes' sieve. +# [+Prime+::+TrialDivisionGenerator+] +# Uses the trial division method. +# [+Prime+::+Generator23+] +# Generates all positive integers which are not divisible by either 2 or 3. +# This sequence is very bad as a pseudo-prime sequence. But this +# is faster and uses much less memory than the other generators. So, +# it is suitable for factorizing an integer which is not large but +# has many prime factors. e.g. for Prime#prime? . + +class Prime + include Enumerable + @the_instance = Prime.new + + # obsolete. Use +Prime+::+instance+ or class methods of +Prime+. + def initialize + @generator = EratosthenesGenerator.new + extend OldCompatibility + warn "Prime::new is obsolete. use Prime::instance or class methods of Prime." + end + + class << self + extend Forwardable + include Enumerable + # Returns the default instance of Prime. + def instance; @the_instance end + + def method_added(method) # :nodoc: + (class<< self;self;end).def_delegator :instance, method + end + end + + # Iterates the given block over all prime numbers. + # + # == Parameters + # + # +ubound+:: + # Optional. An arbitrary positive number. + # The upper bound of enumeration. The method enumerates + # prime numbers infinitely if +ubound+ is nil. + # +generator+:: + # Optional. An implementation of pseudo-prime generator. + # + # == Return value + # + # An evaluated value of the given block at the last time. + # Or an enumerator which is compatible to an +Enumerator+ + # if no block given. + # + # == Description + # + # Calls +block+ once for each prime number, passing the prime as + # a parameter. + # + # +ubound+:: + # Upper bound of prime numbers. The iterator stops after it + # yields all prime numbers p <= +ubound+. + # + # == Note + # + # +Prime+.+new+ returns an object extended by +Prime+::+OldCompatibility+ + # in order to be compatible with Ruby 1.8, and +Prime+#each is overwritten + # by +Prime+::+OldCompatibility+#+each+. + # + # +Prime+.+new+ is now obsolete. Use +Prime+.+instance+.+each+ or simply + # +Prime+.+each+. + def each(ubound = nil, generator = EratosthenesGenerator.new, &block) + generator.upper_bound = ubound + generator.each(&block) + end + + + # Returns true if +value+ is prime, false for a composite. + # + # == Parameters + # + # +value+:: an arbitrary integer to be checked. + # +generator+:: optional. A pseudo-prime generator. + def prime?(value, generator = Prime::Generator23.new) + value = -value if value < 0 + return false if value < 2 + for num in generator + q,r = value.divmod num + return true if q < num + return false if r == 0 + end + end + + # Re-composes a prime factorization and returns the product. + # + # == Parameters + # +pd+:: Array of pairs of integers. The each internal + # pair consists of a prime number -- a prime factor -- + # and a natural number -- an exponent. + # + # == Example + # For [[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]], it returns: + # + # p_1**e_1 * p_2**e_2 * .... * p_n**e_n. + # + # Prime.int_from_prime_division([[2,2], [3,1]]) #=> 12 + def int_from_prime_division(pd) + pd.inject(1){|value, (prime, index)| + value *= prime**index + } + end + + # Returns the factorization of +value+. + # + # == Parameters + # +value+:: An arbitrary integer. + # +generator+:: Optional. A pseudo-prime generator. + # +generator+.succ must return the next + # pseudo-prime number in the ascending + # order. It must generate all prime numbers, + # but may also generate non prime numbers too. + # + # === Exceptions + # +ZeroDivisionError+:: when +value+ is zero. + # + # == Example + # For an arbitrary integer: + # + # n = p_1**e_1 * p_2**e_2 * .... * p_n**e_n, + # + # prime_division(n) returns: + # + # [[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]]. + # + # Prime.prime_division(12) #=> [[2,2], [3,1]] + # + def prime_division(value, generator = Prime::Generator23.new) + raise ZeroDivisionError if value == 0 + if value < 0 + value = -value + pv = [[-1, 1]] + else + pv = [] + end + for prime in generator + count = 0 + while (value1, mod = value.divmod(prime) + mod) == 0 + value = value1 + count += 1 + end + if count != 0 + pv.push [prime, count] + end + break if value1 <= prime + end + if value > 1 + pv.push [value, 1] + end + return pv + end + + # An abstract class for enumerating pseudo-prime numbers. + # + # Concrete subclasses should override succ, next, rewind. + class PseudoPrimeGenerator + include Enumerable + + def initialize(ubound = nil) + @ubound = ubound + end + + def upper_bound=(ubound) + @ubound = ubound + end + def upper_bound + @ubound + end + + # returns the next pseudo-prime number, and move the internal + # position forward. + # + # +PseudoPrimeGenerator+#succ raises +NotImplementedError+. + def succ + raise NotImplementedError, "need to define `succ'" + end + + # alias of +succ+. + def next + raise NotImplementedError, "need to define `next'" + end + + # Rewinds the internal position for enumeration. + # + # See +Enumerator+#rewind. + def rewind + raise NotImplementedError, "need to define `rewind'" + end + + # Iterates the given block for each prime number. + def each(&block) + return self.dup unless block + if @ubound + last_value = nil + loop do + prime = succ + break last_value if prime > @ubound + last_value = block.call(prime) + end + else + loop do + block.call(succ) + end + end + end + + # see +Enumerator+#with_index. + alias with_index each_with_index + + # see +Enumerator+#with_object. + def with_object(obj) + return enum_for(:with_object) unless block_given? + each do |prime| + yield prime, obj + end + end + end + + # An implementation of +PseudoPrimeGenerator+. + # + # Uses +EratosthenesSieve+. + class EratosthenesGenerator < PseudoPrimeGenerator + def initialize + @last_prime_index = -1 + super + end + + def succ + @last_prime_index += 1 + EratosthenesSieve.instance.get_nth_prime(@last_prime_index) + end + def rewind + initialize + end + alias next succ + end + + # An implementation of +PseudoPrimeGenerator+ which uses + # a prime table generated by trial division. + class TrialDivisionGenerator= @primes.length + # Only check for prime factors up to the square root of the potential primes, + # but without the performance hit of an actual square root calculation. + if @next_to_check + 4 > @ulticheck_next_squared + @ulticheck_index += 1 + @ulticheck_next_squared = @primes.at(@ulticheck_index + 1) ** 2 + end + # Only check numbers congruent to one and five, modulo six. All others + + # are divisible by two or three. This also allows us to skip checking against + # two and three. + @primes.push @next_to_check if @primes[2..@ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil? + @next_to_check += 4 + @primes.push @next_to_check if @primes[2..@ulticheck_index].find {|prime| @next_to_check % prime == 0 }.nil? + @next_to_check += 2 + end + return @primes[index] + end + end + + # Internal use. An implementation of eratosthenes' sieve + class EratosthenesSieve + include Singleton + + def initialize + @primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101] + # @max_checked must be an even number + @max_checked = @primes.last + 1 + end + + def get_nth_prime(n) + compute_primes while @primes.size <= n + @primes[n] + end + + private + def compute_primes + # max_segment_size must be an even number + max_segment_size = 1e6.to_i + max_cached_prime = @primes.last + # do not double count primes if #compute_primes is interrupted + # by Timeout.timeout + @max_checked = max_cached_prime + 1 if max_cached_prime > @max_checked + + segment_min = @max_checked + segment_max = [segment_min + max_segment_size, max_cached_prime * 2].min + root = Integer(Math.sqrt(segment_max).floor) + + sieving_primes = @primes[1 .. -1].take_while { |prime| prime <= root } + offsets = Array.new(sieving_primes.size) do |i| + (-(segment_min + 1 + sieving_primes[i]) / 2) % sieving_primes[i] + end + + segment = ((segment_min + 1) .. segment_max).step(2).to_a + sieving_primes.each_with_index do |prime, index| + composite_index = offsets[index] + while composite_index < segment.size do + segment[composite_index] = nil + composite_index += prime + end + end + + segment.each do |prime| + @primes.push prime unless prime.nil? + end + @max_checked = segment_max + end + end + + # Provides a +Prime+ object with compatibility to Ruby 1.8 when instantiated via +Prime+.+new+. + module OldCompatibility + # Returns the next prime number and forwards internal pointer. + def succ + @generator.succ + end + alias next succ + + # Overwrites Prime#each. + # + # Iterates the given block over all prime numbers. Note that enumeration + # starts from the current position of internal pointer, not rewound. + def each(&block) + return @generator.dup unless block_given? + loop do + yield succ + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/profile.rb b/ruby/lib/ruby/2.1.0/profile.rb new file mode 100644 index 0000000..2aeecce --- /dev/null +++ b/ruby/lib/ruby/2.1.0/profile.rb @@ -0,0 +1,10 @@ +require 'profiler' + +RubyVM::InstructionSequence.compile_option = { + :trace_instruction => true, + :specialized_instruction => false +} +END { + Profiler__::print_profile(STDERR) +} +Profiler__::start_profile diff --git a/ruby/lib/ruby/2.1.0/profiler.rb b/ruby/lib/ruby/2.1.0/profiler.rb new file mode 100644 index 0000000..e53951c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/profiler.rb @@ -0,0 +1,148 @@ +# Profile provides a way to Profile your Ruby application. +# +# Profiling your program is a way of determining which methods are called and +# how long each method takes to complete. This way you can detect which +# methods are possible bottlenecks. +# +# Profiling your program will slow down your execution time considerably, +# so activate it only when you need it. Don't confuse benchmarking with +# profiling. +# +# There are two ways to activate Profiling: +# +# == Command line +# +# Run your Ruby script with -rprofile: +# +# ruby -rprofile example.rb +# +# If you're profiling an executable in your $PATH you can use +# ruby -S: +# +# ruby -rprofile -S some_executable +# +# == From code +# +# Just require 'profile': +# +# require 'profile' +# +# def slow_method +# 5000.times do +# 9999999999999999*999999999 +# end +# end +# +# def fast_method +# 5000.times do +# 9999999999999999+999999999 +# end +# end +# +# slow_method +# fast_method +# +# The output in both cases is a report when the execution is over: +# +# ruby -rprofile example.rb +# +# % cumulative self self total +# time seconds seconds calls ms/call ms/call name +# 68.42 0.13 0.13 2 65.00 95.00 Integer#times +# 15.79 0.16 0.03 5000 0.01 0.01 Fixnum#* +# 15.79 0.19 0.03 5000 0.01 0.01 Fixnum#+ +# 0.00 0.19 0.00 2 0.00 0.00 IO#set_encoding +# 0.00 0.19 0.00 1 0.00 100.00 Object#slow_method +# 0.00 0.19 0.00 2 0.00 0.00 Module#method_added +# 0.00 0.19 0.00 1 0.00 90.00 Object#fast_method +# 0.00 0.19 0.00 1 0.00 190.00 #toplevel + +module Profiler__ + class Wrapper < Struct.new(:defined_class, :method_id, :hash) # :nodoc: + private :defined_class=, :method_id=, :hash= + + def initialize(klass, mid) + super(klass, mid, nil) + self.hash = Struct.instance_method(:hash).bind(self).call + end + + def to_s + "#{defined_class.inspect}#".sub(/\A\##\z/, '\1.') << method_id.to_s + end + alias inspect to_s + end + + # internal values + @@start = nil # the start time that profiling began + @@stacks = nil # the map of stacks keyed by thread + @@maps = nil # the map of call data keyed by thread, class and id. Call data contains the call count, total time, + PROFILE_CALL_PROC = TracePoint.new(*%i[call c_call b_call]) {|tp| # :nodoc: + now = Process.times[0] + stack = (@@stacks[Thread.current] ||= []) + stack.push [now, 0.0] + } + PROFILE_RETURN_PROC = TracePoint.new(*%i[return c_return b_return]) {|tp| # :nodoc: + now = Process.times[0] + key = Wrapper.new(tp.defined_class, tp.method_id) + stack = (@@stacks[Thread.current] ||= []) + if tick = stack.pop + threadmap = (@@maps[Thread.current] ||= {}) + data = (threadmap[key] ||= [0, 0.0, 0.0, key]) + data[0] += 1 + cost = now - tick[0] + data[1] += cost + data[2] += cost - tick[1] + stack[-1][1] += cost if stack[-1] + end + } +module_function + # Starts the profiler. + # + # See Profiler__ for more information. + def start_profile + @@start = Process.times[0] + @@stacks = {} + @@maps = {} + PROFILE_CALL_PROC.enable + PROFILE_RETURN_PROC.enable + end + # Stops the profiler. + # + # See Profiler__ for more information. + def stop_profile + PROFILE_CALL_PROC.disable + PROFILE_RETURN_PROC.disable + end + # Outputs the results from the profiler. + # + # See Profiler__ for more information. + def print_profile(f) + stop_profile + total = Process.times[0] - @@start + if total == 0 then total = 0.01 end + totals = {} + @@maps.values.each do |threadmap| + threadmap.each do |key, data| + total_data = (totals[key] ||= [0, 0.0, 0.0, key]) + total_data[0] += data[0] + total_data[1] += data[1] + total_data[2] += data[2] + end + end + + # Maybe we should show a per thread output and a totals view? + + data = totals.values + data = data.sort_by{|x| -x[2]} + sum = 0 + f.printf " %% cumulative self self total\n" + f.printf " time seconds seconds calls ms/call ms/call name\n" + for d in data + sum += d[2] + f.printf "%6.2f %8.2f %8.2f %8d ", d[2]/total*100, sum, d[2], d[0] + f.printf "%8.2f %8.2f %s\n", d[2]*1000/d[0], d[1]*1000/d[0], d[3] + end + f.printf "%6.2f %8.2f %8.2f %8d ", 0.0, total, 0.0, 1 # ??? + f.printf "%8.2f %8.2f %s\n", 0.0, total*1000, "#toplevel" # ??? + end +end diff --git a/ruby/lib/ruby/2.1.0/pstore.rb b/ruby/lib/ruby/2.1.0/pstore.rb new file mode 100644 index 0000000..a2813a8 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/pstore.rb @@ -0,0 +1,506 @@ +# = PStore -- Transactional File Storage for Ruby Objects +# +# pstore.rb - +# originally by matz +# documentation by Kev Jackson and James Edward Gray II +# improved by Hongli Lai +# +# See PStore for documentation. + +require "digest/md5" + +# +# PStore implements a file based persistence mechanism based on a Hash. User +# code can store hierarchies of Ruby objects (values) into the data store file +# by name (keys). An object hierarchy may be just a single object. User code +# may later read values back from the data store or even update data, as needed. +# +# The transactional behavior ensures that any changes succeed or fail together. +# This can be used to ensure that the data store is not left in a transitory +# state, where some values were updated but others were not. +# +# Behind the scenes, Ruby objects are stored to the data store file with +# Marshal. That carries the usual limitations. Proc objects cannot be +# marshalled, for example. +# +# == Usage example: +# +# require "pstore" +# +# # a mock wiki object... +# class WikiPage +# def initialize( page_name, author, contents ) +# @page_name = page_name +# @revisions = Array.new +# +# add_revision(author, contents) +# end +# +# attr_reader :page_name +# +# def add_revision( author, contents ) +# @revisions << { :created => Time.now, +# :author => author, +# :contents => contents } +# end +# +# def wiki_page_references +# [@page_name] + @revisions.last[:contents].scan(/\b(?:[A-Z]+[a-z]+){2,}/) +# end +# +# # ... +# end +# +# # create a new page... +# home_page = WikiPage.new( "HomePage", "James Edward Gray II", +# "A page about the JoysOfDocumentation..." ) +# +# # then we want to update page data and the index together, or not at all... +# wiki = PStore.new("wiki_pages.pstore") +# wiki.transaction do # begin transaction; do all of this or none of it +# # store page... +# wiki[home_page.page_name] = home_page +# # ensure that an index has been created... +# wiki[:wiki_index] ||= Array.new +# # update wiki index... +# wiki[:wiki_index].push(*home_page.wiki_page_references) +# end # commit changes to wiki data store file +# +# ### Some time later... ### +# +# # read wiki data... +# wiki.transaction(true) do # begin read-only transaction, no changes allowed +# wiki.roots.each do |data_root_name| +# p data_root_name +# p wiki[data_root_name] +# end +# end +# +# == Transaction modes +# +# By default, file integrity is only ensured as long as the operating system +# (and the underlying hardware) doesn't raise any unexpected I/O errors. If an +# I/O error occurs while PStore is writing to its file, then the file will +# become corrupted. +# +# You can prevent this by setting pstore.ultra_safe = true. +# However, this results in a minor performance loss, and only works on platforms +# that support atomic file renames. Please consult the documentation for +# +ultra_safe+ for details. +# +# Needless to say, if you're storing valuable data with PStore, then you should +# backup the PStore files from time to time. +class PStore + RDWR_ACCESS = {mode: IO::RDWR | IO::CREAT | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze + RD_ACCESS = {mode: IO::RDONLY | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze + WR_ACCESS = {mode: IO::WRONLY | IO::CREAT | IO::TRUNC | IO::BINARY, encoding: Encoding::ASCII_8BIT}.freeze + + # The error type thrown by all PStore methods. + class Error < StandardError + end + + # Whether PStore should do its best to prevent file corruptions, even when under + # unlikely-to-occur error conditions such as out-of-space conditions and other + # unusual OS filesystem errors. Setting this flag comes at the price in the form + # of a performance loss. + # + # This flag only has effect on platforms on which file renames are atomic (e.g. + # all POSIX platforms: Linux, MacOS X, FreeBSD, etc). The default value is false. + attr_accessor :ultra_safe + + # + # To construct a PStore object, pass in the _file_ path where you would like + # the data to be stored. + # + # PStore objects are always reentrant. But if _thread_safe_ is set to true, + # then it will become thread-safe at the cost of a minor performance hit. + # + def initialize(file, thread_safe = false) + dir = File::dirname(file) + unless File::directory? dir + raise PStore::Error, format("directory %s does not exist", dir) + end + if File::exist? file and not File::readable? file + raise PStore::Error, format("file %s not readable", file) + end + @filename = file + @abort = false + @ultra_safe = false + @thread_safe = thread_safe + @lock = Mutex.new + end + + # Raises PStore::Error if the calling code is not in a PStore#transaction. + def in_transaction + raise PStore::Error, "not in transaction" unless @lock.locked? + end + # + # Raises PStore::Error if the calling code is not in a PStore#transaction or + # if the code is in a read-only PStore#transaction. + # + def in_transaction_wr + in_transaction + raise PStore::Error, "in read-only transaction" if @rdonly + end + private :in_transaction, :in_transaction_wr + + # + # Retrieves a value from the PStore file data, by _name_. The hierarchy of + # Ruby objects stored under that root _name_ will be returned. + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def [](name) + in_transaction + @table[name] + end + # + # This method is just like PStore#[], save that you may also provide a + # _default_ value for the object. In the event the specified _name_ is not + # found in the data store, your _default_ will be returned instead. If you do + # not specify a default, PStore::Error will be raised if the object is not + # found. + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def fetch(name, default=PStore::Error) + in_transaction + unless @table.key? name + if default == PStore::Error + raise PStore::Error, format("undefined root name `%s'", name) + else + return default + end + end + @table[name] + end + # + # Stores an individual Ruby object or a hierarchy of Ruby objects in the data + # store file under the root _name_. Assigning to a _name_ already in the data + # store clobbers the old data. + # + # == Example: + # + # require "pstore" + # + # store = PStore.new("data_file.pstore") + # store.transaction do # begin transaction + # # load some data into the store... + # store[:single_object] = "My data..." + # store[:obj_heirarchy] = { "Kev Jackson" => ["rational.rb", "pstore.rb"], + # "James Gray" => ["erb.rb", "pstore.rb"] } + # end # commit changes to data store file + # + # *WARNING*: This method is only valid in a PStore#transaction and it cannot + # be read-only. It will raise PStore::Error if called at any other time. + # + def []=(name, value) + in_transaction_wr + @table[name] = value + end + # + # Removes an object hierarchy from the data store, by _name_. + # + # *WARNING*: This method is only valid in a PStore#transaction and it cannot + # be read-only. It will raise PStore::Error if called at any other time. + # + def delete(name) + in_transaction_wr + @table.delete name + end + + # + # Returns the names of all object hierarchies currently in the store. + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def roots + in_transaction + @table.keys + end + # + # Returns true if the supplied _name_ is currently in the data store. + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def root?(name) + in_transaction + @table.key? name + end + # Returns the path to the data store file. + def path + @filename + end + + # + # Ends the current PStore#transaction, committing any changes to the data + # store immediately. + # + # == Example: + # + # require "pstore" + # + # store = PStore.new("data_file.pstore") + # store.transaction do # begin transaction + # # load some data into the store... + # store[:one] = 1 + # store[:two] = 2 + # + # store.commit # end transaction here, committing changes + # + # store[:three] = 3 # this change is never reached + # end + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def commit + in_transaction + @abort = false + throw :pstore_abort_transaction + end + # + # Ends the current PStore#transaction, discarding any changes to the data + # store. + # + # == Example: + # + # require "pstore" + # + # store = PStore.new("data_file.pstore") + # store.transaction do # begin transaction + # store[:one] = 1 # this change is not applied, see below... + # store[:two] = 2 # this change is not applied, see below... + # + # store.abort # end transaction here, discard all changes + # + # store[:three] = 3 # this change is never reached + # end + # + # *WARNING*: This method is only valid in a PStore#transaction. It will + # raise PStore::Error if called at any other time. + # + def abort + in_transaction + @abort = true + throw :pstore_abort_transaction + end + + # + # Opens a new transaction for the data store. Code executed inside a block + # passed to this method may read and write data to and from the data store + # file. + # + # At the end of the block, changes are committed to the data store + # automatically. You may exit the transaction early with a call to either + # PStore#commit or PStore#abort. See those methods for details about how + # changes are handled. Raising an uncaught Exception in the block is + # equivalent to calling PStore#abort. + # + # If _read_only_ is set to +true+, you will only be allowed to read from the + # data store during the transaction and any attempts to change the data will + # raise a PStore::Error. + # + # Note that PStore does not support nested transactions. + # + def transaction(read_only = false) # :yields: pstore + value = nil + if !@thread_safe + raise PStore::Error, "nested transaction" unless @lock.try_lock + else + begin + @lock.lock + rescue ThreadError + raise PStore::Error, "nested transaction" + end + end + begin + @rdonly = read_only + @abort = false + file = open_and_lock_file(@filename, read_only) + if file + begin + @table, checksum, original_data_size = load_data(file, read_only) + + catch(:pstore_abort_transaction) do + value = yield(self) + end + + if !@abort && !read_only + save_data(checksum, original_data_size, file) + end + ensure + file.close if !file.closed? + end + else + # This can only occur if read_only == true. + @table = {} + catch(:pstore_abort_transaction) do + value = yield(self) + end + end + ensure + @lock.unlock + end + value + end + + private + # Constant for relieving Ruby's garbage collector. + EMPTY_STRING = "" + EMPTY_MARSHAL_DATA = Marshal.dump({}) + EMPTY_MARSHAL_CHECKSUM = Digest::MD5.digest(EMPTY_MARSHAL_DATA) + + # + # Open the specified filename (either in read-only mode or in + # read-write mode) and lock it for reading or writing. + # + # The opened File object will be returned. If _read_only_ is true, + # and the file does not exist, then nil will be returned. + # + # All exceptions are propagated. + # + def open_and_lock_file(filename, read_only) + if read_only + begin + file = File.new(filename, RD_ACCESS) + begin + file.flock(File::LOCK_SH) + return file + rescue + file.close + raise + end + rescue Errno::ENOENT + return nil + end + else + file = File.new(filename, RDWR_ACCESS) + file.flock(File::LOCK_EX) + return file + end + end + + # Load the given PStore file. + # If +read_only+ is true, the unmarshalled Hash will be returned. + # If +read_only+ is false, a 3-tuple will be returned: the unmarshalled + # Hash, an MD5 checksum of the data, and the size of the data. + def load_data(file, read_only) + if read_only + begin + table = load(file) + raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash) + rescue EOFError + # This seems to be a newly-created file. + table = {} + end + table + else + data = file.read + if data.empty? + # This seems to be a newly-created file. + table = {} + checksum = empty_marshal_checksum + size = empty_marshal_data.bytesize + else + table = load(data) + checksum = Digest::MD5.digest(data) + size = data.bytesize + raise Error, "PStore file seems to be corrupted." unless table.is_a?(Hash) + end + data.replace(EMPTY_STRING) + [table, checksum, size] + end + end + + def on_windows? + is_windows = RUBY_PLATFORM =~ /mswin|mingw|bccwin|wince/ + self.class.__send__(:define_method, :on_windows?) do + is_windows + end + is_windows + end + + def save_data(original_checksum, original_file_size, file) + new_data = dump(@table) + + if new_data.bytesize != original_file_size || Digest::MD5.digest(new_data) != original_checksum + if @ultra_safe && !on_windows? + # Windows doesn't support atomic file renames. + save_data_with_atomic_file_rename_strategy(new_data, file) + else + save_data_with_fast_strategy(new_data, file) + end + end + + new_data.replace(EMPTY_STRING) + end + + def save_data_with_atomic_file_rename_strategy(data, file) + temp_filename = "#{@filename}.tmp.#{Process.pid}.#{rand 1000000}" + temp_file = File.new(temp_filename, WR_ACCESS) + begin + temp_file.flock(File::LOCK_EX) + temp_file.write(data) + temp_file.flush + File.rename(temp_filename, @filename) + rescue + File.unlink(temp_file) rescue nil + raise + ensure + temp_file.close + end + end + + def save_data_with_fast_strategy(data, file) + file.rewind + file.write(data) + file.truncate(data.bytesize) + end + + + # This method is just a wrapped around Marshal.dump + # to allow subclass overriding used in YAML::Store. + def dump(table) # :nodoc: + Marshal::dump(table) + end + + # This method is just a wrapped around Marshal.load. + # to allow subclass overriding used in YAML::Store. + def load(content) # :nodoc: + Marshal::load(content) + end + + def empty_marshal_data + EMPTY_MARSHAL_DATA + end + def empty_marshal_checksum + EMPTY_MARSHAL_CHECKSUM + end +end + +# :enddoc: + +if __FILE__ == $0 + db = PStore.new("/tmp/foo") + db.transaction do + p db.roots + ary = db["root"] = [1,2,3,4] + ary[1] = [1,1.5] + end + + 1000.times do + db.transaction do + db["root"][0] += 1 + p db["root"][0] + end + end + + db.transaction(true) do + p db["root"] + end +end diff --git a/ruby/lib/ruby/2.1.0/psych.rb b/ruby/lib/ruby/2.1.0/psych.rb new file mode 100644 index 0000000..329514a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych.rb @@ -0,0 +1,498 @@ +require 'psych.so' +require 'psych/nodes' +require 'psych/streaming' +require 'psych/visitors' +require 'psych/handler' +require 'psych/tree_builder' +require 'psych/parser' +require 'psych/omap' +require 'psych/set' +require 'psych/coder' +require 'psych/core_ext' +require 'psych/deprecated' +require 'psych/stream' +require 'psych/json/tree_builder' +require 'psych/json/stream' +require 'psych/handlers/document_stream' +require 'psych/class_loader' + +### +# = Overview +# +# Psych is a YAML parser and emitter. +# Psych leverages libyaml [Home page: http://pyyaml.org/wiki/LibYAML] +# or [HG repo: https://bitbucket.org/xi/libyaml] for its YAML parsing +# and emitting capabilities. In addition to wrapping libyaml, Psych also +# knows how to serialize and de-serialize most Ruby objects to and from +# the YAML format. +# +# = I NEED TO PARSE OR EMIT YAML RIGHT NOW! +# +# # Parse some YAML +# Psych.load("--- foo") # => "foo" +# +# # Emit some YAML +# Psych.dump("foo") # => "--- foo\n...\n" +# { :a => 'b'}.to_yaml # => "---\n:a: b\n" +# +# Got more time on your hands? Keep on reading! +# +# == YAML Parsing +# +# Psych provides a range of interfaces for parsing a YAML document ranging from +# low level to high level, depending on your parsing needs. At the lowest +# level, is an event based parser. Mid level is access to the raw YAML AST, +# and at the highest level is the ability to unmarshal YAML to Ruby objects. +# +# == YAML Emitting +# +# Psych provides a range of interfaces ranging from low to high level for +# producing YAML documents. Very similar to the YAML parsing interfaces, Psych +# provides at the lowest level, an event based system, mid-level is building +# a YAML AST, and the highest level is converting a Ruby object straight to +# a YAML document. +# +# == High-level API +# +# === Parsing +# +# The high level YAML parser provided by Psych simply takes YAML as input and +# returns a Ruby data structure. For information on using the high level parser +# see Psych.load +# +# ==== Reading from a string +# +# Psych.load("--- a") # => 'a' +# Psych.load("---\n - a\n - b") # => ['a', 'b'] +# +# ==== Reading from a file +# +# Psych.load_file("database.yml") +# +# ==== Exception handling +# +# begin +# # The second argument chnages only the exception contents +# Psych.parse("--- `", "file.txt") +# rescue Psych::SyntaxError => ex +# ex.file # => 'file.txt' +# ex.message # => "(file.txt): found character that cannot start any token" +# end +# +# === Emitting +# +# The high level emitter has the easiest interface. Psych simply takes a Ruby +# data structure and converts it to a YAML document. See Psych.dump for more +# information on dumping a Ruby data structure. +# +# ==== Writing to a string +# +# # Dump an array, get back a YAML string +# Psych.dump(['a', 'b']) # => "---\n- a\n- b\n" +# +# # Dump an array to an IO object +# Psych.dump(['a', 'b'], StringIO.new) # => # +# +# # Dump an array with indentation set +# Psych.dump(['a', ['b']], :indentation => 3) # => "---\n- a\n- - b\n" +# +# # Dump an array to an IO with indentation set +# Psych.dump(['a', ['b']], StringIO.new, :indentation => 3) +# +# ==== Writing to a file +# +# Currently there is no direct API for dumping Ruby structure to file: +# +# File.open('database.yml', 'w') do |file| +# file.write(Psych.dump(['a', 'b'])) +# end +# +# == Mid-level API +# +# === Parsing +# +# Psych provides access to an AST produced from parsing a YAML document. This +# tree is built using the Psych::Parser and Psych::TreeBuilder. The AST can +# be examined and manipulated freely. Please see Psych::parse_stream, +# Psych::Nodes, and Psych::Nodes::Node for more information on dealing with +# YAML syntax trees. +# +# ==== Reading from a string +# +# # Returns Psych::Nodes::Stream +# Psych.parse_stream("---\n - a\n - b") +# +# # Returns Psych::Nodes::Document +# Psych.parse("---\n - a\n - b") +# +# ==== Reading from a file +# +# # Returns Psych::Nodes::Stream +# Psych.parse_stream(File.read('database.yml')) +# +# # Returns Psych::Nodes::Document +# Psych.parse_file('database.yml') +# +# ==== Exception handling +# +# begin +# # The second argument chnages only the exception contents +# Psych.parse("--- `", "file.txt") +# rescue Psych::SyntaxError => ex +# ex.file # => 'file.txt' +# ex.message # => "(file.txt): found character that cannot start any token" +# end +# +# === Emitting +# +# At the mid level is building an AST. This AST is exactly the same as the AST +# used when parsing a YAML document. Users can build an AST by hand and the +# AST knows how to emit itself as a YAML document. See Psych::Nodes, +# Psych::Nodes::Node, and Psych::TreeBuilder for more information on building +# a YAML AST. +# +# ==== Writing to a string +# +# # We need Psych::Nodes::Stream (not Psych::Nodes::Document) +# stream = Psych.parse_stream("---\n - a\n - b") +# +# stream.to_yaml # => "---\n- a\n- b\n" +# +# ==== Writing to a file +# +# # We need Psych::Nodes::Stream (not Psych::Nodes::Document) +# stream = Psych.parse_stream(File.read('database.yml')) +# +# File.open('database.yml', 'w') do |file| +# file.write(stream.to_yaml) +# end +# +# == Low-level API +# +# === Parsing +# +# The lowest level parser should be used when the YAML input is already known, +# and the developer does not want to pay the price of building an AST or +# automatic detection and conversion to Ruby objects. See Psych::Parser for +# more information on using the event based parser. +# +# ==== Reading to Psych::Nodes::Stream structure +# +# parser = Psych::Parser.new(TreeBuilder.new) # => # +# parser = Psych.parser # it's an alias for the above +# +# parser.parse("---\n - a\n - b") # => # +# parser.handler # => # +# parser.handler.root # => # +# +# ==== Receiving an events stream +# +# parser = Psych::Parser.new(Psych::Handlers::Recorder.new) +# +# parser.parse("---\n - a\n - b") +# parser.events # => [list of [event, args] lists] +# # event is one of: Psych::Handler::EVENTS +# # args are the arguments passed to the event +# +# === Emitting +# +# The lowest level emitter is an event based system. Events are sent to a +# Psych::Emitter object. That object knows how to convert the events to a YAML +# document. This interface should be used when document format is known in +# advance or speed is a concern. See Psych::Emitter for more information. +# +# ==== Writing to a Ruby structure +# +# Psych.parser.parse("--- a") # => # +# +# parser.handler.first # => # +# parser.handler.first.to_ruby # => ["a"] +# +# parser.handler.root.first # => # +# parser.handler.root.first.to_ruby # => "a" +# +# # You can instantiate an Emitter manually +# Psych::Visitors::ToRuby.new.accept(parser.handler.root.first) +# # => "a" + +module Psych + # The version is Psych you're using + VERSION = '2.0.5' + + # The version of libyaml Psych is using + LIBYAML_VERSION = Psych.libyaml_version.join '.' + + ### + # Load +yaml+ in to a Ruby data structure. If multiple documents are + # provided, the object contained in the first document will be returned. + # +filename+ will be used in the exception message if any exception is raised + # while parsing. + # + # Raises a Psych::SyntaxError when a YAML syntax error is detected. + # + # Example: + # + # Psych.load("--- a") # => 'a' + # Psych.load("---\n - a\n - b") # => ['a', 'b'] + # + # begin + # Psych.load("--- `", "file.txt") + # rescue Psych::SyntaxError => ex + # ex.file # => 'file.txt' + # ex.message # => "(file.txt): found character that cannot start any token" + # end + def self.load yaml, filename = nil + result = parse(yaml, filename) + result ? result.to_ruby : result + end + + ### + # Safely load the yaml string in +yaml+. By default, only the following + # classes are allowed to be deserialized: + # + # * TrueClass + # * FalseClass + # * NilClass + # * Numeric + # * String + # * Array + # * Hash + # + # Recursive data structures are not allowed by default. Arbitrary classes + # can be allowed by adding those classes to the +whitelist+. They are + # additive. For example, to allow Date deserialization: + # + # Psych.safe_load(yaml, [Date]) + # + # Now the Date class can be loaded in addition to the classes listed above. + # + # Aliases can be explicitly allowed by changing the +aliases+ parameter. + # For example: + # + # x = [] + # x << x + # yaml = Psych.dump x + # Psych.safe_load yaml # => raises an exception + # Psych.safe_load yaml, [], [], true # => loads the aliases + # + # A Psych::DisallowedClass exception will be raised if the yaml contains a + # class that isn't in the whitelist. + # + # A Psych::BadAlias exception will be raised if the yaml contains aliases + # but the +aliases+ parameter is set to false. + def self.safe_load yaml, whitelist_classes = [], whitelist_symbols = [], aliases = false, filename = nil + result = parse(yaml, filename) + return unless result + + class_loader = ClassLoader::Restricted.new(whitelist_classes.map(&:to_s), + whitelist_symbols.map(&:to_s)) + scanner = ScalarScanner.new class_loader + if aliases + visitor = Visitors::ToRuby.new scanner, class_loader + else + visitor = Visitors::NoAliasRuby.new scanner, class_loader + end + visitor.accept result + end + + ### + # Parse a YAML string in +yaml+. Returns the Psych::Nodes::Document. + # +filename+ is used in the exception message if a Psych::SyntaxError is + # raised. + # + # Raises a Psych::SyntaxError when a YAML syntax error is detected. + # + # Example: + # + # Psych.parse("---\n - a\n - b") # => # + # + # begin + # Psych.parse("--- `", "file.txt") + # rescue Psych::SyntaxError => ex + # ex.file # => 'file.txt' + # ex.message # => "(file.txt): found character that cannot start any token" + # end + # + # See Psych::Nodes for more information about YAML AST. + def self.parse yaml, filename = nil + parse_stream(yaml, filename) do |node| + return node + end + false + end + + ### + # Parse a file at +filename+. Returns the Psych::Nodes::Document. + # + # Raises a Psych::SyntaxError when a YAML syntax error is detected. + def self.parse_file filename + File.open filename, 'r:bom|utf-8' do |f| + parse f, filename + end + end + + ### + # Returns a default parser + def self.parser + Psych::Parser.new(TreeBuilder.new) + end + + ### + # Parse a YAML string in +yaml+. Returns the Psych::Nodes::Stream. + # This method can handle multiple YAML documents contained in +yaml+. + # +filename+ is used in the exception message if a Psych::SyntaxError is + # raised. + # + # If a block is given, a Psych::Nodes::Document node will be yielded to the + # block as it's being parsed. + # + # Raises a Psych::SyntaxError when a YAML syntax error is detected. + # + # Example: + # + # Psych.parse_stream("---\n - a\n - b") # => # + # + # Psych.parse_stream("--- a\n--- b") do |node| + # node # => # + # end + # + # begin + # Psych.parse_stream("--- `", "file.txt") + # rescue Psych::SyntaxError => ex + # ex.file # => 'file.txt' + # ex.message # => "(file.txt): found character that cannot start any token" + # end + # + # See Psych::Nodes for more information about YAML AST. + def self.parse_stream yaml, filename = nil, &block + if block_given? + parser = Psych::Parser.new(Handlers::DocumentStream.new(&block)) + parser.parse yaml, filename + else + parser = self.parser + parser.parse yaml, filename + parser.handler.root + end + end + + ### + # call-seq: + # Psych.dump(o) -> string of yaml + # Psych.dump(o, options) -> string of yaml + # Psych.dump(o, io) -> io object passed in + # Psych.dump(o, io, options) -> io object passed in + # + # Dump Ruby object +o+ to a YAML string. Optional +options+ may be passed in + # to control the output format. If an IO object is passed in, the YAML will + # be dumped to that IO object. + # + # Example: + # + # # Dump an array, get back a YAML string + # Psych.dump(['a', 'b']) # => "---\n- a\n- b\n" + # + # # Dump an array to an IO object + # Psych.dump(['a', 'b'], StringIO.new) # => # + # + # # Dump an array with indentation set + # Psych.dump(['a', ['b']], :indentation => 3) # => "---\n- a\n- - b\n" + # + # # Dump an array to an IO with indentation set + # Psych.dump(['a', ['b']], StringIO.new, :indentation => 3) + def self.dump o, io = nil, options = {} + if Hash === io + options = io + io = nil + end + + visitor = Psych::Visitors::YAMLTree.create options + visitor << o + visitor.tree.yaml io, options + end + + ### + # Dump a list of objects as separate documents to a document stream. + # + # Example: + # + # Psych.dump_stream("foo\n ", {}) # => "--- ! \"foo\\n \"\n--- {}\n" + def self.dump_stream *objects + visitor = Psych::Visitors::YAMLTree.create({}) + objects.each do |o| + visitor << o + end + visitor.tree.yaml + end + + ### + # Dump Ruby +object+ to a JSON string. + def self.to_json object + visitor = Psych::Visitors::JSONTree.create + visitor << object + visitor.tree.yaml + end + + ### + # Load multiple documents given in +yaml+. Returns the parsed documents + # as a list. If a block is given, each document will be converted to Ruby + # and passed to the block during parsing + # + # Example: + # + # Psych.load_stream("--- foo\n...\n--- bar\n...") # => ['foo', 'bar'] + # + # list = [] + # Psych.load_stream("--- foo\n...\n--- bar\n...") do |ruby| + # list << ruby + # end + # list # => ['foo', 'bar'] + # + def self.load_stream yaml, filename = nil + if block_given? + parse_stream(yaml, filename) do |node| + yield node.to_ruby + end + else + parse_stream(yaml, filename).children.map { |child| child.to_ruby } + end + end + + ### + # Load the document contained in +filename+. Returns the yaml contained in + # +filename+ as a Ruby object + def self.load_file filename + File.open(filename, 'r:bom|utf-8') { |f| self.load f, filename } + end + + # :stopdoc: + @domain_types = {} + def self.add_domain_type domain, type_tag, &block + key = ['tag', domain, type_tag].join ':' + @domain_types[key] = [key, block] + @domain_types["tag:#{type_tag}"] = [key, block] + end + + def self.add_builtin_type type_tag, &block + domain = 'yaml.org,2002' + key = ['tag', domain, type_tag].join ':' + @domain_types[key] = [key, block] + end + + def self.remove_type type_tag + @domain_types.delete type_tag + end + + @load_tags = {} + @dump_tags = {} + def self.add_tag tag, klass + @load_tags[tag] = klass.name + @dump_tags[klass] = tag + end + + class << self + attr_accessor :load_tags + attr_accessor :dump_tags + attr_accessor :domain_types + end + # :startdoc: +end diff --git a/ruby/lib/ruby/2.1.0/psych/class_loader.rb b/ruby/lib/ruby/2.1.0/psych/class_loader.rb new file mode 100644 index 0000000..46c6b93 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/class_loader.rb @@ -0,0 +1,101 @@ +require 'psych/omap' +require 'psych/set' + +module Psych + class ClassLoader # :nodoc: + BIG_DECIMAL = 'BigDecimal' + COMPLEX = 'Complex' + DATE = 'Date' + DATE_TIME = 'DateTime' + EXCEPTION = 'Exception' + OBJECT = 'Object' + PSYCH_OMAP = 'Psych::Omap' + PSYCH_SET = 'Psych::Set' + RANGE = 'Range' + RATIONAL = 'Rational' + REGEXP = 'Regexp' + STRUCT = 'Struct' + SYMBOL = 'Symbol' + + def initialize + @cache = CACHE.dup + end + + def load klassname + return nil if !klassname || klassname.empty? + + find klassname + end + + def symbolize sym + symbol + sym.to_sym + end + + constants.each do |const| + konst = const_get const + define_method(const.to_s.downcase) do + load konst + end + end + + private + + def find klassname + @cache[klassname] ||= resolve(klassname) + end + + def resolve klassname + name = klassname + retried = false + + begin + path2class(name) + rescue ArgumentError, NameError => ex + unless retried + name = "Struct::#{name}" + retried = ex + retry + end + raise retried + end + end + + CACHE = Hash[constants.map { |const| + val = const_get const + begin + [val, ::Object.const_get(val)] + rescue + nil + end + }.compact] + + class Restricted < ClassLoader + def initialize classes, symbols + @classes = classes + @symbols = symbols + super() + end + + def symbolize sym + return super if @symbols.empty? + + if @symbols.include? sym + super + else + raise DisallowedClass, 'Symbol' + end + end + + private + + def find klassname + if @classes.include? klassname + super + else + raise DisallowedClass, klassname + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/coder.rb b/ruby/lib/ruby/2.1.0/psych/coder.rb new file mode 100644 index 0000000..2b830d2 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/coder.rb @@ -0,0 +1,94 @@ +module Psych + ### + # If an object defines +encode_with+, then an instance of Psych::Coder will + # be passed to the method when the object is being serialized. The Coder + # automatically assumes a Psych::Nodes::Mapping is being emitted. Other + # objects like Sequence and Scalar may be emitted if +seq=+ or +scalar=+ are + # called, respectively. + class Coder + attr_accessor :tag, :style, :implicit, :object + attr_reader :type, :seq + + def initialize tag + @map = {} + @seq = [] + @implicit = false + @type = :map + @tag = tag + @style = Psych::Nodes::Mapping::BLOCK + @scalar = nil + @object = nil + end + + def scalar *args + if args.length > 0 + warn "#{caller[0]}: Coder#scalar(a,b,c) is deprecated" if $VERBOSE + @tag, @scalar, _ = args + @type = :scalar + end + @scalar + end + + # Emit a map. The coder will be yielded to the block. + def map tag = @tag, style = @style + @tag = tag + @style = style + yield self if block_given? + @map + end + + # Emit a scalar with +value+ and +tag+ + def represent_scalar tag, value + self.tag = tag + self.scalar = value + end + + # Emit a sequence with +list+ and +tag+ + def represent_seq tag, list + @tag = tag + self.seq = list + end + + # Emit a sequence with +map+ and +tag+ + def represent_map tag, map + @tag = tag + self.map = map + end + + # Emit an arbitrary object +obj+ and +tag+ + def represent_object tag, obj + @tag = tag + @type = :object + @object = obj + end + + # Emit a scalar with +value+ + def scalar= value + @type = :scalar + @scalar = value + end + + # Emit a map with +value+ + def map= map + @type = :map + @map = map + end + + def []= k, v + @type = :map + @map[k] = v + end + alias :add :[]= + + def [] k + @type = :map + @map[k] + end + + # Emit a sequence of +list+ + def seq= list + @type = :seq + @seq = list + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/core_ext.rb b/ruby/lib/ruby/2.1.0/psych/core_ext.rb new file mode 100644 index 0000000..9c8134d --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/core_ext.rb @@ -0,0 +1,35 @@ +class Object + def self.yaml_tag url + Psych.add_tag(url, self) + end + + # FIXME: rename this to "to_yaml" when syck is removed + + ### + # call-seq: to_yaml(options = {}) + # + # Convert an object to YAML. See Psych.dump for more information on the + # available +options+. + def psych_to_yaml options = {} + Psych.dump self, options + end + remove_method :to_yaml rescue nil + alias :to_yaml :psych_to_yaml +end + +class Module + def psych_yaml_as url + return if caller[0].end_with?('rubytypes.rb') + if $VERBOSE + warn "#{caller[0]}: yaml_as is deprecated, please use yaml_tag" + end + Psych.add_tag(url, self) + end + + remove_method :yaml_as rescue nil + alias :yaml_as :psych_yaml_as +end + +if defined?(::IRB) + require 'psych/y' +end diff --git a/ruby/lib/ruby/2.1.0/psych/deprecated.rb b/ruby/lib/ruby/2.1.0/psych/deprecated.rb new file mode 100644 index 0000000..8c310b3 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/deprecated.rb @@ -0,0 +1,85 @@ +require 'date' + +module Psych + DEPRECATED = __FILE__ # :nodoc: + + module DeprecatedMethods # :nodoc: + attr_accessor :taguri + attr_accessor :to_yaml_style + end + + def self.quick_emit thing, opts = {}, &block # :nodoc: + warn "#{caller[0]}: YAML.quick_emit is deprecated" if $VERBOSE && !caller[0].start_with?(File.dirname(__FILE__)) + target = eval 'self', block.binding + target.extend DeprecatedMethods + metaclass = class << target; self; end + metaclass.send(:define_method, :encode_with) do |coder| + target.taguri = coder.tag + target.to_yaml_style = coder.style + block.call coder + end + target.psych_to_yaml unless opts[:nodump] + end + + # This method is deprecated, use Psych.load_stream instead. + def self.load_documents yaml, &block + if $VERBOSE + warn "#{caller[0]}: load_documents is deprecated, use load_stream" + end + list = load_stream yaml + return list unless block_given? + list.each(&block) + end + + def self.detect_implicit thing + warn "#{caller[0]}: detect_implicit is deprecated" if $VERBOSE + return '' unless String === thing + return 'null' if '' == thing + ss = ScalarScanner.new(ClassLoader.new) + ss.tokenize(thing).class.name.downcase + end + + def self.add_ruby_type type_tag, &block + warn "#{caller[0]}: add_ruby_type is deprecated, use add_domain_type" if $VERBOSE + domain = 'ruby.yaml.org,2002' + key = ['tag', domain, type_tag].join ':' + @domain_types[key] = [key, block] + end + + def self.add_private_type type_tag, &block + warn "#{caller[0]}: add_private_type is deprecated, use add_domain_type" if $VERBOSE + domain = 'x-private' + key = [domain, type_tag].join ':' + @domain_types[key] = [key, block] + end + + def self.tagurize thing + warn "#{caller[0]}: add_private_type is deprecated, use add_domain_type" if $VERBOSE + return thing unless String === thing + "tag:yaml.org,2002:#{thing}" + end + + def self.read_type_class type, reference + warn "#{caller[0]}: read_type_class is deprecated" if $VERBOSE + _, _, type, name = type.split ':', 4 + + reference = name.split('::').inject(reference) do |k,n| + k.const_get(n.to_sym) + end if name + [type, reference] + end + + def self.object_maker klass, hash + warn "#{caller[0]}: object_maker is deprecated" if $VERBOSE + klass.allocate.tap do |obj| + hash.each { |k,v| obj.instance_variable_set(:"@#{k}", v) } + end + end +end + +class Object + undef :to_yaml_properties rescue nil + def to_yaml_properties # :nodoc: + instance_variables + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/exception.rb b/ruby/lib/ruby/2.1.0/psych/exception.rb new file mode 100644 index 0000000..ce9d2ca --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/exception.rb @@ -0,0 +1,13 @@ +module Psych + class Exception < RuntimeError + end + + class BadAlias < Exception + end + + class DisallowedClass < Exception + def initialize klass_name + super "Tried to load unspecified class: #{klass_name}" + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/handler.rb b/ruby/lib/ruby/2.1.0/psych/handler.rb new file mode 100644 index 0000000..c55afe7 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/handler.rb @@ -0,0 +1,249 @@ +module Psych + ### + # Psych::Handler is an abstract base class that defines the events used + # when dealing with Psych::Parser. Clients who want to use Psych::Parser + # should implement a class that inherits from Psych::Handler and define + # events that they can handle. + # + # Psych::Handler defines all events that Psych::Parser can possibly send to + # event handlers. + # + # See Psych::Parser for more details + class Handler + ### + # Configuration options for dumping YAML. + class DumperOptions + attr_accessor :line_width, :indentation, :canonical + + def initialize + @line_width = 0 + @indentation = 2 + @canonical = false + end + end + + # Default dumping options + OPTIONS = DumperOptions.new + + # Events that a Handler should respond to. + EVENTS = [ :alias, + :empty, + :end_document, + :end_mapping, + :end_sequence, + :end_stream, + :scalar, + :start_document, + :start_mapping, + :start_sequence, + :start_stream ] + + ### + # Called with +encoding+ when the YAML stream starts. This method is + # called once per stream. A stream may contain multiple documents. + # + # See the constants in Psych::Parser for the possible values of +encoding+. + def start_stream encoding + end + + ### + # Called when the document starts with the declared +version+, + # +tag_directives+, if the document is +implicit+. + # + # +version+ will be an array of integers indicating the YAML version being + # dealt with, +tag_directives+ is a list of tuples indicating the prefix + # and suffix of each tag, and +implicit+ is a boolean indicating whether + # the document is started implicitly. + # + # === Example + # + # Given the following YAML: + # + # %YAML 1.1 + # %TAG ! tag:tenderlovemaking.com,2009: + # --- !squee + # + # The parameters for start_document must be this: + # + # version # => [1, 1] + # tag_directives # => [["!", "tag:tenderlovemaking.com,2009:"]] + # implicit # => false + def start_document version, tag_directives, implicit + end + + ### + # Called with the document ends. +implicit+ is a boolean value indicating + # whether or not the document has an implicit ending. + # + # === Example + # + # Given the following YAML: + # + # --- + # hello world + # + # +implicit+ will be true. Given this YAML: + # + # --- + # hello world + # ... + # + # +implicit+ will be false. + def end_document implicit + end + + ### + # Called when an alias is found to +anchor+. +anchor+ will be the name + # of the anchor found. + # + # === Example + # + # Here we have an example of an array that references itself in YAML: + # + # --- &ponies + # - first element + # - *ponies + # + # &ponies is the achor, *ponies is the alias. In this case, alias is + # called with "ponies". + def alias anchor + end + + ### + # Called when a scalar +value+ is found. The scalar may have an + # +anchor+, a +tag+, be implicitly +plain+ or implicitly +quoted+ + # + # +value+ is the string value of the scalar + # +anchor+ is an associated anchor or nil + # +tag+ is an associated tag or nil + # +plain+ is a boolean value + # +quoted+ is a boolean value + # +style+ is an integer idicating the string style + # + # See the constants in Psych::Nodes::Scalar for the possible values of + # +style+ + # + # === Example + # + # Here is a YAML document that exercises most of the possible ways this + # method can be called: + # + # --- + # - !str "foo" + # - &anchor fun + # - many + # lines + # - | + # many + # newlines + # + # The above YAML document contains a list with four strings. Here are + # the parameters sent to this method in the same order: + # + # # value anchor tag plain quoted style + # ["foo", nil, "!str", false, false, 3 ] + # ["fun", "anchor", nil, true, false, 1 ] + # ["many lines", nil, nil, true, false, 1 ] + # ["many\nnewlines\n", nil, nil, false, true, 4 ] + # + def scalar value, anchor, tag, plain, quoted, style + end + + ### + # Called when a sequence is started. + # + # +anchor+ is the anchor associated with the sequence or nil. + # +tag+ is the tag associated with the sequence or nil. + # +implicit+ a boolean indicating whether or not the sequence was implicitly + # started. + # +style+ is an integer indicating the list style. + # + # See the constants in Psych::Nodes::Sequence for the possible values of + # +style+. + # + # === Example + # + # Here is a YAML document that exercises most of the possible ways this + # method can be called: + # + # --- + # - !!seq [ + # a + # ] + # - &pewpew + # - b + # + # The above YAML document consists of three lists, an outer list that + # contains two inner lists. Here is a matrix of the parameters sent + # to represent these lists: + # + # # anchor tag implicit style + # [nil, nil, true, 1 ] + # [nil, "tag:yaml.org,2002:seq", false, 2 ] + # ["pewpew", nil, true, 1 ] + + def start_sequence anchor, tag, implicit, style + end + + ### + # Called when a sequence ends. + def end_sequence + end + + ### + # Called when a map starts. + # + # +anchor+ is the anchor associated with the map or +nil+. + # +tag+ is the tag associated with the map or +nil+. + # +implicit+ is a boolean indicating whether or not the map was implicitly + # started. + # +style+ is an integer indicating the mapping style. + # + # See the constants in Psych::Nodes::Mapping for the possible values of + # +style+. + # + # === Example + # + # Here is a YAML document that exercises most of the possible ways this + # method can be called: + # + # --- + # k: !!map { hello: world } + # v: &pewpew + # hello: world + # + # The above YAML document consists of three maps, an outer map that contains + # two inner maps. Below is a matrix of the parameters sent in order to + # represent these three maps: + # + # # anchor tag implicit style + # [nil, nil, true, 1 ] + # [nil, "tag:yaml.org,2002:map", false, 2 ] + # ["pewpew", nil, true, 1 ] + + def start_mapping anchor, tag, implicit, style + end + + ### + # Called when a map ends + def end_mapping + end + + ### + # Called when an empty event happens. (Which, as far as I can tell, is + # never). + def empty + end + + ### + # Called when the YAML stream ends + def end_stream + end + + ### + # Is this handler a streaming handler? + def streaming? + false + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/handlers/document_stream.rb b/ruby/lib/ruby/2.1.0/psych/handlers/document_stream.rb new file mode 100644 index 0000000..e429993 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/handlers/document_stream.rb @@ -0,0 +1,22 @@ +require 'psych/tree_builder' + +module Psych + module Handlers + class DocumentStream < Psych::TreeBuilder # :nodoc: + def initialize &block + super + @block = block + end + + def start_document version, tag_directives, implicit + n = Nodes::Document.new version, tag_directives, implicit + push n + end + + def end_document implicit_end = !streaming? + @last.implicit_end = implicit_end + @block.call pop + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/handlers/recorder.rb b/ruby/lib/ruby/2.1.0/psych/handlers/recorder.rb new file mode 100644 index 0000000..4eae62e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/handlers/recorder.rb @@ -0,0 +1,39 @@ +require 'psych/handler' + +module Psych + module Handlers + ### + # This handler will capture an event and record the event. Recorder events + # are available vial Psych::Handlers::Recorder#events. + # + # For example: + # + # recorder = Psych::Handlers::Recorder.new + # parser = Psych::Parser.new recorder + # parser.parse '--- foo' + # + # recorder.events # => [list of events] + # + # # Replay the events + # + # emitter = Psych::Emitter.new $stdout + # recorder.events.each do |m, args| + # emitter.send m, *args + # end + + class Recorder < Psych::Handler + attr_reader :events + + def initialize + @events = [] + super + end + + EVENTS.each do |event| + define_method event do |*args| + @events << [event, args] + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/json/ruby_events.rb b/ruby/lib/ruby/2.1.0/psych/json/ruby_events.rb new file mode 100644 index 0000000..6b73249 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/json/ruby_events.rb @@ -0,0 +1,19 @@ +module Psych + module JSON + module RubyEvents # :nodoc: + def visit_Time o + formatted = format_time o + @emitter.scalar formatted, nil, nil, false, true, Nodes::Scalar::DOUBLE_QUOTED + end + + def visit_DateTime o + visit_Time o.to_time + end + + def visit_String o + @emitter.scalar o.to_s, nil, nil, false, true, Nodes::Scalar::DOUBLE_QUOTED + end + alias :visit_Symbol :visit_String + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/json/stream.rb b/ruby/lib/ruby/2.1.0/psych/json/stream.rb new file mode 100644 index 0000000..fe2a6e9 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/json/stream.rb @@ -0,0 +1,16 @@ +require 'psych/json/ruby_events' +require 'psych/json/yaml_events' + +module Psych + module JSON + class Stream < Psych::Visitors::JSONTree + include Psych::JSON::RubyEvents + include Psych::Streaming + extend Psych::Streaming::ClassMethods + + class Emitter < Psych::Stream::Emitter # :nodoc: + include Psych::JSON::YAMLEvents + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/json/tree_builder.rb b/ruby/lib/ruby/2.1.0/psych/json/tree_builder.rb new file mode 100644 index 0000000..b799c93 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/json/tree_builder.rb @@ -0,0 +1,12 @@ +require 'psych/json/yaml_events' + +module Psych + module JSON + ### + # Psych::JSON::TreeBuilder is an event based AST builder. Events are sent + # to an instance of Psych::JSON::TreeBuilder and a JSON AST is constructed. + class TreeBuilder < Psych::TreeBuilder + include Psych::JSON::YAMLEvents + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/json/yaml_events.rb b/ruby/lib/ruby/2.1.0/psych/json/yaml_events.rb new file mode 100644 index 0000000..d054d9b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/json/yaml_events.rb @@ -0,0 +1,29 @@ +module Psych + module JSON + module YAMLEvents # :nodoc: + def start_document version, tag_directives, implicit + super(version, tag_directives, !streaming?) + end + + def end_document implicit_end = !streaming? + super(implicit_end) + end + + def start_mapping anchor, tag, implicit, style + super(anchor, nil, true, Nodes::Mapping::FLOW) + end + + def start_sequence anchor, tag, implicit, style + super(anchor, nil, true, Nodes::Sequence::FLOW) + end + + def scalar value, anchor, tag, plain, quoted, style + if "tag:yaml.org,2002:null" == tag + super('null', nil, nil, true, false, Nodes::Scalar::PLAIN) + else + super + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/nodes.rb b/ruby/lib/ruby/2.1.0/psych/nodes.rb new file mode 100644 index 0000000..f3b33fe --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/nodes.rb @@ -0,0 +1,77 @@ +require 'psych/nodes/node' +require 'psych/nodes/stream' +require 'psych/nodes/document' +require 'psych/nodes/sequence' +require 'psych/nodes/scalar' +require 'psych/nodes/mapping' +require 'psych/nodes/alias' + +module Psych + ### + # = Overview + # + # When using Psych.load to deserialize a YAML document, the document is + # translated to an intermediary AST. That intermediary AST is then + # translated in to a Ruby object graph. + # + # In the opposite direction, when using Psych.dump, the Ruby object graph is + # translated to an intermediary AST which is then converted to a YAML + # document. + # + # Psych::Nodes contains all of the classes that make up the nodes of a YAML + # AST. You can manually build an AST and use one of the visitors (see + # Psych::Visitors) to convert that AST to either a YAML document or to a + # Ruby object graph. + # + # Here is an example of building an AST that represents a list with one + # scalar: + # + # # Create our nodes + # stream = Psych::Nodes::Stream.new + # doc = Psych::Nodes::Document.new + # seq = Psych::Nodes::Sequence.new + # scalar = Psych::Nodes::Scalar.new('foo') + # + # # Build up our tree + # stream.children << doc + # doc.children << seq + # seq.children << scalar + # + # The stream is the root of the tree. We can then convert the tree to YAML: + # + # stream.to_yaml => "---\n- foo\n" + # + # Or convert it to Ruby: + # + # stream.to_ruby => [["foo"]] + # + # == YAML AST Requirements + # + # A valid YAML AST *must* have one Psych::Nodes::Stream at the root. A + # Psych::Nodes::Stream node must have 1 or more Psych::Nodes::Document nodes + # as children. + # + # Psych::Nodes::Document nodes must have one and *only* one child. That child + # may be one of: + # + # * Psych::Nodes::Sequence + # * Psych::Nodes::Mapping + # * Psych::Nodes::Scalar + # + # Psych::Nodes::Sequence and Psych::Nodes::Mapping nodes may have many + # children, but Psych::Nodes::Mapping nodes should have an even number of + # children. + # + # All of these are valid children for Psych::Nodes::Sequence and + # Psych::Nodes::Mapping nodes: + # + # * Psych::Nodes::Sequence + # * Psych::Nodes::Mapping + # * Psych::Nodes::Scalar + # * Psych::Nodes::Alias + # + # Psych::Nodes::Scalar and Psych::Nodes::Alias are both terminal nodes and + # should not have any children. + module Nodes + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/nodes/alias.rb b/ruby/lib/ruby/2.1.0/psych/nodes/alias.rb new file mode 100644 index 0000000..5bd4df1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/nodes/alias.rb @@ -0,0 +1,18 @@ +module Psych + module Nodes + ### + # This class represents a {YAML Alias}[http://yaml.org/spec/1.1/#alias]. + # It points to an +anchor+. + # + # A Psych::Nodes::Alias is a terminal node and may have no children. + class Alias < Psych::Nodes::Node + # The anchor this alias links to + attr_accessor :anchor + + # Create a new Alias that points to an +anchor+ + def initialize anchor + @anchor = anchor + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/nodes/document.rb b/ruby/lib/ruby/2.1.0/psych/nodes/document.rb new file mode 100644 index 0000000..32014d6 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/nodes/document.rb @@ -0,0 +1,60 @@ +module Psych + module Nodes + ### + # This represents a YAML Document. This node must be a child of + # Psych::Nodes::Stream. A Psych::Nodes::Document must have one child, + # and that child may be one of the following: + # + # * Psych::Nodes::Sequence + # * Psych::Nodes::Mapping + # * Psych::Nodes::Scalar + class Document < Psych::Nodes::Node + # The version of the YAML document + attr_accessor :version + + # A list of tag directives for this document + attr_accessor :tag_directives + + # Was this document implicitly created? + attr_accessor :implicit + + # Is the end of the document implicit? + attr_accessor :implicit_end + + ### + # Create a new Psych::Nodes::Document object. + # + # +version+ is a list indicating the YAML version. + # +tags_directives+ is a list of tag directive declarations + # +implicit+ is a flag indicating whether the document will be implicitly + # started. + # + # == Example: + # This creates a YAML document object that represents a YAML 1.1 document + # with one tag directive, and has an implicit start: + # + # Psych::Nodes::Document.new( + # [1,1], + # [["!", "tag:tenderlovemaking.com,2009:"]], + # true + # ) + # + # == See Also + # See also Psych::Handler#start_document + def initialize version = [], tag_directives = [], implicit = false + super() + @version = version + @tag_directives = tag_directives + @implicit = implicit + @implicit_end = true + end + + ### + # Returns the root node. A Document may only have one root node: + # http://yaml.org/spec/1.1/#id898031 + def root + children.first + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/nodes/mapping.rb b/ruby/lib/ruby/2.1.0/psych/nodes/mapping.rb new file mode 100644 index 0000000..5ba95ce --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/nodes/mapping.rb @@ -0,0 +1,56 @@ +module Psych + module Nodes + ### + # This class represents a {YAML Mapping}[http://yaml.org/spec/1.1/#mapping]. + # + # A Psych::Nodes::Mapping node may have 0 or more children, but must have + # an even number of children. Here are the valid children a + # Psych::Nodes::Mapping node may have: + # + # * Psych::Nodes::Sequence + # * Psych::Nodes::Mapping + # * Psych::Nodes::Scalar + # * Psych::Nodes::Alias + class Mapping < Psych::Nodes::Node + # Any Map Style + ANY = 0 + + # Block Map Style + BLOCK = 1 + + # Flow Map Style + FLOW = 2 + + # The optional anchor for this mapping + attr_accessor :anchor + + # The optional tag for this mapping + attr_accessor :tag + + # Is this an implicit mapping? + attr_accessor :implicit + + # The style of this mapping + attr_accessor :style + + ### + # Create a new Psych::Nodes::Mapping object. + # + # +anchor+ is the anchor associated with the map or +nil+. + # +tag+ is the tag associated with the map or +nil+. + # +implicit+ is a boolean indicating whether or not the map was implicitly + # started. + # +style+ is an integer indicating the mapping style. + # + # == See Also + # See also Psych::Handler#start_mapping + def initialize anchor = nil, tag = nil, implicit = true, style = BLOCK + super() + @anchor = anchor + @tag = tag + @implicit = implicit + @style = style + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/nodes/node.rb b/ruby/lib/ruby/2.1.0/psych/nodes/node.rb new file mode 100644 index 0000000..83233a6 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/nodes/node.rb @@ -0,0 +1,55 @@ +require 'stringio' +require 'psych/class_loader' +require 'psych/scalar_scanner' + +module Psych + module Nodes + ### + # The base class for any Node in a YAML parse tree. This class should + # never be instantiated. + class Node + include Enumerable + + # The children of this node + attr_reader :children + + # An associated tag + attr_reader :tag + + # Create a new Psych::Nodes::Node + def initialize + @children = [] + end + + ### + # Iterate over each node in the tree. Yields each node to +block+ depth + # first. + def each &block + return enum_for :each unless block_given? + Visitors::DepthFirst.new(block).accept self + end + + ### + # Convert this node to Ruby. + # + # See also Psych::Visitors::ToRuby + def to_ruby + Visitors::ToRuby.create.accept(self) + end + alias :transform :to_ruby + + ### + # Convert this node to YAML. + # + # See also Psych::Visitors::Emitter + def yaml io = nil, options = {} + real_io = io || StringIO.new(''.encode('utf-8')) + + Visitors::Emitter.new(real_io, options).accept self + return real_io.string unless io + io + end + alias :to_yaml :yaml + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/nodes/scalar.rb b/ruby/lib/ruby/2.1.0/psych/nodes/scalar.rb new file mode 100644 index 0000000..1b1b25b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/nodes/scalar.rb @@ -0,0 +1,67 @@ +module Psych + module Nodes + ### + # This class represents a {YAML Scalar}[http://yaml.org/spec/1.1/#id858081]. + # + # This node type is a terminal node and should not have any children. + class Scalar < Psych::Nodes::Node + # Any style scalar, the emitter chooses + ANY = 0 + + # Plain scalar style + PLAIN = 1 + + # Single quoted style + SINGLE_QUOTED = 2 + + # Double quoted style + DOUBLE_QUOTED = 3 + + # Literal style + LITERAL = 4 + + # Folded style + FOLDED = 5 + + # The scalar value + attr_accessor :value + + # The anchor value (if there is one) + attr_accessor :anchor + + # The tag value (if there is one) + attr_accessor :tag + + # Is this a plain scalar? + attr_accessor :plain + + # Is this scalar quoted? + attr_accessor :quoted + + # The style of this scalar + attr_accessor :style + + ### + # Create a new Psych::Nodes::Scalar object. + # + # +value+ is the string value of the scalar + # +anchor+ is an associated anchor or nil + # +tag+ is an associated tag or nil + # +plain+ is a boolean value + # +quoted+ is a boolean value + # +style+ is an integer idicating the string style + # + # == See Also + # + # See also Psych::Handler#scalar + def initialize value, anchor = nil, tag = nil, plain = true, quoted = false, style = ANY + @value = value + @anchor = anchor + @tag = tag + @plain = plain + @quoted = quoted + @style = style + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/nodes/sequence.rb b/ruby/lib/ruby/2.1.0/psych/nodes/sequence.rb new file mode 100644 index 0000000..e4b833d --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/nodes/sequence.rb @@ -0,0 +1,81 @@ +module Psych + module Nodes + ### + # This class represents a + # {YAML sequence}[http://yaml.org/spec/1.1/#sequence/syntax]. + # + # A YAML sequence is basically a list, and looks like this: + # + # %YAML 1.1 + # --- + # - I am + # - a Sequence + # + # A YAML sequence may have an anchor like this: + # + # %YAML 1.1 + # --- + # &A [ + # "This sequence", + # "has an anchor" + # ] + # + # A YAML sequence may also have a tag like this: + # + # %YAML 1.1 + # --- + # !!seq [ + # "This sequence", + # "has a tag" + # ] + # + # This class represents a sequence in a YAML document. A + # Psych::Nodes::Sequence node may have 0 or more children. Valid children + # for this node are: + # + # * Psych::Nodes::Sequence + # * Psych::Nodes::Mapping + # * Psych::Nodes::Scalar + # * Psych::Nodes::Alias + class Sequence < Psych::Nodes::Node + # Any Styles, emitter chooses + ANY = 0 + + # Block style sequence + BLOCK = 1 + + # Flow style sequence + FLOW = 2 + + # The anchor for this sequence (if any) + attr_accessor :anchor + + # The tag name for this sequence (if any) + attr_accessor :tag + + # Is this sequence started implicitly? + attr_accessor :implicit + + # The sequece style used + attr_accessor :style + + ### + # Create a new object representing a YAML sequence. + # + # +anchor+ is the anchor associated with the sequence or nil. + # +tag+ is the tag associated with the sequence or nil. + # +implicit+ a boolean indicating whether or not the sequence was + # implicitly started. + # +style+ is an integer indicating the list style. + # + # See Psych::Handler#start_sequence + def initialize anchor = nil, tag = nil, implicit = true, style = BLOCK + super() + @anchor = anchor + @tag = tag + @implicit = implicit + @style = style + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/nodes/stream.rb b/ruby/lib/ruby/2.1.0/psych/nodes/stream.rb new file mode 100644 index 0000000..7cf5e03 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/nodes/stream.rb @@ -0,0 +1,37 @@ +module Psych + module Nodes + ### + # Represents a YAML stream. This is the root node for any YAML parse + # tree. This node must have one or more child nodes. The only valid + # child node for a Psych::Nodes::Stream node is Psych::Nodes::Document. + class Stream < Psych::Nodes::Node + + # Encodings supported by Psych (and libyaml) + + # Any encoding + ANY = Psych::Parser::ANY + + # UTF-8 encoding + UTF8 = Psych::Parser::UTF8 + + # UTF-16LE encoding + UTF16LE = Psych::Parser::UTF16LE + + # UTF-16BE encoding + UTF16BE = Psych::Parser::UTF16BE + + # The encoding used for this stream + attr_accessor :encoding + + ### + # Create a new Psych::Nodes::Stream node with an +encoding+ that + # defaults to Psych::Nodes::Stream::UTF8. + # + # See also Psych::Handler#start_stream + def initialize encoding = UTF8 + super() + @encoding = encoding + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/omap.rb b/ruby/lib/ruby/2.1.0/psych/omap.rb new file mode 100644 index 0000000..6286270 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/omap.rb @@ -0,0 +1,4 @@ +module Psych + class Omap < ::Hash + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/parser.rb b/ruby/lib/ruby/2.1.0/psych/parser.rb new file mode 100644 index 0000000..84085f1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/parser.rb @@ -0,0 +1,51 @@ +module Psych + ### + # YAML event parser class. This class parses a YAML document and calls + # events on the handler that is passed to the constructor. The events can + # be used for things such as constructing a YAML AST or deserializing YAML + # documents. It can even be fed back to Psych::Emitter to emit the same + # document that was parsed. + # + # See Psych::Handler for documentation on the events that Psych::Parser emits. + # + # Here is an example that prints out ever scalar found in a YAML document: + # + # # Handler for detecting scalar values + # class ScalarHandler < Psych::Handler + # def scalar value, anchor, tag, plain, quoted, style + # puts value + # end + # end + # + # parser = Psych::Parser.new(ScalarHandler.new) + # parser.parse(yaml_document) + # + # Here is an example that feeds the parser back in to Psych::Emitter. The + # YAML document is read from STDIN and written back out to STDERR: + # + # parser = Psych::Parser.new(Psych::Emitter.new($stderr)) + # parser.parse($stdin) + # + # Psych uses Psych::Parser in combination with Psych::TreeBuilder to + # construct an AST of the parsed YAML document. + + class Parser + class Mark < Struct.new(:index, :line, :column) + end + + # The handler on which events will be called + attr_accessor :handler + + # Set the encoding for this parser to +encoding+ + attr_writer :external_encoding + + ### + # Creates a new Psych::Parser instance with +handler+. YAML events will + # be called on +handler+. See Psych::Parser for more details. + + def initialize handler = Handler.new + @handler = handler + @external_encoding = ANY + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/scalar_scanner.rb b/ruby/lib/ruby/2.1.0/psych/scalar_scanner.rb new file mode 100644 index 0000000..3fc9edd --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/scalar_scanner.rb @@ -0,0 +1,149 @@ +require 'strscan' + +module Psych + ### + # Scan scalars for built in types + class ScalarScanner + # Taken from http://yaml.org/type/timestamp.html + TIME = /^-?\d{4}-\d{1,2}-\d{1,2}(?:[Tt]|\s+)\d{1,2}:\d\d:\d\d(?:\.\d*)?(?:\s*(?:Z|[-+]\d{1,2}:?(?:\d\d)?))?$/ + + # Taken from http://yaml.org/type/float.html + FLOAT = /^(?:[-+]?([0-9][0-9_,]*)?\.[0-9]*([eE][-+][0-9]+)?(?# base 10) + |[-+]?[0-9][0-9_,]*(:[0-5]?[0-9])+\.[0-9_]*(?# base 60) + |[-+]?\.(inf|Inf|INF)(?# infinity) + |\.(nan|NaN|NAN)(?# not a number))$/x + + # Taken from http://yaml.org/type/int.html + INTEGER = /^(?:[-+]?0b[0-1_]+ (?# base 2) + |[-+]?0[0-7_]+ (?# base 8) + |[-+]?(?:0|[1-9][0-9_]*) (?# base 10) + |[-+]?0x[0-9a-fA-F_]+ (?# base 16))$/x + + attr_reader :class_loader + + # Create a new scanner + def initialize class_loader + @string_cache = {} + @symbol_cache = {} + @class_loader = class_loader + end + + # Tokenize +string+ returning the Ruby object + def tokenize string + return nil if string.empty? + return string if @string_cache.key?(string) + return @symbol_cache[string] if @symbol_cache.key?(string) + + case string + # Check for a String type, being careful not to get caught by hash keys, hex values, and + # special floats (e.g., -.inf). + when /^[^\d\.:-]?[A-Za-z_\s!@#\$%\^&\*\(\)\{\}\<\>\|\/\\~;=]+/ + if string.length > 5 + @string_cache[string] = true + return string + end + + case string + when /^[^ytonf~]/i + @string_cache[string] = true + string + when '~', /^null$/i + nil + when /^(yes|true|on)$/i + true + when /^(no|false|off)$/i + false + else + @string_cache[string] = true + string + end + when TIME + begin + parse_time string + rescue ArgumentError + string + end + when /^\d{4}-(?:1[012]|0\d|\d)-(?:[12]\d|3[01]|0\d|\d)$/ + require 'date' + begin + class_loader.date.strptime(string, '%Y-%m-%d') + rescue ArgumentError + string + end + when /^\.inf$/i + Float::INFINITY + when /^-\.inf$/i + -Float::INFINITY + when /^\.nan$/i + Float::NAN + when /^:./ + if string =~ /^:(["'])(.*)\1/ + @symbol_cache[string] = class_loader.symbolize($2.sub(/^:/, '')) + else + @symbol_cache[string] = class_loader.symbolize(string.sub(/^:/, '')) + end + when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+$/ + i = 0 + string.split(':').each_with_index do |n,e| + i += (n.to_i * 60 ** (e - 2).abs) + end + i + when /^[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\.[0-9_]*$/ + i = 0 + string.split(':').each_with_index do |n,e| + i += (n.to_f * 60 ** (e - 2).abs) + end + i + when FLOAT + if string =~ /\A[-+]?\.\Z/ + @string_cache[string] = true + string + else + Float(string.gsub(/[,_]|\.$/, '')) + end + else + int = parse_int string.gsub(/[,_]/, '') + return int if int + + @string_cache[string] = true + string + end + end + + ### + # Parse and return an int from +string+ + def parse_int string + return unless INTEGER === string + Integer(string) + end + + ### + # Parse and return a Time from +string+ + def parse_time string + klass = class_loader.load 'Time' + + date, time = *(string.split(/[ tT]/, 2)) + (yy, m, dd) = date.match(/^(-?\d{4})-(\d{1,2})-(\d{1,2})/).captures.map { |x| x.to_i } + md = time.match(/(\d+:\d+:\d+)(?:\.(\d*))?\s*(Z|[-+]\d+(:\d\d)?)?/) + + (hh, mm, ss) = md[1].split(':').map { |x| x.to_i } + us = (md[2] ? Rational("0.#{md[2]}") : 0) * 1000000 + + time = klass.utc(yy, m, dd, hh, mm, ss, us) + + return time if 'Z' == md[3] + return klass.at(time.to_i, us) unless md[3] + + tz = md[3].match(/^([+\-]?\d{1,2})\:?(\d{1,2})?$/)[1..-1].compact.map { |digit| Integer(digit, 10) } + offset = tz.first * 3600 + + if offset < 0 + offset -= ((tz[1] || 0) * 60) + else + offset += ((tz[1] || 0) * 60) + end + + klass.at((time - offset).to_i, us) + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/set.rb b/ruby/lib/ruby/2.1.0/psych/set.rb new file mode 100644 index 0000000..6793a8e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/set.rb @@ -0,0 +1,4 @@ +module Psych + class Set < ::Hash + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/stream.rb b/ruby/lib/ruby/2.1.0/psych/stream.rb new file mode 100644 index 0000000..88c4c4c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/stream.rb @@ -0,0 +1,37 @@ +module Psych + ### + # Psych::Stream is a streaming YAML emitter. It will not buffer your YAML, + # but send it straight to an IO. + # + # Here is an example use: + # + # stream = Psych::Stream.new($stdout) + # stream.start + # stream.push({:foo => 'bar'}) + # stream.finish + # + # YAML will be immediately emitted to $stdout with no buffering. + # + # Psych::Stream#start will take a block and ensure that Psych::Stream#finish + # is called, so you can do this form: + # + # stream = Psych::Stream.new($stdout) + # stream.start do |em| + # em.push(:foo => 'bar') + # end + # + class Stream < Psych::Visitors::YAMLTree + class Emitter < Psych::Emitter # :nodoc: + def end_document implicit_end = !streaming? + super + end + + def streaming? + true + end + end + + include Psych::Streaming + extend Psych::Streaming::ClassMethods + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/streaming.rb b/ruby/lib/ruby/2.1.0/psych/streaming.rb new file mode 100644 index 0000000..9d94eb5 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/streaming.rb @@ -0,0 +1,27 @@ +module Psych + module Streaming + module ClassMethods + ### + # Create a new streaming emitter. Emitter will print to +io+. See + # Psych::Stream for an example. + def new io + emitter = const_get(:Emitter).new(io) + class_loader = ClassLoader.new + ss = ScalarScanner.new class_loader + super(emitter, ss, {}) + end + end + + ### + # Start streaming using +encoding+ + def start encoding = Nodes::Stream::UTF8 + super.tap { yield self if block_given? } + ensure + finish if block_given? + end + + private + def register target, obj + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/syntax_error.rb b/ruby/lib/ruby/2.1.0/psych/syntax_error.rb new file mode 100644 index 0000000..e200ef0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/syntax_error.rb @@ -0,0 +1,21 @@ +require 'psych/exception' + +module Psych + class SyntaxError < Psych::Exception + attr_reader :file, :line, :column, :offset, :problem, :context + + def initialize file, line, col, offset, problem, context + err = [problem, context].compact.join ' ' + filename = file || '' + message = "(%s): %s at line %d column %d" % [filename, err, line, col] + + @file = file + @line = line + @column = col + @offset = offset + @problem = problem + @context = context + super(message) + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/tree_builder.rb b/ruby/lib/ruby/2.1.0/psych/tree_builder.rb new file mode 100644 index 0000000..c8f3447 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/tree_builder.rb @@ -0,0 +1,96 @@ +require 'psych/handler' + +module Psych + ### + # This class works in conjunction with Psych::Parser to build an in-memory + # parse tree that represents a YAML document. + # + # == Example + # + # parser = Psych::Parser.new Psych::TreeBuilder.new + # parser.parse('--- foo') + # tree = parser.handler.root + # + # See Psych::Handler for documentation on the event methods used in this + # class. + class TreeBuilder < Psych::Handler + # Returns the root node for the built tree + attr_reader :root + + # Create a new TreeBuilder instance + def initialize + @stack = [] + @last = nil + @root = nil + end + + %w{ + Sequence + Mapping + }.each do |node| + class_eval %{ + def start_#{node.downcase}(anchor, tag, implicit, style) + n = Nodes::#{node}.new(anchor, tag, implicit, style) + @last.children << n + push n + end + + def end_#{node.downcase} + pop + end + } + end + + ### + # Handles start_document events with +version+, +tag_directives+, + # and +implicit+ styling. + # + # See Psych::Handler#start_document + def start_document version, tag_directives, implicit + n = Nodes::Document.new version, tag_directives, implicit + @last.children << n + push n + end + + ### + # Handles end_document events with +version+, +tag_directives+, + # and +implicit+ styling. + # + # See Psych::Handler#start_document + def end_document implicit_end = !streaming? + @last.implicit_end = implicit_end + pop + end + + def start_stream encoding + @root = Nodes::Stream.new(encoding) + push @root + end + + def end_stream + pop + end + + def scalar value, anchor, tag, plain, quoted, style + s = Nodes::Scalar.new(value,anchor,tag,plain,quoted,style) + @last.children << s + s + end + + def alias anchor + @last.children << Nodes::Alias.new(anchor) + end + + private + def push value + @stack.push value + @last = value + end + + def pop + x = @stack.pop + @last = @stack.last + x + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/visitors.rb b/ruby/lib/ruby/2.1.0/psych/visitors.rb new file mode 100644 index 0000000..cc98b10 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/visitors.rb @@ -0,0 +1,6 @@ +require 'psych/visitors/visitor' +require 'psych/visitors/to_ruby' +require 'psych/visitors/emitter' +require 'psych/visitors/yaml_tree' +require 'psych/visitors/json_tree' +require 'psych/visitors/depth_first' diff --git a/ruby/lib/ruby/2.1.0/psych/visitors/depth_first.rb b/ruby/lib/ruby/2.1.0/psych/visitors/depth_first.rb new file mode 100644 index 0000000..c6eb814 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/visitors/depth_first.rb @@ -0,0 +1,26 @@ +module Psych + module Visitors + class DepthFirst < Psych::Visitors::Visitor + def initialize block + @block = block + end + + private + + def nary o + o.children.each { |x| visit x } + @block.call o + end + alias :visit_Psych_Nodes_Stream :nary + alias :visit_Psych_Nodes_Document :nary + alias :visit_Psych_Nodes_Sequence :nary + alias :visit_Psych_Nodes_Mapping :nary + + def terminal o + @block.call o + end + alias :visit_Psych_Nodes_Scalar :terminal + alias :visit_Psych_Nodes_Alias :terminal + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/visitors/emitter.rb b/ruby/lib/ruby/2.1.0/psych/visitors/emitter.rb new file mode 100644 index 0000000..c886e50 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/visitors/emitter.rb @@ -0,0 +1,51 @@ +module Psych + module Visitors + class Emitter < Psych::Visitors::Visitor + def initialize io, options = {} + opts = [:indentation, :canonical, :line_width].find_all { |opt| + options.key?(opt) + } + + if opts.empty? + @handler = Psych::Emitter.new io + else + du = Handler::DumperOptions.new + opts.each { |option| du.send :"#{option}=", options[option] } + @handler = Psych::Emitter.new io, du + end + end + + def visit_Psych_Nodes_Stream o + @handler.start_stream o.encoding + o.children.each { |c| accept c } + @handler.end_stream + end + + def visit_Psych_Nodes_Document o + @handler.start_document o.version, o.tag_directives, o.implicit + o.children.each { |c| accept c } + @handler.end_document o.implicit_end + end + + def visit_Psych_Nodes_Scalar o + @handler.scalar o.value, o.anchor, o.tag, o.plain, o.quoted, o.style + end + + def visit_Psych_Nodes_Sequence o + @handler.start_sequence o.anchor, o.tag, o.implicit, o.style + o.children.each { |c| accept c } + @handler.end_sequence + end + + def visit_Psych_Nodes_Mapping o + @handler.start_mapping o.anchor, o.tag, o.implicit, o.style + o.children.each { |c| accept c } + @handler.end_mapping + end + + def visit_Psych_Nodes_Alias o + @handler.alias o.anchor + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/visitors/json_tree.rb b/ruby/lib/ruby/2.1.0/psych/visitors/json_tree.rb new file mode 100644 index 0000000..0127ac8 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/visitors/json_tree.rb @@ -0,0 +1,24 @@ +require 'psych/json/ruby_events' + +module Psych + module Visitors + class JSONTree < YAMLTree + include Psych::JSON::RubyEvents + + def self.create options = {} + emitter = Psych::JSON::TreeBuilder.new + class_loader = ClassLoader.new + ss = ScalarScanner.new class_loader + new(emitter, ss, options) + end + + def accept target + if target.respond_to?(:encode_with) + dump_coder target + else + send(@dispatch_cache[target.class], target) + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/visitors/to_ruby.rb b/ruby/lib/ruby/2.1.0/psych/visitors/to_ruby.rb new file mode 100644 index 0000000..5c3baa5 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/visitors/to_ruby.rb @@ -0,0 +1,370 @@ +require 'psych/scalar_scanner' +require 'psych/class_loader' +require 'psych/exception' + +unless defined?(Regexp::NOENCODING) + Regexp::NOENCODING = 32 +end + +module Psych + module Visitors + ### + # This class walks a YAML AST, converting each node to Ruby + class ToRuby < Psych::Visitors::Visitor + def self.create + class_loader = ClassLoader.new + scanner = ScalarScanner.new class_loader + new(scanner, class_loader) + end + + attr_reader :class_loader + + def initialize ss, class_loader + super() + @st = {} + @ss = ss + @domain_types = Psych.domain_types + @class_loader = class_loader + end + + def accept target + result = super + return result if @domain_types.empty? || !target.tag + + key = target.tag.sub(/^[!\/]*/, '').sub(/(,\d+)\//, '\1:') + key = "tag:#{key}" unless key =~ /^(tag:|x-private)/ + + if @domain_types.key? key + value, block = @domain_types[key] + return block.call value, result + end + + result + end + + def deserialize o + if klass = resolve_class(Psych.load_tags[o.tag]) + instance = klass.allocate + + if instance.respond_to?(:init_with) + coder = Psych::Coder.new(o.tag) + coder.scalar = o.value + instance.init_with coder + end + + return instance + end + + return o.value if o.quoted + return @ss.tokenize(o.value) unless o.tag + + case o.tag + when '!binary', 'tag:yaml.org,2002:binary' + o.value.unpack('m').first + when /^!(?:str|ruby\/string)(?::(.*))?/, 'tag:yaml.org,2002:str' + klass = resolve_class($1) + if klass + klass.allocate.replace o.value + else + o.value + end + when '!ruby/object:BigDecimal' + require 'bigdecimal' + class_loader.big_decimal._load o.value + when "!ruby/object:DateTime" + class_loader.date_time + require 'date' + @ss.parse_time(o.value).to_datetime + when '!ruby/encoding' + ::Encoding.find o.value + when "!ruby/object:Complex" + class_loader.complex + Complex(o.value) + when "!ruby/object:Rational" + class_loader.rational + Rational(o.value) + when "!ruby/class", "!ruby/module" + resolve_class o.value + when "tag:yaml.org,2002:float", "!float" + Float(@ss.tokenize(o.value)) + when "!ruby/regexp" + klass = class_loader.regexp + o.value =~ /^\/(.*)\/([mixn]*)$/ + source = $1 + options = 0 + lang = nil + ($2 || '').split('').each do |option| + case option + when 'x' then options |= Regexp::EXTENDED + when 'i' then options |= Regexp::IGNORECASE + when 'm' then options |= Regexp::MULTILINE + when 'n' then options |= Regexp::NOENCODING + else lang = option + end + end + klass.new(*[source, options, lang].compact) + when "!ruby/range" + klass = class_loader.range + args = o.value.split(/([.]{2,3})/, 2).map { |s| + accept Nodes::Scalar.new(s) + } + args.push(args.delete_at(1) == '...') + klass.new(*args) + when /^!ruby\/sym(bol)?:?(.*)?$/ + class_loader.symbolize o.value + else + @ss.tokenize o.value + end + end + private :deserialize + + def visit_Psych_Nodes_Scalar o + register o, deserialize(o) + end + + def visit_Psych_Nodes_Sequence o + if klass = resolve_class(Psych.load_tags[o.tag]) + instance = klass.allocate + + if instance.respond_to?(:init_with) + coder = Psych::Coder.new(o.tag) + coder.seq = o.children.map { |c| accept c } + instance.init_with coder + end + + return instance + end + + case o.tag + when nil + register_empty(o) + when '!omap', 'tag:yaml.org,2002:omap' + map = register(o, Psych::Omap.new) + o.children.each { |a| + map[accept(a.children.first)] = accept a.children.last + } + map + when /^!(?:seq|ruby\/array):(.*)$/ + klass = resolve_class($1) + list = register(o, klass.allocate) + o.children.each { |c| list.push accept c } + list + else + register_empty(o) + end + end + + def visit_Psych_Nodes_Mapping o + if Psych.load_tags[o.tag] + return revive(resolve_class(Psych.load_tags[o.tag]), o) + end + return revive_hash(register(o, {}), o) unless o.tag + + case o.tag + when /^!ruby\/struct:?(.*)?$/ + klass = resolve_class($1) if $1 + + if klass + s = register(o, klass.allocate) + + members = {} + struct_members = s.members.map { |x| class_loader.symbolize x } + o.children.each_slice(2) do |k,v| + member = accept(k) + value = accept(v) + if struct_members.include?(class_loader.symbolize(member)) + s.send("#{member}=", value) + else + members[member.to_s.sub(/^@/, '')] = value + end + end + init_with(s, members, o) + else + klass = class_loader.struct + members = o.children.map { |c| accept c } + h = Hash[*members] + klass.new(*h.map { |k,v| + class_loader.symbolize k + }).new(*h.map { |k,v| v }) + end + + when /^!ruby\/object:?(.*)?$/ + name = $1 || 'Object' + + if name == 'Complex' + class_loader.complex + h = Hash[*o.children.map { |c| accept c }] + register o, Complex(h['real'], h['image']) + elsif name == 'Rational' + class_loader.rational + h = Hash[*o.children.map { |c| accept c }] + register o, Rational(h['numerator'], h['denominator']) + else + obj = revive((resolve_class(name) || class_loader.object), o) + obj + end + + when /^!(?:str|ruby\/string)(?::(.*))?/, 'tag:yaml.org,2002:str' + klass = resolve_class($1) + members = {} + string = nil + + o.children.each_slice(2) do |k,v| + key = accept k + value = accept v + + if key == 'str' + if klass + string = klass.allocate.replace value + else + string = value + end + register(o, string) + else + members[key] = value + end + end + init_with(string, members.map { |k,v| [k.to_s.sub(/^@/, ''),v] }, o) + when /^!ruby\/array:(.*)$/ + klass = resolve_class($1) + list = register(o, klass.allocate) + + members = Hash[o.children.map { |c| accept c }.each_slice(2).to_a] + list.replace members['internal'] + + members['ivars'].each do |ivar, v| + list.instance_variable_set ivar, v + end + list + + when '!ruby/range' + klass = class_loader.range + h = Hash[*o.children.map { |c| accept c }] + register o, klass.new(h['begin'], h['end'], h['excl']) + + when /^!ruby\/exception:?(.*)?$/ + h = Hash[*o.children.map { |c| accept c }] + + e = build_exception((resolve_class($1) || class_loader.exception), + h.delete('message')) + init_with(e, h, o) + + when '!set', 'tag:yaml.org,2002:set' + set = class_loader.psych_set.new + @st[o.anchor] = set if o.anchor + o.children.each_slice(2) do |k,v| + set[accept(k)] = accept(v) + end + set + + when /^!map:(.*)$/, /^!ruby\/hash:(.*)$/ + revive_hash register(o, resolve_class($1).new), o + + when '!omap', 'tag:yaml.org,2002:omap' + map = register(o, class_loader.psych_omap.new) + o.children.each_slice(2) do |l,r| + map[accept(l)] = accept r + end + map + + else + revive_hash(register(o, {}), o) + end + end + + def visit_Psych_Nodes_Document o + accept o.root + end + + def visit_Psych_Nodes_Stream o + o.children.map { |c| accept c } + end + + def visit_Psych_Nodes_Alias o + @st.fetch(o.anchor) { raise BadAlias, "Unknown alias: #{o.anchor}" } + end + + private + def register node, object + @st[node.anchor] = object if node.anchor + object + end + + def register_empty object + list = register(object, []) + object.children.each { |c| list.push accept c } + list + end + + def revive_hash hash, o + o.children.each_slice(2) { |k,v| + key = accept(k) + val = accept(v) + + if key == '<<' + case v + when Nodes::Alias + begin + hash.merge! val + rescue TypeError + hash[key] = val + end + when Nodes::Sequence + begin + h = {} + val.reverse_each do |value| + h.merge! value + end + hash.merge! h + rescue TypeError + hash[key] = val + end + else + hash[key] = val + end + else + hash[key] = val + end + + } + hash + end + + def merge_key hash, key, val + end + + def revive klass, node + s = register(node, klass.allocate) + init_with(s, revive_hash({}, node), node) + end + + def init_with o, h, node + c = Psych::Coder.new(node.tag) + c.map = h + + if o.respond_to?(:init_with) + o.init_with c + elsif o.respond_to?(:yaml_initialize) + if $VERBOSE + warn "Implementing #{o.class}#yaml_initialize is deprecated, please implement \"init_with(coder)\"" + end + o.yaml_initialize c.tag, c.map + else + h.each { |k,v| o.instance_variable_set(:"@#{k}", v) } + end + o + end + + # Convert +klassname+ to a Class + def resolve_class klassname + class_loader.load klassname + end + end + + class NoAliasRuby < ToRuby + def visit_Psych_Nodes_Alias o + raise BadAlias, "Unknown alias: #{o.anchor}" + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/visitors/visitor.rb b/ruby/lib/ruby/2.1.0/psych/visitors/visitor.rb new file mode 100644 index 0000000..4d7772f --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/visitors/visitor.rb @@ -0,0 +1,19 @@ +module Psych + module Visitors + class Visitor + def accept target + visit target + end + + private + + DISPATCH = Hash.new do |hash, klass| + hash[klass] = "visit_#{klass.name.gsub('::', '_')}" + end + + def visit target + send DISPATCH[target.class], target + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb b/ruby/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb new file mode 100644 index 0000000..ff0fcd2 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/visitors/yaml_tree.rb @@ -0,0 +1,521 @@ +require 'psych/tree_builder' +require 'psych/scalar_scanner' +require 'psych/class_loader' + +module Psych + module Visitors + ### + # YAMLTree builds a YAML ast given a Ruby object. For example: + # + # builder = Psych::Visitors::YAMLTree.new + # builder << { :foo => 'bar' } + # builder.tree # => # true) + end + end + rescue + # public_method or source_location might be overridden, + # and it's OK to skip it since it's only to emit a warning + end + end + + if target.respond_to?(:encode_with) + dump_coder target + else + send(@dispatch_cache[target.class], target) + end + end + + def visit_Psych_Omap o + seq = @emitter.start_sequence(nil, '!omap', false, Nodes::Sequence::BLOCK) + register(o, seq) + + o.each { |k,v| visit_Hash k => v } + @emitter.end_sequence + end + + def visit_Encoding o + tag = "!ruby/encoding" + @emitter.scalar o.name, nil, tag, false, false, Nodes::Scalar::ANY + end + + def visit_Object o + tag = Psych.dump_tags[o.class] + unless tag + klass = o.class == Object ? nil : o.class.name + tag = ['!ruby/object', klass].compact.join(':') + end + + map = @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK) + register(o, map) + + dump_ivars o + @emitter.end_mapping + end + + def visit_Struct o + tag = ['!ruby/struct', o.class.name].compact.join(':') + + register o, @emitter.start_mapping(nil, tag, false, Nodes::Mapping::BLOCK) + o.members.each do |member| + @emitter.scalar member.to_s, nil, nil, true, false, Nodes::Scalar::ANY + accept o[member] + end + + dump_ivars o + + @emitter.end_mapping + end + + def visit_Exception o + tag = ['!ruby/exception', o.class.name].join ':' + + @emitter.start_mapping nil, tag, false, Nodes::Mapping::BLOCK + + { + 'message' => private_iv_get(o, 'mesg'), + 'backtrace' => private_iv_get(o, 'backtrace'), + }.each do |k,v| + next unless v + @emitter.scalar k, nil, nil, true, false, Nodes::Scalar::ANY + accept v + end + + dump_ivars o + + @emitter.end_mapping + end + + def visit_Regexp o + register o, @emitter.scalar(o.inspect, nil, '!ruby/regexp', false, false, Nodes::Scalar::ANY) + end + + def visit_DateTime o + formatted = if o.offset.zero? + o.strftime("%Y-%m-%d %H:%M:%S.%9N Z".freeze) + else + o.strftime("%Y-%m-%d %H:%M:%S.%9N %:z".freeze) + end + tag = '!ruby/object:DateTime' + register o, @emitter.scalar(formatted, nil, tag, false, false, Nodes::Scalar::ANY) + end + + def visit_Time o + formatted = format_time o + register o, @emitter.scalar(formatted, nil, nil, true, false, Nodes::Scalar::ANY) + end + + def visit_Rational o + register o, @emitter.start_mapping(nil, '!ruby/object:Rational', false, Nodes::Mapping::BLOCK) + + [ + 'denominator', o.denominator.to_s, + 'numerator', o.numerator.to_s + ].each do |m| + @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY + end + + @emitter.end_mapping + end + + def visit_Complex o + register o, @emitter.start_mapping(nil, '!ruby/object:Complex', false, Nodes::Mapping::BLOCK) + + ['real', o.real.to_s, 'image', o.imag.to_s].each do |m| + @emitter.scalar m, nil, nil, true, false, Nodes::Scalar::ANY + end + + @emitter.end_mapping + end + + def visit_Integer o + @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY + end + alias :visit_TrueClass :visit_Integer + alias :visit_FalseClass :visit_Integer + alias :visit_Date :visit_Integer + + def visit_Float o + if o.nan? + @emitter.scalar '.nan', nil, nil, true, false, Nodes::Scalar::ANY + elsif o.infinite? + @emitter.scalar((o.infinite? > 0 ? '.inf' : '-.inf'), + nil, nil, true, false, Nodes::Scalar::ANY) + else + @emitter.scalar o.to_s, nil, nil, true, false, Nodes::Scalar::ANY + end + end + + def visit_BigDecimal o + @emitter.scalar o._dump, nil, '!ruby/object:BigDecimal', false, false, Nodes::Scalar::ANY + end + + def visit_String o + plain = true + quote = true + style = Nodes::Scalar::PLAIN + tag = nil + str = o + + if binary?(o) + str = [o].pack('m').chomp + tag = '!binary' # FIXME: change to below when syck is removed + #tag = 'tag:yaml.org,2002:binary' + style = Nodes::Scalar::LITERAL + plain = false + quote = false + elsif o =~ /\n/ + style = Nodes::Scalar::LITERAL + elsif o =~ /^\W[^"]*$/ + style = Nodes::Scalar::DOUBLE_QUOTED + else + unless String === @ss.tokenize(o) + style = Nodes::Scalar::SINGLE_QUOTED + end + end + + ivars = find_ivars o + + if ivars.empty? + unless o.class == ::String + tag = "!ruby/string:#{o.class}" + plain = false + quote = false + end + @emitter.scalar str, nil, tag, plain, quote, style + else + maptag = '!ruby/string' + maptag << ":#{o.class}" unless o.class == ::String + + register o, @emitter.start_mapping(nil, maptag, false, Nodes::Mapping::BLOCK) + @emitter.scalar 'str', nil, nil, true, false, Nodes::Scalar::ANY + @emitter.scalar str, nil, tag, plain, quote, style + + dump_ivars o + + @emitter.end_mapping + end + end + + def visit_Module o + raise TypeError, "can't dump anonymous module: #{o}" unless o.name + register o, @emitter.scalar(o.name, nil, '!ruby/module', false, false, Nodes::Scalar::SINGLE_QUOTED) + end + + def visit_Class o + raise TypeError, "can't dump anonymous class: #{o}" unless o.name + register o, @emitter.scalar(o.name, nil, '!ruby/class', false, false, Nodes::Scalar::SINGLE_QUOTED) + end + + def visit_Range o + register o, @emitter.start_mapping(nil, '!ruby/range', false, Nodes::Mapping::BLOCK) + ['begin', o.begin, 'end', o.end, 'excl', o.exclude_end?].each do |m| + accept m + end + @emitter.end_mapping + end + + def visit_Hash o + tag = o.class == ::Hash ? nil : "!ruby/hash:#{o.class}" + implicit = !tag + + register(o, @emitter.start_mapping(nil, tag, implicit, Psych::Nodes::Mapping::BLOCK)) + + o.each do |k,v| + accept k + accept v + end + + @emitter.end_mapping + end + + def visit_Psych_Set o + register(o, @emitter.start_mapping(nil, '!set', false, Psych::Nodes::Mapping::BLOCK)) + + o.each do |k,v| + accept k + accept v + end + + @emitter.end_mapping + end + + def visit_Array o + if o.class == ::Array + register o, @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK) + o.each { |c| accept c } + @emitter.end_sequence + else + visit_array_subclass o + end + end + + def visit_NilClass o + @emitter.scalar('', nil, 'tag:yaml.org,2002:null', true, false, Nodes::Scalar::ANY) + end + + def visit_Symbol o + @emitter.scalar ":#{o}", nil, nil, true, false, Nodes::Scalar::ANY + end + + private + # FIXME: Remove the index and count checks in Psych 3.0 + NULL = "\x00" + BINARY_RANGE = "\x00-\x7F" + WS_RANGE = "^ -~\t\r\n" + + def binary? string + (string.encoding == Encoding::ASCII_8BIT && !string.ascii_only?) || + string.index(NULL) || + string.count(BINARY_RANGE, WS_RANGE).fdiv(string.length) > 0.3 + end + + def visit_array_subclass o + tag = "!ruby/array:#{o.class}" + if o.instance_variables.empty? + node = @emitter.start_sequence(nil, tag, false, Nodes::Sequence::BLOCK) + register o, node + o.each { |c| accept c } + @emitter.end_sequence + else + node = @emitter.start_mapping(nil, tag, false, Nodes::Sequence::BLOCK) + register o, node + + # Dump the internal list + accept 'internal' + @emitter.start_sequence(nil, nil, true, Nodes::Sequence::BLOCK) + o.each { |c| accept c } + @emitter.end_sequence + + # Dump the ivars + accept 'ivars' + @emitter.start_mapping(nil, nil, true, Nodes::Sequence::BLOCK) + o.instance_variables.each do |ivar| + accept ivar + accept o.instance_variable_get ivar + end + @emitter.end_mapping + + @emitter.end_mapping + end + end + + def dump_list o + end + + # '%:z' was no defined until 1.9.3 + if RUBY_VERSION < '1.9.3' + def format_time time + formatted = time.strftime("%Y-%m-%d %H:%M:%S.%9N") + + if time.utc? + formatted += " Z" + else + zone = time.strftime('%z') + formatted += " #{zone[0,3]}:#{zone[3,5]}" + end + + formatted + end + else + def format_time time + if time.utc? + time.strftime("%Y-%m-%d %H:%M:%S.%9N Z") + else + time.strftime("%Y-%m-%d %H:%M:%S.%9N %:z") + end + end + end + + # FIXME: remove this method once "to_yaml_properties" is removed + def find_ivars target + begin + loc = target.method(:to_yaml_properties).source_location.first + unless loc.start_with?(Psych::DEPRECATED) || loc.end_with?('rubytypes.rb') + if $VERBOSE + warn "#{loc}: to_yaml_properties is deprecated, please implement \"encode_with(coder)\"" + end + return target.to_yaml_properties + end + rescue + # public_method or source_location might be overridden, + # and it's OK to skip it since it's only to emit a warning. + end + + target.instance_variables + end + + def register target, yaml_obj + @st.register target, yaml_obj + yaml_obj + end + + def dump_coder o + @coders << o + tag = Psych.dump_tags[o.class] + unless tag + klass = o.class == Object ? nil : o.class.name + tag = ['!ruby/object', klass].compact.join(':') + end + + c = Psych::Coder.new(tag) + o.encode_with(c) + emit_coder c + end + + def emit_coder c + case c.type + when :scalar + @emitter.scalar c.scalar, nil, c.tag, c.tag.nil?, false, Nodes::Scalar::ANY + when :seq + @emitter.start_sequence nil, c.tag, c.tag.nil?, Nodes::Sequence::BLOCK + c.seq.each do |thing| + accept thing + end + @emitter.end_sequence + when :map + @emitter.start_mapping nil, c.tag, c.implicit, c.style + c.map.each do |k,v| + accept k + accept v + end + @emitter.end_mapping + when :object + accept c.object + end + end + + def dump_ivars target + ivars = find_ivars target + + ivars.each do |iv| + @emitter.scalar("#{iv.to_s.sub(/^@/, '')}", nil, nil, true, false, Nodes::Scalar::ANY) + accept target.instance_variable_get(iv) + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/psych/y.rb b/ruby/lib/ruby/2.1.0/psych/y.rb new file mode 100644 index 0000000..d0e049d --- /dev/null +++ b/ruby/lib/ruby/2.1.0/psych/y.rb @@ -0,0 +1,9 @@ +module Kernel + ### + # An alias for Psych.dump_stream meant to be used with IRB. + def y *objects + puts Psych.dump_stream(*objects) + end + private :y +end + diff --git a/ruby/lib/ruby/2.1.0/racc/parser.rb b/ruby/lib/ruby/2.1.0/racc/parser.rb new file mode 100644 index 0000000..9e6de03 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/racc/parser.rb @@ -0,0 +1,624 @@ +#-- +# $originalId: parser.rb,v 1.8 2006/07/06 11:42:07 aamine Exp $ +# +# Copyright (c) 1999-2006 Minero Aoki +# +# This program is free software. +# You can distribute/modify this program under the same terms of ruby. +# +# As a special exception, when this code is copied by Racc +# into a Racc output file, you may use that output file +# without restriction. +#++ + +module Racc + class ParseError < StandardError; end +end +unless defined?(::ParseError) + ParseError = Racc::ParseError +end + +# Racc is a LALR(1) parser generator. +# It is written in Ruby itself, and generates Ruby programs. +# +# == Command-line Reference +# +# racc [-ofilename] [--output-file=filename] +# [-erubypath] [--embedded=rubypath] +# [-v] [--verbose] +# [-Ofilename] [--log-file=filename] +# [-g] [--debug] +# [-E] [--embedded] +# [-l] [--no-line-convert] +# [-c] [--line-convert-all] +# [-a] [--no-omit-actions] +# [-C] [--check-only] +# [-S] [--output-status] +# [--version] [--copyright] [--help] grammarfile +# +# [+filename+] +# Racc grammar file. Any extension is permitted. +# [-o+outfile+, --output-file=+outfile+] +# A filename for output. default is <+filename+>.tab.rb +# [-O+filename+, --log-file=+filename+] +# Place logging output in file +filename+. +# Default log file name is <+filename+>.output. +# [-e+rubypath+, --executable=+rubypath+] +# output executable file(mode 755). where +path+ is the Ruby interpreter. +# [-v, --verbose] +# verbose mode. create +filename+.output file, like yacc's y.output file. +# [-g, --debug] +# add debug code to parser class. To display debugging information, +# use this '-g' option and set @yydebug true in parser class. +# [-E, --embedded] +# Output parser which doesn't need runtime files (racc/parser.rb). +# [-C, --check-only] +# Check syntax of racc grammar file and quit. +# [-S, --output-status] +# Print messages time to time while compiling. +# [-l, --no-line-convert] +# turns off line number converting. +# [-c, --line-convert-all] +# Convert line number of actions, inner, header and footer. +# [-a, --no-omit-actions] +# Call all actions, even if an action is empty. +# [--version] +# print Racc version and quit. +# [--copyright] +# Print copyright and quit. +# [--help] +# Print usage and quit. +# +# == Generating Parser Using Racc +# +# To compile Racc grammar file, simply type: +# +# $ racc parse.y +# +# This creates Ruby script file "parse.tab.y". The -o option can change the output filename. +# +# == Writing A Racc Grammar File +# +# If you want your own parser, you have to write a grammar file. +# A grammar file contains the name of your parser class, grammar for the parser, +# user code, and anything else. +# When writing a grammar file, yacc's knowledge is helpful. +# If you have not used yacc before, Racc is not too difficult. +# +# Here's an example Racc grammar file. +# +# class Calcparser +# rule +# target: exp { print val[0] } +# +# exp: exp '+' exp +# | exp '*' exp +# | '(' exp ')' +# | NUMBER +# end +# +# Racc grammar files resemble yacc files. +# But (of course), this is Ruby code. +# yacc's $$ is the 'result', $0, $1... is +# an array called 'val', and $-1, $-2... is an array called '_values'. +# +# See the {Grammar File Reference}[rdoc-ref:lib/racc/rdoc/grammar.en.rdoc] for +# more information on grammar files. +# +# == Parser +# +# Then you must prepare the parse entry method. There are two types of +# parse methods in Racc, Racc::Parser#do_parse and Racc::Parser#yyparse +# +# Racc::Parser#do_parse is simple. +# +# It's yyparse() of yacc, and Racc::Parser#next_token is yylex(). +# This method must returns an array like [TOKENSYMBOL, ITS_VALUE]. +# EOF is [false, false]. +# (TOKENSYMBOL is a Ruby symbol (taken from String#intern) by default. +# If you want to change this, see the grammar reference. +# +# Racc::Parser#yyparse is little complicated, but useful. +# It does not use Racc::Parser#next_token, instead it gets tokens from any iterator. +# +# For example, yyparse(obj, :scan) causes +# calling +obj#scan+, and you can return tokens by yielding them from +obj#scan+. +# +# == Debugging +# +# When debugging, "-v" or/and the "-g" option is helpful. +# +# "-v" creates verbose log file (.output). +# "-g" creates a "Verbose Parser". +# Verbose Parser prints the internal status when parsing. +# But it's _not_ automatic. +# You must use -g option and set +@yydebug+ to +true+ in order to get output. +# -g option only creates the verbose parser. +# +# === Racc reported syntax error. +# +# Isn't there too many "end"? +# grammar of racc file is changed in v0.10. +# +# Racc does not use '%' mark, while yacc uses huge number of '%' marks.. +# +# === Racc reported "XXXX conflicts". +# +# Try "racc -v xxxx.y". +# It causes producing racc's internal log file, xxxx.output. +# +# === Generated parsers does not work correctly +# +# Try "racc -g xxxx.y". +# This command let racc generate "debugging parser". +# Then set @yydebug=true in your parser. +# It produces a working log of your parser. +# +# == Re-distributing Racc runtime +# +# A parser, which is created by Racc, requires the Racc runtime module; +# racc/parser.rb. +# +# Ruby 1.8.x comes with Racc runtime module, +# you need NOT distribute Racc runtime files. +# +# If you want to include the Racc runtime module with your parser. +# This can be done by using '-E' option: +# +# $ racc -E -omyparser.rb myparser.y +# +# This command creates myparser.rb which `includes' Racc runtime. +# Only you must do is to distribute your parser file (myparser.rb). +# +# Note: parser.rb is LGPL, but your parser is not. +# Your own parser is completely yours. +module Racc + + unless defined?(Racc_No_Extensions) + Racc_No_Extensions = false # :nodoc: + end + + class Parser + + Racc_Runtime_Version = '1.4.6' + Racc_Runtime_Revision = %w$originalRevision: 1.8 $[1] + + Racc_Runtime_Core_Version_R = '1.4.6' + Racc_Runtime_Core_Revision_R = %w$originalRevision: 1.8 $[1] + begin + require 'racc/cparse' + # Racc_Runtime_Core_Version_C = (defined in extension) + Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2] + unless new.respond_to?(:_racc_do_parse_c, true) + raise LoadError, 'old cparse.so' + end + if Racc_No_Extensions + raise LoadError, 'selecting ruby version of racc runtime core' + end + + Racc_Main_Parsing_Routine = :_racc_do_parse_c # :nodoc: + Racc_YY_Parse_Method = :_racc_yyparse_c # :nodoc: + Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C # :nodoc: + Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_C # :nodoc: + Racc_Runtime_Type = 'c' # :nodoc: + rescue LoadError + Racc_Main_Parsing_Routine = :_racc_do_parse_rb + Racc_YY_Parse_Method = :_racc_yyparse_rb + Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R + Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_R + Racc_Runtime_Type = 'ruby' + end + + def Parser.racc_runtime_type # :nodoc: + Racc_Runtime_Type + end + + def _racc_setup + @yydebug = false unless self.class::Racc_debug_parser + @yydebug = false unless defined?(@yydebug) + if @yydebug + @racc_debug_out = $stderr unless defined?(@racc_debug_out) + @racc_debug_out ||= $stderr + end + arg = self.class::Racc_arg + arg[13] = true if arg.size < 14 + arg + end + + def _racc_init_sysvars + @racc_state = [0] + @racc_tstack = [] + @racc_vstack = [] + + @racc_t = nil + @racc_val = nil + + @racc_read_next = true + + @racc_user_yyerror = false + @racc_error_status = 0 + end + + # The entry point of the parser. This method is used with #next_token. + # If Racc wants to get token (and its value), calls next_token. + # + # Example: + # def parse + # @q = [[1,1], + # [2,2], + # [3,3], + # [false, '$']] + # do_parse + # end + # + # def next_token + # @q.shift + # end + def do_parse + __send__(Racc_Main_Parsing_Routine, _racc_setup(), false) + end + + # The method to fetch next token. + # If you use #do_parse method, you must implement #next_token. + # + # The format of return value is [TOKEN_SYMBOL, VALUE]. + # +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT + # for 'IDENT'. ";" (String) for ';'. + # + # The final symbol (End of file) must be false. + def next_token + raise NotImplementedError, "#{self.class}\#next_token is not defined" + end + + def _racc_do_parse_rb(arg, in_debug) + action_table, action_check, action_default, action_pointer, + _, _, _, _, + _, _, token_table, _, + _, _, * = arg + + _racc_init_sysvars + tok = act = i = nil + + catch(:racc_end_parse) { + while true + if i = action_pointer[@racc_state[-1]] + if @racc_read_next + if @racc_t != 0 # not EOF + tok, @racc_val = next_token() + unless tok # EOF + @racc_t = 0 + else + @racc_t = (token_table[tok] or 1) # error token + end + racc_read_token(@racc_t, tok, @racc_val) if @yydebug + @racc_read_next = false + end + end + i += @racc_t + unless i >= 0 and + act = action_table[i] and + action_check[i] == @racc_state[-1] + act = action_default[@racc_state[-1]] + end + else + act = action_default[@racc_state[-1]] + end + while act = _racc_evalact(act, arg) + ; + end + end + } + end + + # Another entry point for the parser. + # If you use this method, you must implement RECEIVER#METHOD_ID method. + # + # RECEIVER#METHOD_ID is a method to get next token. + # It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE]. + def yyparse(recv, mid) + __send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), true) + end + + def _racc_yyparse_rb(recv, mid, arg, c_debug) + action_table, action_check, action_default, action_pointer, + _, _, _, _, + _, _, token_table, _, + _, _, * = arg + + _racc_init_sysvars + act = nil + i = nil + + catch(:racc_end_parse) { + until i = action_pointer[@racc_state[-1]] + while act = _racc_evalact(action_default[@racc_state[-1]], arg) + ; + end + end + recv.__send__(mid) do |tok, val| + unless tok + @racc_t = 0 + else + @racc_t = (token_table[tok] or 1) # error token + end + @racc_val = val + @racc_read_next = false + + i += @racc_t + unless i >= 0 and + act = action_table[i] and + action_check[i] == @racc_state[-1] + act = action_default[@racc_state[-1]] + end + while act = _racc_evalact(act, arg) + ; + end + + while not(i = action_pointer[@racc_state[-1]]) or + not @racc_read_next or + @racc_t == 0 # $ + unless i and i += @racc_t and + i >= 0 and + act = action_table[i] and + action_check[i] == @racc_state[-1] + act = action_default[@racc_state[-1]] + end + while act = _racc_evalact(act, arg) + ; + end + end + end + } + end + + ### + ### common + ### + + def _racc_evalact(act, arg) + action_table, action_check, _, action_pointer, + _, _, _, _, + _, _, _, shift_n, reduce_n, + _, _, * = arg + nerr = 0 # tmp + + if act > 0 and act < shift_n + # + # shift + # + if @racc_error_status > 0 + @racc_error_status -= 1 unless @racc_t == 1 # error token + end + @racc_vstack.push @racc_val + @racc_state.push act + @racc_read_next = true + if @yydebug + @racc_tstack.push @racc_t + racc_shift @racc_t, @racc_tstack, @racc_vstack + end + + elsif act < 0 and act > -reduce_n + # + # reduce + # + code = catch(:racc_jump) { + @racc_state.push _racc_do_reduce(arg, act) + false + } + if code + case code + when 1 # yyerror + @racc_user_yyerror = true # user_yyerror + return -reduce_n + when 2 # yyaccept + return shift_n + else + raise '[Racc Bug] unknown jump code' + end + end + + elsif act == shift_n + # + # accept + # + racc_accept if @yydebug + throw :racc_end_parse, @racc_vstack[0] + + elsif act == -reduce_n + # + # error + # + case @racc_error_status + when 0 + unless arg[21] # user_yyerror + nerr += 1 + on_error @racc_t, @racc_val, @racc_vstack + end + when 3 + if @racc_t == 0 # is $ + throw :racc_end_parse, nil + end + @racc_read_next = true + end + @racc_user_yyerror = false + @racc_error_status = 3 + while true + if i = action_pointer[@racc_state[-1]] + i += 1 # error token + if i >= 0 and + (act = action_table[i]) and + action_check[i] == @racc_state[-1] + break + end + end + throw :racc_end_parse, nil if @racc_state.size <= 1 + @racc_state.pop + @racc_vstack.pop + if @yydebug + @racc_tstack.pop + racc_e_pop @racc_state, @racc_tstack, @racc_vstack + end + end + return act + + else + raise "[Racc Bug] unknown action #{act.inspect}" + end + + racc_next_state(@racc_state[-1], @racc_state) if @yydebug + + nil + end + + def _racc_do_reduce(arg, act) + _, _, _, _, + goto_table, goto_check, goto_default, goto_pointer, + nt_base, reduce_table, _, _, + _, use_result, * = arg + state = @racc_state + vstack = @racc_vstack + tstack = @racc_tstack + + i = act * -3 + len = reduce_table[i] + reduce_to = reduce_table[i+1] + method_id = reduce_table[i+2] + void_array = [] + + tmp_t = tstack[-len, len] if @yydebug + tmp_v = vstack[-len, len] + tstack[-len, len] = void_array if @yydebug + vstack[-len, len] = void_array + state[-len, len] = void_array + + # tstack must be updated AFTER method call + if use_result + vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0]) + else + vstack.push __send__(method_id, tmp_v, vstack) + end + tstack.push reduce_to + + racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug + + k1 = reduce_to - nt_base + if i = goto_pointer[k1] + i += state[-1] + if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1 + return curstate + end + end + goto_default[k1] + end + + # This method is called when a parse error is found. + # + # ERROR_TOKEN_ID is an internal ID of token which caused error. + # You can get string representation of this ID by calling + # #token_to_str. + # + # ERROR_VALUE is a value of error token. + # + # value_stack is a stack of symbol values. + # DO NOT MODIFY this object. + # + # This method raises ParseError by default. + # + # If this method returns, parsers enter "error recovering mode". + def on_error(t, val, vstack) + raise ParseError, sprintf("\nparse error on value %s (%s)", + val.inspect, token_to_str(t) || '?') + end + + # Enter error recovering mode. + # This method does not call #on_error. + def yyerror + throw :racc_jump, 1 + end + + # Exit parser. + # Return value is Symbol_Value_Stack[0]. + def yyaccept + throw :racc_jump, 2 + end + + # Leave error recovering mode. + def yyerrok + @racc_error_status = 0 + end + + # For debugging output + def racc_read_token(t, tok, val) + @racc_debug_out.print 'read ' + @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') ' + @racc_debug_out.puts val.inspect + @racc_debug_out.puts + end + + def racc_shift(tok, tstack, vstack) + @racc_debug_out.puts "shift #{racc_token2str tok}" + racc_print_stacks tstack, vstack + @racc_debug_out.puts + end + + def racc_reduce(toks, sim, tstack, vstack) + out = @racc_debug_out + out.print 'reduce ' + if toks.empty? + out.print ' ' + else + toks.each {|t| out.print ' ', racc_token2str(t) } + end + out.puts " --> #{racc_token2str(sim)}" + + racc_print_stacks tstack, vstack + @racc_debug_out.puts + end + + def racc_accept + @racc_debug_out.puts 'accept' + @racc_debug_out.puts + end + + def racc_e_pop(state, tstack, vstack) + @racc_debug_out.puts 'error recovering mode: pop token' + racc_print_states state + racc_print_stacks tstack, vstack + @racc_debug_out.puts + end + + def racc_next_state(curstate, state) + @racc_debug_out.puts "goto #{curstate}" + racc_print_states state + @racc_debug_out.puts + end + + def racc_print_stacks(t, v) + out = @racc_debug_out + out.print ' [' + t.each_index do |i| + out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')' + end + out.puts ' ]' + end + + def racc_print_states(s) + out = @racc_debug_out + out.print ' [' + s.each {|st| out.print ' ', st } + out.puts ' ]' + end + + def racc_token2str(tok) + self.class::Racc_token_to_s_table[tok] or + raise "[Racc Bug] can't convert token #{tok} to string" + end + + # Convert internal ID of token symbol to the string. + def token_to_str(t) + self.class::Racc_token_to_s_table[t] + end + + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake.rb b/ruby/lib/ruby/2.1.0/rake.rb new file mode 100644 index 0000000..531cfc8 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake.rb @@ -0,0 +1,73 @@ +#-- + +# Copyright 2003-2010 by Jim Weirich (jim.weirich@gmail.com) +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +#++ + +require 'rake/version' + +# :stopdoc: +RAKEVERSION = Rake::VERSION +# :startdoc: + +require 'rbconfig' +require 'fileutils' +require 'singleton' +require 'monitor' +require 'optparse' +require 'ostruct' + +require 'rake/ext/module' +require 'rake/ext/string' +require 'rake/ext/time' + +require 'rake/win32' + +require 'rake/linked_list' +require 'rake/scope' +require 'rake/task_argument_error' +require 'rake/rule_recursion_overflow_error' +require 'rake/rake_module' +require 'rake/trace_output' +require 'rake/pseudo_status' +require 'rake/task_arguments' +require 'rake/invocation_chain' +require 'rake/task' +require 'rake/file_task' +require 'rake/file_creation_task' +require 'rake/multi_task' +require 'rake/dsl_definition' +require 'rake/file_utils_ext' +require 'rake/file_list' +require 'rake/default_loader' +require 'rake/early_time' +require 'rake/name_space' +require 'rake/task_manager' +require 'rake/application' +require 'rake/backtrace' + +$trace = false + +# :stopdoc: +# +# Some top level Constants. + +FileList = Rake::FileList +RakeFileUtils = Rake::FileUtilsExt diff --git a/ruby/lib/ruby/2.1.0/rake/alt_system.rb b/ruby/lib/ruby/2.1.0/rake/alt_system.rb new file mode 100644 index 0000000..a42597b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/alt_system.rb @@ -0,0 +1,108 @@ +# +# Copyright (c) 2008 James M. Lawrence +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# + +require 'rbconfig' + +# +# Alternate implementations of system() and backticks `` on Windows +# for ruby-1.8 and earlier. +# +module Rake::AltSystem + WINDOWS = RbConfig::CONFIG["host_os"] =~ + %r!(msdos|mswin|djgpp|mingw|[Ww]indows)! + + class << self + def define_module_function(name, &block) + define_method(name, &block) + module_function(name) + end + end + + if WINDOWS && RUBY_VERSION < "1.9.0" + RUNNABLE_EXTS = %w[com exe bat cmd] + RUNNABLE_PATTERN = %r!\.(#{RUNNABLE_EXTS.join('|')})\Z!i + + define_module_function :kernel_system, &Kernel.method(:system) + define_module_function :kernel_backticks, &Kernel.method(:'`') + + module_function + + def repair_command(cmd) + "call " + ( + if cmd =~ %r!\A\s*\".*?\"! + # already quoted + cmd + elsif match = cmd.match(%r!\A\s*(\S+)!) + if match[1] =~ %r!/! + # avoid x/y.bat interpretation as x with option /y + %Q!"#{match[1]}"! + match.post_match + else + # a shell command will fail if quoted + cmd + end + else + # empty or whitespace + cmd + end + ) + end + + def find_runnable(file) + if file =~ RUNNABLE_PATTERN + file + else + RUNNABLE_EXTS.each { |ext| + test = "#{file}.#{ext}" + return test if File.exist?(test) + } + nil + end + end + + def system(cmd, *args) + repaired = ( + if args.empty? + [repair_command(cmd)] + elsif runnable = find_runnable(cmd) + [File.expand_path(runnable), *args] + else + # non-existent file + [cmd, *args] + end + ) + kernel_system(*repaired) + end + + def backticks(cmd) + kernel_backticks(repair_command(cmd)) + end + + define_module_function :'`', &method(:backticks) + else + # Non-Windows or ruby-1.9+: same as Kernel versions + define_module_function :system, &Kernel.method(:system) + define_module_function :backticks, &Kernel.method(:'`') + define_module_function :'`', &Kernel.method(:'`') + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/application.rb b/ruby/lib/ruby/2.1.0/rake/application.rb new file mode 100644 index 0000000..b76244b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/application.rb @@ -0,0 +1,728 @@ +require 'shellwords' +require 'optparse' + +require 'rake/task_manager' +require 'rake/file_list' +require 'rake/thread_pool' +require 'rake/thread_history_display' +require 'rake/trace_output' +require 'rake/win32' + +module Rake + + CommandLineOptionError = Class.new(StandardError) + + ###################################################################### + # Rake main application object. When invoking +rake+ from the + # command line, a Rake::Application object is created and run. + # + class Application + include TaskManager + include TraceOutput + + # The name of the application (typically 'rake') + attr_reader :name + + # The original directory where rake was invoked. + attr_reader :original_dir + + # Name of the actual rakefile used. + attr_reader :rakefile + + # Number of columns on the terminal + attr_accessor :terminal_columns + + # List of the top level task names (task names from the command line). + attr_reader :top_level_tasks + + DEFAULT_RAKEFILES = [ + 'rakefile', + 'Rakefile', + 'rakefile.rb', + 'Rakefile.rb' + ].freeze + + # Initialize a Rake::Application object. + def initialize + super + @name = 'rake' + @rakefiles = DEFAULT_RAKEFILES.dup + @rakefile = nil + @pending_imports = [] + @imported = [] + @loaders = {} + @default_loader = Rake::DefaultLoader.new + @original_dir = Dir.pwd + @top_level_tasks = [] + add_loader('rb', DefaultLoader.new) + add_loader('rf', DefaultLoader.new) + add_loader('rake', DefaultLoader.new) + @tty_output = STDOUT.tty? + @terminal_columns = ENV['RAKE_COLUMNS'].to_i + end + + # Run the Rake application. The run method performs the following + # three steps: + # + # * Initialize the command line options (+init+). + # * Define the tasks (+load_rakefile+). + # * Run the top level tasks (+top_level+). + # + # If you wish to build a custom rake command, you should call + # +init+ on your application. Then define any tasks. Finally, + # call +top_level+ to run your top level tasks. + def run + standard_exception_handling do + init + load_rakefile + top_level + end + end + + # Initialize the command line parameters and app name. + def init(app_name='rake') + standard_exception_handling do + @name = app_name + handle_options + collect_tasks + end + end + + # Find the rakefile and then load it and any pending imports. + def load_rakefile + standard_exception_handling do + raw_load_rakefile + end + end + + # Run the top level tasks of a Rake application. + def top_level + run_with_threads do + if options.show_tasks + display_tasks_and_comments + elsif options.show_prereqs + display_prerequisites + else + top_level_tasks.each { |task_name| invoke_task(task_name) } + end + end + end + + # Run the given block with the thread startup and shutdown. + def run_with_threads + thread_pool.gather_history if options.job_stats == :history + + yield + + thread_pool.join + if options.job_stats + stats = thread_pool.statistics + puts "Maximum active threads: #{stats[:max_active_threads]}" + puts "Total threads in play: #{stats[:total_threads_in_play]}" + end + ThreadHistoryDisplay.new(thread_pool.history).show if + options.job_stats == :history + end + + # Add a loader to handle imported files ending in the extension + # +ext+. + def add_loader(ext, loader) + ext = ".#{ext}" unless ext =~ /^\./ + @loaders[ext] = loader + end + + # Application options from the command line + def options + @options ||= OpenStruct.new + end + + # Return the thread pool used for multithreaded processing. + def thread_pool # :nodoc: + @thread_pool ||= ThreadPool.new(options.thread_pool_size || FIXNUM_MAX) + end + + # private ---------------------------------------------------------------- + + def invoke_task(task_string) + name, args = parse_task_string(task_string) + t = self[name] + t.invoke(*args) + end + + def parse_task_string(string) + if string =~ /^([^\[]+)(\[(.*)\])$/ + name = $1 + args = $3.split(/\s*,\s*/) + else + name = string + args = [] + end + [name, args] + end + + # Provide standard exception handling for the given block. + def standard_exception_handling + yield + rescue SystemExit + # Exit silently with current status + raise + rescue OptionParser::InvalidOption => ex + $stderr.puts ex.message + exit(false) + rescue Exception => ex + # Exit with error message + display_error_message(ex) + exit_because_of_exception(ex) + end + + # Exit the program because of an unhandle exception. + # (may be overridden by subclasses) + def exit_because_of_exception(ex) + exit(false) + end + + # Display the error message that caused the exception. + def display_error_message(ex) + trace "#{name} aborted!" + trace ex.message + if options.backtrace + trace ex.backtrace.join("\n") + else + trace Backtrace.collapse(ex.backtrace).join("\n") + end + trace "Tasks: #{ex.chain}" if has_chain?(ex) + trace "(See full trace by running task with --trace)" unless + options.backtrace + end + + # Warn about deprecated usage. + # + # Example: + # Rake.application.deprecate("import", "Rake.import", caller.first) + # + def deprecate(old_usage, new_usage, call_site) + unless options.ignore_deprecate + $stderr.puts "WARNING: '#{old_usage}' is deprecated. " + + "Please use '#{new_usage}' instead.\n" + + " at #{call_site}" + end + end + + # Does the exception have a task invocation chain? + def has_chain?(exception) + exception.respond_to?(:chain) && exception.chain + end + private :has_chain? + + # True if one of the files in RAKEFILES is in the current directory. + # If a match is found, it is copied into @rakefile. + def have_rakefile + @rakefiles.each do |fn| + if File.exist?(fn) + others = FileList.glob(fn, File::FNM_CASEFOLD) + return others.size == 1 ? others.first : fn + elsif fn == '' + return fn + end + end + return nil + end + + # True if we are outputting to TTY, false otherwise + def tty_output? + @tty_output + end + + # Override the detected TTY output state (mostly for testing) + def tty_output=(tty_output_state) + @tty_output = tty_output_state + end + + # We will truncate output if we are outputting to a TTY or if we've been + # given an explicit column width to honor + def truncate_output? + tty_output? || @terminal_columns.nonzero? + end + + # Display the tasks and comments. + def display_tasks_and_comments + displayable_tasks = tasks.select { |t| + (options.show_all_tasks || t.comment) && + t.name =~ options.show_task_pattern + } + case options.show_tasks + when :tasks + width = displayable_tasks.map { |t| t.name_with_args.length }.max || 10 + if truncate_output? + max_column = terminal_width - name.size - width - 7 + else + max_column = nil + end + + displayable_tasks.each do |t| + printf("#{name} %-#{width}s # %s\n", + t.name_with_args, + max_column ? truncate(t.comment, max_column) : t.comment) + end + when :describe + displayable_tasks.each do |t| + puts "#{name} #{t.name_with_args}" + comment = t.full_comment || "" + comment.split("\n").each do |line| + puts " #{line}" + end + puts + end + when :lines + displayable_tasks.each do |t| + t.locations.each do |loc| + printf "#{name} %-30s %s\n", t.name_with_args, loc + end + end + else + fail "Unknown show task mode: '#{options.show_tasks}'" + end + end + + def terminal_width + if @terminal_columns.nonzero? + result = @terminal_columns + else + result = unix? ? dynamic_width : 80 + end + (result < 10) ? 80 : result + rescue + 80 + end + + # Calculate the dynamic width of the + def dynamic_width + @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput) + end + + def dynamic_width_stty + %x{stty size 2>/dev/null}.split[1].to_i + end + + def dynamic_width_tput + %x{tput cols 2>/dev/null}.to_i + end + + def unix? + RbConfig::CONFIG['host_os'] =~ + /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i + end + + def windows? + Win32.windows? + end + + def truncate(string, width) + if string.nil? + "" + elsif string.length <= width + string + else + (string[0, width - 3] || "") + "..." + end + end + + # Display the tasks and prerequisites + def display_prerequisites + tasks.each do |t| + puts "#{name} #{t.name}" + t.prerequisites.each { |pre| puts " #{pre}" } + end + end + + def trace(*strings) + options.trace_output ||= $stderr + trace_on(options.trace_output, *strings) + end + + def sort_options(options) + options.sort_by { |opt| + opt.select { |o| o =~ /^-/ }.map { |o| o.downcase }.sort.reverse + } + end + private :sort_options + + # A list of all the standard options used in rake, suitable for + # passing to OptionParser. + def standard_rake_options + sort_options( + [ + ['--all', '-A', + "Show all tasks, even uncommented ones", + lambda { |value| + options.show_all_tasks = value + } + ], + ['--backtrace=[OUT]', + "Enable full backtrace. OUT can be stderr (default) or stdout.", + lambda { |value| + options.backtrace = true + select_trace_output(options, 'backtrace', value) + } + ], + ['--comments', + "Show commented tasks only", + lambda { |value| + options.show_all_tasks = !value + } + ], + ['--describe', '-D [PATTERN]', + "Describe the tasks (matching optional PATTERN), then exit.", + lambda { |value| + select_tasks_to_show(options, :describe, value) + } + ], + ['--dry-run', '-n', + "Do a dry run without executing actions.", + lambda { |value| + Rake.verbose(true) + Rake.nowrite(true) + options.dryrun = true + options.trace = true + } + ], + ['--execute', '-e CODE', + "Execute some Ruby code and exit.", + lambda { |value| + eval(value) + exit + } + ], + ['--execute-print', '-p CODE', + "Execute some Ruby code, print the result, then exit.", + lambda { |value| + puts eval(value) + exit + } + ], + ['--execute-continue', '-E CODE', + "Execute some Ruby code, " + + "then continue with normal task processing.", + lambda { |value| eval(value) } + ], + ['--jobs', '-j [NUMBER]', + "Specifies the maximum number of tasks to execute in parallel. " + + "(default is 2)", + lambda { |value| + options.thread_pool_size = [(value || 2).to_i, 2].max + } + ], + ['--job-stats [LEVEL]', + "Display job statistics. " + + "LEVEL=history displays a complete job list", + lambda { |value| + if value =~ /^history/i + options.job_stats = :history + else + options.job_stats = true + end + } + ], + ['--libdir', '-I LIBDIR', + "Include LIBDIR in the search path for required modules.", + lambda { |value| $:.push(value) } + ], + ['--multitask', '-m', + "Treat all tasks as multitasks.", + lambda { |value| options.always_multitask = true } + ], + ['--no-search', '--nosearch', + '-N', "Do not search parent directories for the Rakefile.", + lambda { |value| options.nosearch = true } + ], + ['--prereqs', '-P', + "Display the tasks and dependencies, then exit.", + lambda { |value| options.show_prereqs = true } + ], + ['--quiet', '-q', + "Do not log messages to standard output.", + lambda { |value| Rake.verbose(false) } + ], + ['--rakefile', '-f [FILE]', + "Use FILE as the rakefile.", + lambda { |value| + value ||= '' + @rakefiles.clear + @rakefiles << value + } + ], + ['--rakelibdir', '--rakelib', '-R RAKELIBDIR', + "Auto-import any .rake files in RAKELIBDIR. " + + "(default is 'rakelib')", + lambda { |value| + options.rakelib = value.split(File::PATH_SEPARATOR) + } + ], + ['--require', '-r MODULE', + "Require MODULE before executing rakefile.", + lambda { |value| + begin + require value + rescue LoadError => ex + begin + rake_require value + rescue LoadError + raise ex + end + end + } + ], + ['--rules', + "Trace the rules resolution.", + lambda { |value| options.trace_rules = true } + ], + ['--silent', '-s', + "Like --quiet, but also suppresses the " + + "'in directory' announcement.", + lambda { |value| + Rake.verbose(false) + options.silent = true + } + ], + ['--suppress-backtrace PATTERN', + "Suppress backtrace lines matching regexp PATTERN. " + + "Ignored if --trace is on.", + lambda { |value| + options.suppress_backtrace_pattern = Regexp.new(value) + } + ], + ['--system', '-g', + "Using system wide (global) rakefiles " + + "(usually '~/.rake/*.rake').", + lambda { |value| options.load_system = true } + ], + ['--no-system', '--nosystem', '-G', + "Use standard project Rakefile search paths, " + + "ignore system wide rakefiles.", + lambda { |value| options.ignore_system = true } + ], + ['--tasks', '-T [PATTERN]', + "Display the tasks (matching optional PATTERN) " + + "with descriptions, then exit.", + lambda { |value| + select_tasks_to_show(options, :tasks, value) + } + ], + ['--trace=[OUT]', '-t', + "Turn on invoke/execute tracing, enable full backtrace. " + + "OUT can be stderr (default) or stdout.", + lambda { |value| + options.trace = true + options.backtrace = true + select_trace_output(options, 'trace', value) + Rake.verbose(true) + } + ], + ['--verbose', '-v', + "Log message to standard output.", + lambda { |value| Rake.verbose(true) } + ], + ['--version', '-V', + "Display the program version.", + lambda { |value| + puts "rake, version #{RAKEVERSION}" + exit + } + ], + ['--where', '-W [PATTERN]', + "Describe the tasks (matching optional PATTERN), then exit.", + lambda { |value| + select_tasks_to_show(options, :lines, value) + options.show_all_tasks = true + } + ], + ['--no-deprecation-warnings', '-X', + "Disable the deprecation warnings.", + lambda { |value| + options.ignore_deprecate = true + } + ], + ]) + end + + def select_tasks_to_show(options, show_tasks, value) + options.show_tasks = show_tasks + options.show_task_pattern = Regexp.new(value || '') + Rake::TaskManager.record_task_metadata = true + end + private :select_tasks_to_show + + def select_trace_output(options, trace_option, value) + value = value.strip unless value.nil? + case value + when 'stdout' + options.trace_output = $stdout + when 'stderr', nil + options.trace_output = $stderr + else + fail CommandLineOptionError, + "Unrecognized --#{trace_option} option '#{value}'" + end + end + private :select_trace_output + + # Read and handle the command line options. + def handle_options + options.rakelib = ['rakelib'] + options.trace_output = $stderr + + OptionParser.new do |opts| + opts.banner = "#{Rake.application.name} [-f rakefile] {options} targets..." + opts.separator "" + opts.separator "Options are ..." + + opts.on_tail("-h", "--help", "-H", "Display this help message.") do + puts opts + exit + end + + standard_rake_options.each { |args| opts.on(*args) } + opts.environment('RAKEOPT') + end.parse! + end + + # Similar to the regular Ruby +require+ command, but will check + # for *.rake files in addition to *.rb files. + def rake_require(file_name, paths=$LOAD_PATH, loaded=$") + fn = file_name + ".rake" + return false if loaded.include?(fn) + paths.each do |path| + full_path = File.join(path, fn) + if File.exist?(full_path) + Rake.load_rakefile(full_path) + loaded << fn + return true + end + end + fail LoadError, "Can't find #{file_name}" + end + + def find_rakefile_location + here = Dir.pwd + until (fn = have_rakefile) + Dir.chdir("..") + return nil if Dir.pwd == here || options.nosearch + here = Dir.pwd + end + [fn, here] + ensure + Dir.chdir(Rake.original_dir) + end + + def print_rakefile_directory(location) + $stderr.puts "(in #{Dir.pwd})" unless + options.silent or original_dir == location + end + + def raw_load_rakefile # :nodoc: + rakefile, location = find_rakefile_location + if (! options.ignore_system) && + (options.load_system || rakefile.nil?) && + system_dir && File.directory?(system_dir) + print_rakefile_directory(location) + glob("#{system_dir}/*.rake") do |name| + add_import name + end + else + fail "No Rakefile found (looking for: #{@rakefiles.join(', ')})" if + rakefile.nil? + @rakefile = rakefile + Dir.chdir(location) + print_rakefile_directory(location) + Rake.load_rakefile(File.expand_path(@rakefile)) if + @rakefile && @rakefile != '' + options.rakelib.each do |rlib| + glob("#{rlib}/*.rake") do |name| + add_import name + end + end + end + load_imports + end + + def glob(path, &block) + FileList.glob(path.gsub("\\", '/')).each(&block) + end + private :glob + + # The directory path containing the system wide rakefiles. + def system_dir + @system_dir ||= + begin + if ENV['RAKE_SYSTEM'] + ENV['RAKE_SYSTEM'] + else + standard_system_dir + end + end + end + + # The standard directory containing system wide rake files. + if Win32.windows? + def standard_system_dir #:nodoc: + Win32.win32_system_dir + end + else + def standard_system_dir #:nodoc: + File.join(File.expand_path('~'), '.rake') + end + end + private :standard_system_dir + + # Collect the list of tasks on the command line. If no tasks are + # given, return a list containing only the default task. + # Environmental assignments are processed at this time as well. + def collect_tasks + @top_level_tasks = [] + ARGV.each do |arg| + if arg =~ /^(\w+)=(.*)$/m + ENV[$1] = $2 + else + @top_level_tasks << arg unless arg =~ /^-/ + end + end + @top_level_tasks.push(default_task_name) if @top_level_tasks.empty? + end + + # Default task name ("default"). + # (May be overridden by subclasses) + def default_task_name + "default" + end + + # Add a file to the list of files to be imported. + def add_import(fn) + @pending_imports << fn + end + + # Load the pending list of imported files. + def load_imports + while fn = @pending_imports.shift + next if @imported.member?(fn) + fn_task = lookup(fn) and fn_task.invoke + ext = File.extname(fn) + loader = @loaders[ext] || @default_loader + loader.load(fn) + @imported << fn + end + end + + def rakefile_location(backtrace=caller) + backtrace.map { |t| t[/([^:]+):/, 1] } + + re = /^#{@rakefile}$/ + re = /#{re.source}/i if windows? + + backtrace.find { |str| str =~ re } || '' + end + + private + FIXNUM_MAX = (2**(0.size * 8 - 2) - 1) # :nodoc: + + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/backtrace.rb b/ruby/lib/ruby/2.1.0/rake/backtrace.rb new file mode 100644 index 0000000..9b2ba61 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/backtrace.rb @@ -0,0 +1,20 @@ +module Rake + module Backtrace + SYS_KEYS = RbConfig::CONFIG.keys.grep(/(prefix|libdir)/) + SYS_PATHS = RbConfig::CONFIG.values_at(*SYS_KEYS).uniq + + [ File.join(File.dirname(__FILE__), "..") ] + + SUPPRESSED_PATHS = SYS_PATHS. + map { |s| s.gsub("\\", "/") }. + map { |f| File.expand_path(f) }. + reject { |s| s.nil? || s =~ /^ *$/ } + SUPPRESSED_PATHS_RE = SUPPRESSED_PATHS.map { |f| Regexp.quote(f) }.join("|") + SUPPRESS_PATTERN = %r!(\A(#{SUPPRESSED_PATHS_RE})|bin/rake:\d+)!i + + def self.collapse(backtrace) + pattern = Rake.application.options.suppress_backtrace_pattern || + SUPPRESS_PATTERN + backtrace.reject { |elem| elem =~ pattern } + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/clean.rb b/ruby/lib/ruby/2.1.0/rake/clean.rb new file mode 100644 index 0000000..8001ce5 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/clean.rb @@ -0,0 +1,55 @@ +# The 'rake/clean' file defines two file lists (CLEAN and CLOBBER) and +# two rake tasks (:clean and :clobber). +# +# [:clean] Clean up the project by deleting scratch files and backup +# files. Add files to the CLEAN file list to have the :clean +# target handle them. +# +# [:clobber] Clobber all generated and non-source files in a project. +# The task depends on :clean, so all the clean files will +# be deleted as well as files in the CLOBBER file list. +# The intent of this task is to return a project to its +# pristine, just unpacked state. + +require 'rake' + +# :stopdoc: + +module Rake + module Cleaner + extend FileUtils + + module_function + + def cleanup_files(file_names) + file_names.each do |file_name| + cleanup(file_name) + end + end + + def cleanup(file_name, opts={}) + begin + rm_r file_name, opts + rescue StandardError => ex + puts "Failed to remove #{file_name}: #{ex}" + end + end + end +end + +CLEAN = ::Rake::FileList["**/*~", "**/*.bak", "**/core"] +CLEAN.clear_exclude.exclude { |fn| + fn.pathmap("%f").downcase == 'core' && File.directory?(fn) +} + +desc "Remove any temporary products." +task :clean do + Rake::Cleaner.cleanup_files(CLEAN) +end + +CLOBBER = ::Rake::FileList.new + +desc "Remove any generated file." +task :clobber => [:clean] do + Rake::Cleaner.cleanup_files(CLOBBER) +end diff --git a/ruby/lib/ruby/2.1.0/rake/cloneable.rb b/ruby/lib/ruby/2.1.0/rake/cloneable.rb new file mode 100644 index 0000000..ac67471 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/cloneable.rb @@ -0,0 +1,16 @@ +module Rake + # ########################################################################## + # Mixin for creating easily cloned objects. + # + module Cloneable + # The hook that invoked by 'clone' and 'dup' methods. + def initialize_copy(source) + super + source.instance_variables.each do |var| + src_value = source.instance_variable_get(var) + value = src_value.clone rescue src_value + instance_variable_set(var, value) + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/contrib/compositepublisher.rb b/ruby/lib/ruby/2.1.0/rake/contrib/compositepublisher.rb new file mode 100644 index 0000000..69952a0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/contrib/compositepublisher.rb @@ -0,0 +1,21 @@ +module Rake + + # Manage several publishers as a single entity. + class CompositePublisher + def initialize + @publishers = [] + end + + # Add a publisher to the composite. + def add(pub) + @publishers << pub + end + + # Upload all the individual publishers. + def upload + @publishers.each { |p| p.upload } + end + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rake/contrib/ftptools.rb b/ruby/lib/ruby/2.1.0/rake/contrib/ftptools.rb new file mode 100644 index 0000000..0dd50fd --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/contrib/ftptools.rb @@ -0,0 +1,139 @@ +# = Tools for FTP uploading. +# +# This file is still under development and is not released for general +# use. + +require 'date' +require 'net/ftp' +require 'rake/file_list' + +module Rake # :nodoc: + + #################################################################### + # Note: Not released for general use. + class FtpFile + attr_reader :name, :size, :owner, :group, :time + + def self.date + @date_class ||= Date + end + + def self.time + @time_class ||= Time + end + + def initialize(path, entry) + @path = path + @mode, _, @owner, @group, size, d1, d2, d3, @name = entry.split(' ') + @size = size.to_i + @time = determine_time(d1, d2, d3) + end + + def path + File.join(@path, @name) + end + + def directory? + @mode[0] == ?d + end + + def mode + parse_mode(@mode) + end + + def symlink? + @mode[0] == ?l + end + + private # -------------------------------------------------------- + + def parse_mode(m) + result = 0 + (1..9).each do |i| + result = 2 * result + ((m[i] == ?-) ? 0 : 1) + end + result + end + + def determine_time(d1, d2, d3) + now = self.class.time.now + if /:/ !~ d3 + result = Time.parse("#{d1} #{d2} #{d3}") + else + result = Time.parse("#{d1} #{d2} #{now.year} #{d3}") + result = Time.parse("#{d1} #{d2} #{now.year - 1} #{d3}") if + result > now + end + result + end + end + + #################################################################### + # Manage the uploading of files to an FTP account. + class FtpUploader + + # Log uploads to standard output when true. + attr_accessor :verbose + + class << FtpUploader + # Create an uploader and pass it to the given block as +up+. + # When the block is complete, close the uploader. + def connect(path, host, account, password) + up = self.new(path, host, account, password) + begin + yield(up) + ensure + up.close + end + end + end + + # Create an FTP uploader targeting the directory +path+ on +host+ + # using the given account and password. +path+ will be the root + # path of the uploader. + def initialize(path, host, account, password) + @created = Hash.new + @path = path + @ftp = Net::FTP.new(host, account, password) + makedirs(@path) + @ftp.chdir(@path) + end + + # Create the directory +path+ in the uploader root path. + def makedirs(path) + route = [] + File.split(path).each do |dir| + route << dir + current_dir = File.join(route) + if @created[current_dir].nil? + @created[current_dir] = true + $stderr.puts "Creating Directory #{current_dir}" if @verbose + @ftp.mkdir(current_dir) rescue nil + end + end + end + + # Upload all files matching +wildcard+ to the uploader's root + # path. + def upload_files(wildcard) + FileList.glob(wildcard).each do |fn| + upload(fn) + end + end + + # Close the uploader. + def close + @ftp.close + end + + private # -------------------------------------------------------- + + # Upload a single file to the uploader's root path. + def upload(file) + $stderr.puts "Uploading #{file}" if @verbose + dir = File.dirname(file) + makedirs(dir) + @ftp.putbinaryfile(file, file) unless File.directory?(file) + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/contrib/publisher.rb b/ruby/lib/ruby/2.1.0/rake/contrib/publisher.rb new file mode 100644 index 0000000..8b11edb --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/contrib/publisher.rb @@ -0,0 +1,73 @@ +# Copyright 2003-2010 by Jim Weirich (jim.weirich@gmail.com) +# All rights reserved. + +# :stopdoc: + +# Configuration information about an upload host system. +# name :: Name of host system. +# webdir :: Base directory for the web information for the +# application. The application name (APP) is appended to +# this directory before using. +# pkgdir :: Directory on the host system where packages can be +# placed. +HostInfo = Struct.new(:name, :webdir, :pkgdir) + +# :startdoc: + +# Manage several publishers as a single entity. +class CompositePublisher + def initialize + @publishers = [] + end + + # Add a publisher to the composite. + def add(pub) + @publishers << pub + end + + # Upload all the individual publishers. + def upload + @publishers.each { |p| p.upload } + end +end + +# Publish an entire directory to an existing remote directory using +# SSH. +class SshDirPublisher + def initialize(host, remote_dir, local_dir) + @host = host + @remote_dir = remote_dir + @local_dir = local_dir + end + + def upload + run %{scp -rq #{@local_dir}/* #{@host}:#{@remote_dir}} + end +end + +# Publish an entire directory to a fresh remote directory using SSH. +class SshFreshDirPublisher < SshDirPublisher + def upload + run %{ssh #{@host} rm -rf #{@remote_dir}} rescue nil + run %{ssh #{@host} mkdir #{@remote_dir}} + super + end +end + +# Publish a list of files to an existing remote directory. +class SshFilePublisher + # Create a publisher using the give host information. + def initialize(host, remote_dir, local_dir, *files) + @host = host + @remote_dir = remote_dir + @local_dir = local_dir + @files = files + end + + # Upload the local directory to the remote directory. + def upload + @files.each do |fn| + run %{scp -q #{@local_dir}/#{fn} #{@host}:#{@remote_dir}} + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/contrib/rubyforgepublisher.rb b/ruby/lib/ruby/2.1.0/rake/contrib/rubyforgepublisher.rb new file mode 100644 index 0000000..a4b9693 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/contrib/rubyforgepublisher.rb @@ -0,0 +1,16 @@ +require 'rake/contrib/sshpublisher' + +module Rake + + class RubyForgePublisher < SshDirPublisher + attr_reader :project, :proj_id, :user + + def initialize(projname, user) + super( + "#{user}@rubyforge.org", + "/var/www/gforge-projects/#{projname}", + "html") + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/contrib/sshpublisher.rb b/ruby/lib/ruby/2.1.0/rake/contrib/sshpublisher.rb new file mode 100644 index 0000000..bd6adc1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/contrib/sshpublisher.rb @@ -0,0 +1,50 @@ +require 'rake/dsl_definition' +require 'rake/contrib/compositepublisher' + +module Rake + + # Publish an entire directory to an existing remote directory using + # SSH. + class SshDirPublisher + include Rake::DSL + + def initialize(host, remote_dir, local_dir) + @host = host + @remote_dir = remote_dir + @local_dir = local_dir + end + + def upload + sh %{scp -rq #{@local_dir}/* #{@host}:#{@remote_dir}} + end + end + + # Publish an entire directory to a fresh remote directory using SSH. + class SshFreshDirPublisher < SshDirPublisher + def upload + sh %{ssh #{@host} rm -rf #{@remote_dir}} rescue nil + sh %{ssh #{@host} mkdir #{@remote_dir}} + super + end + end + + # Publish a list of files to an existing remote directory. + class SshFilePublisher + include Rake::DSL + + # Create a publisher using the give host information. + def initialize(host, remote_dir, local_dir, *files) + @host = host + @remote_dir = remote_dir + @local_dir = local_dir + @files = files + end + + # Upload the local directory to the remote directory. + def upload + @files.each do |fn| + sh %{scp -q #{@local_dir}/#{fn} #{@host}:#{@remote_dir}} + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/contrib/sys.rb b/ruby/lib/ruby/2.1.0/rake/contrib/sys.rb new file mode 100644 index 0000000..a3a9f69 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/contrib/sys.rb @@ -0,0 +1,2 @@ +fail "ERROR: 'rake/contrib/sys' is obsolete and no longer supported. " + + "Use 'FileUtils' instead." diff --git a/ruby/lib/ruby/2.1.0/rake/default_loader.rb b/ruby/lib/ruby/2.1.0/rake/default_loader.rb new file mode 100644 index 0000000..5dd3c05 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/default_loader.rb @@ -0,0 +1,10 @@ +module Rake + + # Default Rakefile loader used by +import+. + class DefaultLoader + def load(fn) + Rake.load_rakefile(File.expand_path(fn)) + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/dsl_definition.rb b/ruby/lib/ruby/2.1.0/rake/dsl_definition.rb new file mode 100644 index 0000000..b24a821 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/dsl_definition.rb @@ -0,0 +1,157 @@ +# Rake DSL functions. +require 'rake/file_utils_ext' + +module Rake + + ## + # DSL is a module that provides #task, #desc, #namespace, etc. Use this + # when you'd like to use rake outside the top level scope. + + module DSL + + #-- + # Include the FileUtils file manipulation functions in the top + # level module, but mark them private so that they don't + # unintentionally define methods on other objects. + #++ + + include FileUtilsExt + private(*FileUtils.instance_methods(false)) + private(*FileUtilsExt.instance_methods(false)) + + private + + # Declare a basic task. + # + # Example: + # task :clobber => [:clean] do + # rm_rf "html" + # end + # + def task(*args, &block) + Rake::Task.define_task(*args, &block) + end + + # Declare a file task. + # + # Example: + # file "config.cfg" => ["config.template"] do + # open("config.cfg", "w") do |outfile| + # open("config.template") do |infile| + # while line = infile.gets + # outfile.puts line + # end + # end + # end + # end + # + def file(*args, &block) + Rake::FileTask.define_task(*args, &block) + end + + # Declare a file creation task. + # (Mainly used for the directory command). + def file_create(*args, &block) + Rake::FileCreationTask.define_task(*args, &block) + end + + # Declare a set of files tasks to create the given directories on + # demand. + # + # Example: + # directory "testdata/doc" + # + def directory(*args, &block) + result = file_create(*args, &block) + dir, _ = *Rake.application.resolve_args(args) + Rake.each_dir_parent(dir) do |d| + file_create d do |t| + mkdir_p t.name unless File.exist?(t.name) + end + end + result + end + + # Declare a task that performs its prerequisites in + # parallel. Multitasks does *not* guarantee that its prerequisites + # will execute in any given order (which is obvious when you think + # about it) + # + # Example: + # multitask :deploy => [:deploy_gem, :deploy_rdoc] + # + def multitask(*args, &block) + Rake::MultiTask.define_task(*args, &block) + end + + # Create a new rake namespace and use it for evaluating the given + # block. Returns a NameSpace object that can be used to lookup + # tasks defined in the namespace. + # + # E.g. + # + # ns = namespace "nested" do + # task :run + # end + # task_run = ns[:run] # find :run in the given namespace. + # + def namespace(name=nil, &block) + name = name.to_s if name.kind_of?(Symbol) + name = name.to_str if name.respond_to?(:to_str) + unless name.kind_of?(String) || name.nil? + raise ArgumentError, "Expected a String or Symbol for a namespace name" + end + Rake.application.in_namespace(name, &block) + end + + # Declare a rule for auto-tasks. + # + # Example: + # rule '.o' => '.c' do |t| + # sh %{cc -o #{t.name} #{t.source}} + # end + # + def rule(*args, &block) + Rake::Task.create_rule(*args, &block) + end + + # Describe the next rake task. + # Duplicate descriptions are discarded. + # + # Example: + # desc "Run the Unit Tests" + # task :test => [:build] + # runtests + # end + # + def desc(description) + Rake.application.last_description = description + end + + # Import the partial Rakefiles +fn+. Imported files are loaded + # _after_ the current file is completely loaded. This allows the + # import statement to appear anywhere in the importing file, and yet + # allowing the imported files to depend on objects defined in the + # importing file. + # + # A common use of the import statement is to include files + # containing dependency declarations. + # + # See also the --rakelibdir command line option. + # + # Example: + # import ".depend", "my_rules" + # + def import(*fns) + fns.each do |fn| + Rake.application.add_import(fn) + end + end + end + extend FileUtilsExt +end + +# Extend the main object with the DSL commands. This allows top-level +# calls to task, etc. to work from a Rakefile without polluting the +# object inheritance tree. +self.extend Rake::DSL diff --git a/ruby/lib/ruby/2.1.0/rake/early_time.rb b/ruby/lib/ruby/2.1.0/rake/early_time.rb new file mode 100644 index 0000000..8c0e7d3 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/early_time.rb @@ -0,0 +1,18 @@ +module Rake + + # EarlyTime is a fake timestamp that occurs _before_ any other time value. + class EarlyTime + include Comparable + include Singleton + + def <=>(other) + -1 + end + + def to_s + "" + end + end + + EARLY = EarlyTime.instance +end diff --git a/ruby/lib/ruby/2.1.0/rake/ext/core.rb b/ruby/lib/ruby/2.1.0/rake/ext/core.rb new file mode 100644 index 0000000..c924c7e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/ext/core.rb @@ -0,0 +1,28 @@ +###################################################################### +# Core extension library +# +class Module + # Check for an existing method in the current class before extending. IF + # the method already exists, then a warning is printed and the extension is + # not added. Otherwise the block is yielded and any definitions in the + # block will take effect. + # + # Usage: + # + # class String + # rake_extension("xyz") do + # def xyz + # ... + # end + # end + # end + # + def rake_extension(method) + if method_defined?(method) + $stderr.puts "WARNING: Possible conflict with Rake extension: " + + "#{self}##{method} already exists" + else + yield + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/ext/module.rb b/ruby/lib/ruby/2.1.0/rake/ext/module.rb new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/ext/module.rb @@ -0,0 +1 @@ + diff --git a/ruby/lib/ruby/2.1.0/rake/ext/string.rb b/ruby/lib/ruby/2.1.0/rake/ext/string.rb new file mode 100644 index 0000000..07ef167 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/ext/string.rb @@ -0,0 +1,166 @@ +require 'rake/ext/core' + +###################################################################### +# Rake extension methods for String. +# +class String + + rake_extension("ext") do + # Replace the file extension with +newext+. If there is no extension on + # the string, append the new extension to the end. If the new extension + # is not given, or is the empty string, remove any existing extension. + # + # +ext+ is a user added method for the String class. + def ext(newext='') + return self.dup if ['.', '..'].include? self + newext = (newext =~ /^\./) ? newext : ("." + newext) if newext != '' + self.chomp(File.extname(self)) << newext + end + end + + rake_extension("pathmap") do + # Explode a path into individual components. Used by +pathmap+. + def pathmap_explode + head, tail = File.split(self) + return [self] if head == self + return [tail] if head == '.' || tail == '/' + return [head, tail] if head == '/' + return head.pathmap_explode + [tail] + end + protected :pathmap_explode + + # Extract a partial path from the path. Include +n+ directories from the + # front end (left hand side) if +n+ is positive. Include |+n+| + # directories from the back end (right hand side) if +n+ is negative. + def pathmap_partial(n) + dirs = File.dirname(self).pathmap_explode + partial_dirs = + if n > 0 + dirs[0...n] + elsif n < 0 + dirs.reverse[0...-n].reverse + else + "." + end + File.join(partial_dirs) + end + protected :pathmap_partial + + # Preform the pathmap replacement operations on the given path. The + # patterns take the form 'pat1,rep1;pat2,rep2...'. + def pathmap_replace(patterns, &block) + result = self + patterns.split(';').each do |pair| + pattern, replacement = pair.split(',') + pattern = Regexp.new(pattern) + if replacement == '*' && block_given? + result = result.sub(pattern, &block) + elsif replacement + result = result.sub(pattern, replacement) + else + result = result.sub(pattern, '') + end + end + result + end + protected :pathmap_replace + + # Map the path according to the given specification. The specification + # controls the details of the mapping. The following special patterns are + # recognized: + # + # * %p -- The complete path. + # * %f -- The base file name of the path, with its file extension, + # but without any directories. + # * %n -- The file name of the path without its file extension. + # * %d -- The directory list of the path. + # * %x -- The file extension of the path. An empty string if there + # is no extension. + # * %X -- Everything *but* the file extension. + # * %s -- The alternate file separator if defined, otherwise use + # the standard file separator. + # * %% -- A percent sign. + # + # The %d specifier can also have a numeric prefix (e.g. '%2d'). If the + # number is positive, only return (up to) +n+ directories in the path, + # starting from the left hand side. If +n+ is negative, return (up to) + # |+n+| directories from the right hand side of the path. + # + # Examples: + # + # 'a/b/c/d/file.txt'.pathmap("%2d") => 'a/b' + # 'a/b/c/d/file.txt'.pathmap("%-2d") => 'c/d' + # + # Also the %d, %p, %f, %n, %x, and %X operators can take a + # pattern/replacement argument to perform simple string substitutions on a + # particular part of the path. The pattern and replacement are separated + # by a comma and are enclosed by curly braces. The replacement spec comes + # after the % character but before the operator letter. (e.g. + # "%{old,new}d"). Multiple replacement specs should be separated by + # semi-colons (e.g. "%{old,new;src,bin}d"). + # + # Regular expressions may be used for the pattern, and back refs may be + # used in the replacement text. Curly braces, commas and semi-colons are + # excluded from both the pattern and replacement text (let's keep parsing + # reasonable). + # + # For example: + # + # "src/org/onestepback/proj/A.java".pathmap("%{^src,bin}X.class") + # + # returns: + # + # "bin/org/onestepback/proj/A.class" + # + # If the replacement text is '*', then a block may be provided to perform + # some arbitrary calculation for the replacement. + # + # For example: + # + # "/path/to/file.TXT".pathmap("%X%{.*,*}x") { |ext| + # ext.downcase + # } + # + # Returns: + # + # "/path/to/file.txt" + # + def pathmap(spec=nil, &block) + return self if spec.nil? + result = '' + spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag| + case frag + when '%f' + result << File.basename(self) + when '%n' + result << File.basename(self).ext + when '%d' + result << File.dirname(self) + when '%x' + result << File.extname(self) + when '%X' + result << self.ext + when '%p' + result << self + when '%s' + result << (File::ALT_SEPARATOR || File::SEPARATOR) + when '%-' + # do nothing + when '%%' + result << "%" + when /%(-?\d+)d/ + result << pathmap_partial($1.to_i) + when /^%\{([^}]*)\}(\d*[dpfnxX])/ + patterns, operator = $1, $2 + result << pathmap('%' + operator).pathmap_replace(patterns, &block) + when /^%/ + fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'" + else + result << frag + end + end + result + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/ext/time.rb b/ruby/lib/ruby/2.1.0/rake/ext/time.rb new file mode 100644 index 0000000..ea8b037 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/ext/time.rb @@ -0,0 +1,15 @@ +#-- +# Extensions to time to allow comparisons with an early time class. + +require 'rake/early_time' + +class Time + alias rake_original_time_compare :<=> + def <=>(other) + if Rake::EarlyTime === other + - other.<=>(self) + else + rake_original_time_compare(other) + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/file_creation_task.rb b/ruby/lib/ruby/2.1.0/rake/file_creation_task.rb new file mode 100644 index 0000000..c87e219 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/file_creation_task.rb @@ -0,0 +1,24 @@ +require 'rake/file_task' +require 'rake/early_time' + +module Rake + + # A FileCreationTask is a file task that when used as a dependency will be + # needed if and only if the file has not been created. Once created, it is + # not re-triggered if any of its dependencies are newer, nor does trigger + # any rebuilds of tasks that depend on it whenever it is updated. + # + class FileCreationTask < FileTask + # Is this file task needed? Yes if it doesn't exist. + def needed? + ! File.exist?(name) + end + + # Time stamp for file creation task. This time stamp is earlier + # than any other time stamp. + def timestamp + Rake::EARLY + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/file_list.rb b/ruby/lib/ruby/2.1.0/rake/file_list.rb new file mode 100644 index 0000000..0b60925 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/file_list.rb @@ -0,0 +1,416 @@ +require 'rake/cloneable' +require 'rake/file_utils_ext' +require 'rake/pathmap' + +###################################################################### +module Rake + + # ######################################################################### + # A FileList is essentially an array with a few helper methods defined to + # make file manipulation a bit easier. + # + # FileLists are lazy. When given a list of glob patterns for possible files + # to be included in the file list, instead of searching the file structures + # to find the files, a FileList holds the pattern for latter use. + # + # This allows us to define a number of FileList to match any number of + # files, but only search out the actual files when then FileList itself is + # actually used. The key is that the first time an element of the + # FileList/Array is requested, the pending patterns are resolved into a real + # list of file names. + # + class FileList + + include Cloneable + + # == Method Delegation + # + # The lazy evaluation magic of FileLists happens by implementing all the + # array specific methods to call +resolve+ before delegating the heavy + # lifting to an embedded array object (@items). + # + # In addition, there are two kinds of delegation calls. The regular kind + # delegates to the @items array and returns the result directly. Well, + # almost directly. It checks if the returned value is the @items object + # itself, and if so will return the FileList object instead. + # + # The second kind of delegation call is used in methods that normally + # return a new Array object. We want to capture the return value of these + # methods and wrap them in a new FileList object. We enumerate these + # methods in the +SPECIAL_RETURN+ list below. + + # List of array methods (that are not in +Object+) that need to be + # delegated. + ARRAY_METHODS = (Array.instance_methods - Object.instance_methods). + map { |n| n.to_s } + + # List of additional methods that must be delegated. + MUST_DEFINE = %w[inspect <=>] + + # List of methods that should not be delegated here (we define special + # versions of them explicitly below). + MUST_NOT_DEFINE = %w[to_a to_ary partition *] + + # List of delegated methods that return new array values which need + # wrapping. + SPECIAL_RETURN = %w[ + map collect sort sort_by select find_all reject grep + compact flatten uniq values_at + + - & | + ] + + DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE). + map { |s| s.to_s }.sort.uniq + + # Now do the delegation. + DELEGATING_METHODS.each do |sym| + if SPECIAL_RETURN.include?(sym) + ln = __LINE__ + 1 + class_eval %{ + def #{sym}(*args, &block) + resolve + result = @items.send(:#{sym}, *args, &block) + FileList.new.import(result) + end + }, __FILE__, ln + else + ln = __LINE__ + 1 + class_eval %{ + def #{sym}(*args, &block) + resolve + result = @items.send(:#{sym}, *args, &block) + result.object_id == @items.object_id ? self : result + end + }, __FILE__, ln + end + end + + # Create a file list from the globbable patterns given. If you wish to + # perform multiple includes or excludes at object build time, use the + # "yield self" pattern. + # + # Example: + # file_list = FileList.new('lib/**/*.rb', 'test/test*.rb') + # + # pkg_files = FileList.new('lib/**/*') do |fl| + # fl.exclude(/\bCVS\b/) + # end + # + def initialize(*patterns) + @pending_add = [] + @pending = false + @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup + @exclude_procs = DEFAULT_IGNORE_PROCS.dup + @items = [] + patterns.each { |pattern| include(pattern) } + yield self if block_given? + end + + # Add file names defined by glob patterns to the file list. If an array + # is given, add each element of the array. + # + # Example: + # file_list.include("*.java", "*.cfg") + # file_list.include %w( math.c lib.h *.o ) + # + def include(*filenames) + # TODO: check for pending + filenames.each do |fn| + if fn.respond_to? :to_ary + include(*fn.to_ary) + else + @pending_add << fn + end + end + @pending = true + self + end + alias :add :include + + # Register a list of file name patterns that should be excluded from the + # list. Patterns may be regular expressions, glob patterns or regular + # strings. In addition, a block given to exclude will remove entries that + # return true when given to the block. + # + # Note that glob patterns are expanded against the file system. If a file + # is explicitly added to a file list, but does not exist in the file + # system, then an glob pattern in the exclude list will not exclude the + # file. + # + # Examples: + # FileList['a.c', 'b.c'].exclude("a.c") => ['b.c'] + # FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c'] + # + # If "a.c" is a file, then ... + # FileList['a.c', 'b.c'].exclude("a.*") => ['b.c'] + # + # If "a.c" is not a file, then ... + # FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c'] + # + def exclude(*patterns, &block) + patterns.each do |pat| + @exclude_patterns << pat + end + @exclude_procs << block if block_given? + resolve_exclude unless @pending + self + end + + + # Clear all the exclude patterns so that we exclude nothing. + def clear_exclude + @exclude_patterns = [] + @exclude_procs = [] + self + end + + # Define equality. + def ==(array) + to_ary == array + end + + # Return the internal array object. + def to_a + resolve + @items + end + + # Return the internal array object. + def to_ary + to_a + end + + # Lie about our class. + def is_a?(klass) + klass == Array || super(klass) + end + alias kind_of? is_a? + + # Redefine * to return either a string or a new file list. + def *(other) + result = @items * other + case result + when Array + FileList.new.import(result) + else + result + end + end + + # Resolve all the pending adds now. + def resolve + if @pending + @pending = false + @pending_add.each do |fn| resolve_add(fn) end + @pending_add = [] + resolve_exclude + end + self + end + + def resolve_add(fn) + case fn + when %r{[*?\[\{]} + add_matching(fn) + else + self << fn + end + end + private :resolve_add + + def resolve_exclude + reject! { |fn| excluded_from_list?(fn) } + self + end + private :resolve_exclude + + # Return a new FileList with the results of running +sub+ against each + # element of the original list. + # + # Example: + # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o'] + # + def sub(pat, rep) + inject(FileList.new) { |res, fn| res << fn.sub(pat, rep) } + end + + # Return a new FileList with the results of running +gsub+ against each + # element of the original list. + # + # Example: + # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\") + # => ['lib\\test\\file', 'x\\y'] + # + def gsub(pat, rep) + inject(FileList.new) { |res, fn| res << fn.gsub(pat, rep) } + end + + # Same as +sub+ except that the original file list is modified. + def sub!(pat, rep) + each_with_index { |fn, i| self[i] = fn.sub(pat, rep) } + self + end + + # Same as +gsub+ except that the original file list is modified. + def gsub!(pat, rep) + each_with_index { |fn, i| self[i] = fn.gsub(pat, rep) } + self + end + + # Apply the pathmap spec to each of the included file names, returning a + # new file list with the modified paths. (See String#pathmap for + # details.) + def pathmap(spec=nil) + collect { |fn| fn.pathmap(spec) } + end + + # Return a new FileList with String#ext method applied to + # each member of the array. + # + # This method is a shortcut for: + # + # array.collect { |item| item.ext(newext) } + # + # +ext+ is a user added method for the Array class. + def ext(newext='') + collect { |fn| fn.ext(newext) } + end + + + # Grep each of the files in the filelist using the given pattern. If a + # block is given, call the block on each matching line, passing the file + # name, line number, and the matching line of text. If no block is given, + # a standard emacs style file:linenumber:line message will be printed to + # standard out. Returns the number of matched items. + def egrep(pattern, *options) + matched = 0 + each do |fn| + begin + open(fn, "r", *options) do |inf| + count = 0 + inf.each do |line| + count += 1 + if pattern.match(line) + matched += 1 + if block_given? + yield fn, count, line + else + puts "#{fn}:#{count}:#{line}" + end + end + end + end + rescue StandardError => ex + $stderr.puts "Error while processing '#{fn}': #{ex}" + end + end + matched + end + + # Return a new file list that only contains file names from the current + # file list that exist on the file system. + def existing + select { |fn| File.exist?(fn) } + end + + # Modify the current file list so that it contains only file name that + # exist on the file system. + def existing! + resolve + @items = @items.select { |fn| File.exist?(fn) } + self + end + + # FileList version of partition. Needed because the nested arrays should + # be FileLists in this version. + def partition(&block) # :nodoc: + resolve + result = @items.partition(&block) + [ + FileList.new.import(result[0]), + FileList.new.import(result[1]), + ] + end + + # Convert a FileList to a string by joining all elements with a space. + def to_s + resolve + self.join(' ') + end + + # Add matching glob patterns. + def add_matching(pattern) + FileList.glob(pattern).each do |fn| + self << fn unless excluded_from_list?(fn) + end + end + private :add_matching + + # Should the given file name be excluded from the list? + # + # NOTE: This method was formally named "exclude?", but Rails + # introduced an exclude? method as an array method and setup a + # conflict with file list. We renamed the method to avoid + # confusion. If you were using "FileList#exclude?" in your user + # code, you will need to update. + def excluded_from_list?(fn) + return true if @exclude_patterns.any? do |pat| + case pat + when Regexp + fn =~ pat + when /[*?]/ + File.fnmatch?(pat, fn, File::FNM_PATHNAME) + else + fn == pat + end + end + @exclude_procs.any? { |p| p.call(fn) } + end + + DEFAULT_IGNORE_PATTERNS = [ + /(^|[\/\\])CVS([\/\\]|$)/, + /(^|[\/\\])\.svn([\/\\]|$)/, + /\.bak$/, + /~$/ + ] + DEFAULT_IGNORE_PROCS = [ + proc { |fn| fn =~ /(^|[\/\\])core$/ && ! File.directory?(fn) } + ] + + def import(array) + @items = array + self + end + + class << self + # Create a new file list including the files listed. Similar to: + # + # FileList.new(*args) + def [](*args) + new(*args) + end + + # Get a sorted list of files matching the pattern. This method + # should be prefered to Dir[pattern] and Dir.glob(pattern) because + # the files returned are guaranteed to be sorted. + def glob(pattern, *args) + Dir.glob(pattern, *args).sort + end + end + end +end + +module Rake + class << self + + # Yield each file or directory component. + def each_dir_parent(dir) # :nodoc: + old_length = nil + while dir != '.' && dir.length != old_length + yield(dir) + old_length = dir.length + dir = File.dirname(dir) + end + end + end +end # module Rake diff --git a/ruby/lib/ruby/2.1.0/rake/file_task.rb b/ruby/lib/ruby/2.1.0/rake/file_task.rb new file mode 100644 index 0000000..3e717c2 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/file_task.rb @@ -0,0 +1,46 @@ +require 'rake/task.rb' +require 'rake/early_time' + +module Rake + # ######################################################################### + # A FileTask is a task that includes time based dependencies. If any of a + # FileTask's prerequisites have a timestamp that is later than the file + # represented by this task, then the file must be rebuilt (using the + # supplied actions). + # + class FileTask < Task + + # Is this file task needed? Yes if it doesn't exist, or if its time stamp + # is out of date. + def needed? + ! File.exist?(name) || out_of_date?(timestamp) + end + + # Time stamp for file task. + def timestamp + if File.exist?(name) + File.mtime(name.to_s) + else + Rake::EARLY + end + end + + private + + # Are there any prerequisites with a later time than the given time stamp? + def out_of_date?(stamp) + @prerequisites.any? { |n| application[n, @scope].timestamp > stamp } + end + + # ---------------------------------------------------------------- + # Task class methods. + # + class << self + # Apply the scope to the task name according to the rules for this kind + # of task. File based tasks ignore the scope when creating the name. + def scope_name(scope, task_name) + task_name + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/file_utils.rb b/ruby/lib/ruby/2.1.0/rake/file_utils.rb new file mode 100644 index 0000000..0f7f459 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/file_utils.rb @@ -0,0 +1,116 @@ +require 'rbconfig' +require 'fileutils' + +#-- +# This a FileUtils extension that defines several additional commands to be +# added to the FileUtils utility functions. +module FileUtils + # Path to the currently running Ruby program + RUBY = ENV['RUBY'] || File.join( + RbConfig::CONFIG['bindir'], + RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT']). + sub(/.*\s.*/m, '"\&"') + + OPT_TABLE['sh'] = %w(noop verbose) + OPT_TABLE['ruby'] = %w(noop verbose) + + # Run the system command +cmd+. If multiple arguments are given the command + # is not run with the shell (same semantics as Kernel::exec and + # Kernel::system). + # + # Example: + # sh %{ls -ltr} + # + # sh 'ls', 'file with spaces' + # + # # check exit status after command runs + # sh %{grep pattern file} do |ok, res| + # if ! ok + # puts "pattern not found (status = #{res.exitstatus})" + # end + # end + # + def sh(*cmd, &block) + options = (Hash === cmd.last) ? cmd.pop : {} + shell_runner = block_given? ? block : create_shell_runner(cmd) + set_verbose_option(options) + options[:noop] ||= Rake::FileUtilsExt.nowrite_flag + Rake.rake_check_options options, :noop, :verbose + Rake.rake_output_message cmd.join(" ") if options[:verbose] + + unless options[:noop] + res = rake_system(*cmd) + status = $? + status = Rake::PseudoStatus.new(1) if !res && status.nil? + shell_runner.call(res, status) + end + end + + def create_shell_runner(cmd) # :nodoc: + show_command = cmd.join(" ") + show_command = show_command[0, 42] + "..." unless $trace + lambda do |ok, status| + ok or + fail "Command failed with status (#{status.exitstatus}): " + + "[#{show_command}]" + end + end + private :create_shell_runner + + def set_verbose_option(options) # :nodoc: + unless options.key? :verbose + options[:verbose] = + (Rake::FileUtilsExt.verbose_flag == Rake::FileUtilsExt::DEFAULT) || + Rake::FileUtilsExt.verbose_flag + end + end + private :set_verbose_option + + def rake_system(*cmd) # :nodoc: + Rake::AltSystem.system(*cmd) + end + private :rake_system + + # Run a Ruby interpreter with the given arguments. + # + # Example: + # ruby %{-pe '$_.upcase!' 1 + sh(*([RUBY] + args + [options]), &block) + else + sh("#{RUBY} #{args.first}", options, &block) + end + end + + LN_SUPPORTED = [true] + + # Attempt to do a normal file link, but fall back to a copy if the link + # fails. + def safe_ln(*args) + if ! LN_SUPPORTED[0] + cp(*args) + else + begin + ln(*args) + rescue StandardError, NotImplementedError + LN_SUPPORTED[0] = false + cp(*args) + end + end + end + + # Split a file path into individual directory names. + # + # Example: + # split_all("a/b/c") => ['a', 'b', 'c'] + # + def split_all(path) + head, tail = File.split(path) + return [tail] if head == '.' || tail == '/' + return [head, tail] if head == '/' + return split_all(head) + [tail] + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/file_utils_ext.rb b/ruby/lib/ruby/2.1.0/rake/file_utils_ext.rb new file mode 100644 index 0000000..309159a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/file_utils_ext.rb @@ -0,0 +1,144 @@ +require 'rake/file_utils' + +module Rake + # + # FileUtilsExt provides a custom version of the FileUtils methods + # that respond to the verbose and nowrite + # commands. + # + module FileUtilsExt + include FileUtils + + class << self + attr_accessor :verbose_flag, :nowrite_flag + end + + DEFAULT = Object.new + + FileUtilsExt.verbose_flag = DEFAULT + FileUtilsExt.nowrite_flag = false + + FileUtils.commands.each do |name| + opts = FileUtils.options_of name + default_options = [] + if opts.include?("verbose") + default_options << ':verbose => FileUtilsExt.verbose_flag' + end + if opts.include?("noop") + default_options << ':noop => FileUtilsExt.nowrite_flag' + end + + next if default_options.empty? + module_eval(<<-EOS, __FILE__, __LINE__ + 1) + def #{name}( *args, &block ) + super( + *rake_merge_option(args, + #{default_options.join(', ')} + ), &block) + end + EOS + end + + # Get/set the verbose flag controlling output from the FileUtils + # utilities. If verbose is true, then the utility method is + # echoed to standard output. + # + # Examples: + # verbose # return the current value of the + # # verbose flag + # verbose(v) # set the verbose flag to _v_. + # verbose(v) { code } # Execute code with the verbose flag set + # # temporarily to _v_. Return to the + # # original value when code is done. + def verbose(value=nil) + oldvalue = FileUtilsExt.verbose_flag + FileUtilsExt.verbose_flag = value unless value.nil? + if block_given? + begin + yield + ensure + FileUtilsExt.verbose_flag = oldvalue + end + end + FileUtilsExt.verbose_flag + end + + # Get/set the nowrite flag controlling output from the FileUtils + # utilities. If verbose is true, then the utility method is + # echoed to standard output. + # + # Examples: + # nowrite # return the current value of the + # # nowrite flag + # nowrite(v) # set the nowrite flag to _v_. + # nowrite(v) { code } # Execute code with the nowrite flag set + # # temporarily to _v_. Return to the + # # original value when code is done. + def nowrite(value=nil) + oldvalue = FileUtilsExt.nowrite_flag + FileUtilsExt.nowrite_flag = value unless value.nil? + if block_given? + begin + yield + ensure + FileUtilsExt.nowrite_flag = oldvalue + end + end + oldvalue + end + + # Use this function to prevent potentially destructive ruby code + # from running when the :nowrite flag is set. + # + # Example: + # + # when_writing("Building Project") do + # project.build + # end + # + # The following code will build the project under normal + # conditions. If the nowrite(true) flag is set, then the example + # will print: + # + # DRYRUN: Building Project + # + # instead of actually building the project. + # + def when_writing(msg=nil) + if FileUtilsExt.nowrite_flag + $stderr.puts "DRYRUN: #{msg}" if msg + else + yield + end + end + + # Merge the given options with the default values. + def rake_merge_option(args, defaults) + if Hash === args.last + defaults.update(args.last) + args.pop + end + args.push defaults + args + end + + # Send the message to the default rake output (which is $stderr). + def rake_output_message(message) + $stderr.puts(message) + end + + # Check that the options do not contain options not listed in + # +optdecl+. An ArgumentError exception is thrown if non-declared + # options are found. + def rake_check_options(options, *optdecl) + h = options.dup + optdecl.each do |name| + h.delete name + end + raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless + h.empty? + end + + extend self + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/gempackagetask.rb b/ruby/lib/ruby/2.1.0/rake/gempackagetask.rb new file mode 100644 index 0000000..4ace0a6 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/gempackagetask.rb @@ -0,0 +1,2 @@ +fail "ERROR: 'rake/gempackagetask' is obsolete and no longer supported. " + + "Use 'rubygems/packagetask' instead." diff --git a/ruby/lib/ruby/2.1.0/rake/invocation_chain.rb b/ruby/lib/ruby/2.1.0/rake/invocation_chain.rb new file mode 100644 index 0000000..dae9a35 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/invocation_chain.rb @@ -0,0 +1,57 @@ +module Rake + + #################################################################### + # InvocationChain tracks the chain of task invocations to detect + # circular dependencies. + class InvocationChain < LinkedList + + # Is the invocation already in the chain? + def member?(invocation) + head == invocation || tail.member?(invocation) + end + + # Append an invocation to the chain of invocations. It is an error + # if the invocation already listed. + def append(invocation) + if member?(invocation) + fail RuntimeError, "Circular dependency detected: #{to_s} => #{invocation}" + end + conj(invocation) + end + + # Convert to string, ie: TOP => invocation => invocation + def to_s + "#{prefix}#{head}" + end + + # Class level append. + def self.append(invocation, chain) + chain.append(invocation) + end + + private + + def prefix + "#{tail.to_s} => " + end + + # Null object for an empty chain. + class EmptyInvocationChain < LinkedList::EmptyLinkedList + @parent = InvocationChain + + def member?(obj) + false + end + + def append(invocation) + conj(invocation) + end + + def to_s + "TOP" + end + end + + EMPTY = EmptyInvocationChain.new + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/invocation_exception_mixin.rb b/ruby/lib/ruby/2.1.0/rake/invocation_exception_mixin.rb new file mode 100644 index 0000000..84ff335 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/invocation_exception_mixin.rb @@ -0,0 +1,16 @@ +module Rake + module InvocationExceptionMixin + # Return the invocation chain (list of Rake tasks) that were in + # effect when this exception was detected by rake. May be null if + # no tasks were active. + def chain + @rake_invocation_chain ||= nil + end + + # Set the invocation chain in effect when this exception was + # detected. + def chain=(value) + @rake_invocation_chain = value + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/lib/project.rake b/ruby/lib/ruby/2.1.0/rake/lib/project.rake new file mode 100644 index 0000000..a549732 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/lib/project.rake @@ -0,0 +1,21 @@ +task "create:project" => ["lib", "test", "Rakefile"] + +directory "lib" +directory "test" + +file "Rakefile" do + File.open("Rakefile", "w") do |out| + out.puts %{# -*- ruby -*- + +require 'rake/clean' +require 'rake/testtask' + +task :default => :test + +Rake::TestTask.new do |t| + t.verbose = false + t.test_files = FileList['test/test_*.rb'] +end +} + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/linked_list.rb b/ruby/lib/ruby/2.1.0/rake/linked_list.rb new file mode 100644 index 0000000..2648370 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/linked_list.rb @@ -0,0 +1,103 @@ +module Rake + + # Polylithic linked list structure used to implement several data + # structures in Rake. + class LinkedList + include Enumerable + + attr_reader :head, :tail + + def initialize(head, tail=EMPTY) + @head = head + @tail = tail + end + + # Polymorphically add a new element to the head of a list. The + # type of head node will be the same list type has the tail. + def conj(item) + self.class.cons(item, self) + end + + # Is the list empty? + def empty? + false + end + + # Lists are structurally equivalent. + def ==(other) + current = self + while ! current.empty? && ! other.empty? + return false if current.head != other.head + current = current.tail + other = other.tail + end + current.empty? && other.empty? + end + + # Convert to string: LL(item, item...) + def to_s + items = map { |item| item.to_s }.join(", ") + "LL(#{items})" + end + + # Same as +to_s+, but with inspected items. + def inspect + items = map { |item| item.inspect }.join(", ") + "LL(#{items})" + end + + # For each item in the list. + def each + current = self + while ! current.empty? + yield(current.head) + current = current.tail + end + self + end + + # Make a list out of the given arguments. This method is + # polymorphic + def self.make(*args) + result = empty + args.reverse_each do |item| + result = cons(item, result) + end + result + end + + # Cons a new head onto the tail list. + def self.cons(head, tail) + new(head, tail) + end + + # The standard empty list class for the given LinkedList class. + def self.empty + self::EMPTY + end + + # Represent an empty list, using the Null Object Pattern. + # + # When inheriting from the LinkedList class, you should implement + # a type specific Empty class as well. Make sure you set the class + # instance variable @parent to the assocated list class (this + # allows conj, cons and make to work polymorphically). + class EmptyLinkedList < LinkedList + @parent = LinkedList + + def initialize + end + + def empty? + true + end + + def self.cons(head, tail) + @parent.cons(head, tail) + end + end + + EMPTY = EmptyLinkedList.new + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/loaders/makefile.rb b/ruby/lib/ruby/2.1.0/rake/loaders/makefile.rb new file mode 100644 index 0000000..4ece432 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/loaders/makefile.rb @@ -0,0 +1,40 @@ +module Rake + + # Makefile loader to be used with the import file loader. + class MakefileLoader + include Rake::DSL + + SPACE_MARK = "\0" + + # Load the makefile dependencies in +fn+. + def load(fn) + lines = File.read fn + lines.gsub!(/\\ /, SPACE_MARK) + lines.gsub!(/#[^\n]*\n/m, "") + lines.gsub!(/\\\n/, ' ') + lines.each_line do |line| + process_line(line) + end + end + + private + + # Process one logical line of makefile data. + def process_line(line) + file_tasks, args = line.split(':', 2) + return if args.nil? + dependents = args.split.map { |d| respace(d) } + file_tasks.scan(/\S+/) do |file_task| + file_task = respace(file_task) + file file_task => dependents + end + end + + def respace(str) + str.tr SPACE_MARK, ' ' + end + end + + # Install the handler + Rake.application.add_loader('mf', MakefileLoader.new) +end diff --git a/ruby/lib/ruby/2.1.0/rake/multi_task.rb b/ruby/lib/ruby/2.1.0/rake/multi_task.rb new file mode 100644 index 0000000..5418a7a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/multi_task.rb @@ -0,0 +1,13 @@ +module Rake + + # Same as a regular task, but the immediate prerequisites are done in + # parallel using Ruby threads. + # + class MultiTask < Task + private + def invoke_prerequisites(task_args, invocation_chain) # :nodoc: + invoke_prerequisites_concurrently(task_args, invocation_chain) + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/name_space.rb b/ruby/lib/ruby/2.1.0/rake/name_space.rb new file mode 100644 index 0000000..e1cc094 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/name_space.rb @@ -0,0 +1,25 @@ +module Rake + + # The NameSpace class will lookup task names in the the scope + # defined by a +namespace+ command. + # + class NameSpace + + # Create a namespace lookup object using the given task manager + # and the list of scopes. + def initialize(task_manager, scope_list) + @task_manager = task_manager + @scope = scope_list.dup + end + + # Lookup a task named +name+ in the namespace. + def [](name) + @task_manager.lookup(name, @scope) + end + + # Return the list of tasks defined in this and nested namespaces. + def tasks + @task_manager.tasks_in_scope(@scope) + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/packagetask.rb b/ruby/lib/ruby/2.1.0/rake/packagetask.rb new file mode 100644 index 0000000..029caa6 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/packagetask.rb @@ -0,0 +1,190 @@ +# Define a package task library to aid in the definition of +# redistributable package files. + +require 'rake' +require 'rake/tasklib' + +module Rake + + # Create a packaging task that will package the project into + # distributable files (e.g zip archive or tar files). + # + # The PackageTask will create the following targets: + # + # [:package] + # Create all the requested package files. + # + # [:clobber_package] + # Delete all the package files. This target is automatically + # added to the main clobber target. + # + # [:repackage] + # Rebuild the package files from scratch, even if they are not out + # of date. + # + # ["package_dir/name-version.tgz"] + # Create a gzipped tar package (if need_tar is true). + # + # ["package_dir/name-version.tar.gz"] + # Create a gzipped tar package (if need_tar_gz is true). + # + # ["package_dir/name-version.tar.bz2"] + # Create a bzip2'd tar package (if need_tar_bz2 is true). + # + # ["package_dir/name-version.zip"] + # Create a zip package archive (if need_zip is true). + # + # Example: + # + # Rake::PackageTask.new("rake", "1.2.3") do |p| + # p.need_tar = true + # p.package_files.include("lib/**/*.rb") + # end + # + class PackageTask < TaskLib + # Name of the package (from the GEM Spec). + attr_accessor :name + + # Version of the package (e.g. '1.3.2'). + attr_accessor :version + + # Directory used to store the package files (default is 'pkg'). + attr_accessor :package_dir + + # True if a gzipped tar file (tgz) should be produced (default is + # false). + attr_accessor :need_tar + + # True if a gzipped tar file (tar.gz) should be produced (default + # is false). + attr_accessor :need_tar_gz + + # True if a bzip2'd tar file (tar.bz2) should be produced (default + # is false). + attr_accessor :need_tar_bz2 + + # True if a zip file should be produced (default is false) + attr_accessor :need_zip + + # List of files to be included in the package. + attr_accessor :package_files + + # Tar command for gzipped or bzip2ed archives. The default is 'tar'. + attr_accessor :tar_command + + # Zip command for zipped archives. The default is 'zip'. + attr_accessor :zip_command + + # Create a Package Task with the given name and version. Use +:noversion+ + # as the version to build a package without a version or to provide a + # fully-versioned package name. + + def initialize(name=nil, version=nil) + init(name, version) + yield self if block_given? + define unless name.nil? + end + + # Initialization that bypasses the "yield self" and "define" step. + def init(name, version) + @name = name + @version = version + @package_files = Rake::FileList.new + @package_dir = 'pkg' + @need_tar = false + @need_tar_gz = false + @need_tar_bz2 = false + @need_zip = false + @tar_command = 'tar' + @zip_command = 'zip' + end + + # Create the tasks defined by this task library. + def define + fail "Version required (or :noversion)" if @version.nil? + @version = nil if :noversion == @version + + desc "Build all the packages" + task :package + + desc "Force a rebuild of the package files" + task :repackage => [:clobber_package, :package] + + desc "Remove package products" + task :clobber_package do + rm_r package_dir rescue nil + end + + task :clobber => [:clobber_package] + + [ + [need_tar, tgz_file, "z"], + [need_tar_gz, tar_gz_file, "z"], + [need_tar_bz2, tar_bz2_file, "j"] + ].each do |(need, file, flag)| + if need + task :package => ["#{package_dir}/#{file}"] + file "#{package_dir}/#{file}" => + [package_dir_path] + package_files do + chdir(package_dir) do + sh %{#{@tar_command} #{flag}cvf #{file} #{package_name}} + end + end + end + end + + if need_zip + task :package => ["#{package_dir}/#{zip_file}"] + file "#{package_dir}/#{zip_file}" => + [package_dir_path] + package_files do + chdir(package_dir) do + sh %{#{@zip_command} -r #{zip_file} #{package_name}} + end + end + end + + directory package_dir + + file package_dir_path => @package_files do + mkdir_p package_dir rescue nil + @package_files.each do |fn| + f = File.join(package_dir_path, fn) + fdir = File.dirname(f) + mkdir_p(fdir) unless File.exist?(fdir) + if File.directory?(fn) + mkdir_p(f) + else + rm_f f + safe_ln(fn, f) + end + end + end + self + end + + def package_name + @version ? "#{@name}-#{@version}" : @name + end + + def package_dir_path + "#{package_dir}/#{package_name}" + end + + def tgz_file + "#{package_name}.tgz" + end + + def tar_gz_file + "#{package_name}.tar.gz" + end + + def tar_bz2_file + "#{package_name}.tar.bz2" + end + + def zip_file + "#{package_name}.zip" + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/pathmap.rb b/ruby/lib/ruby/2.1.0/rake/pathmap.rb new file mode 100644 index 0000000..2275724 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/pathmap.rb @@ -0,0 +1 @@ +require 'rake/ext/string' diff --git a/ruby/lib/ruby/2.1.0/rake/phony.rb b/ruby/lib/ruby/2.1.0/rake/phony.rb new file mode 100644 index 0000000..29633ae --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/phony.rb @@ -0,0 +1,15 @@ +# Defines a :phony task that you can use as a dependency. This allows +# file-based tasks to use non-file-based tasks as prerequisites +# without forcing them to rebuild. +# +# See FileTask#out_of_date? and Task#timestamp for more info. + +require 'rake' + +task :phony + +Rake::Task[:phony].tap do |task| + def task.timestamp # :nodoc: + Time.at 0 + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/private_reader.rb b/ruby/lib/ruby/2.1.0/rake/private_reader.rb new file mode 100644 index 0000000..1620978 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/private_reader.rb @@ -0,0 +1,20 @@ +module Rake + + # Include PrivateReader to use +private_reader+. + module PrivateReader # :nodoc: all + + def self.included(base) + base.extend(ClassMethods) + end + + module ClassMethods + + # Declare a list of private accessors + def private_reader(*names) + attr_reader(*names) + private(*names) + end + end + + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/promise.rb b/ruby/lib/ruby/2.1.0/rake/promise.rb new file mode 100644 index 0000000..31c4563 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/promise.rb @@ -0,0 +1,99 @@ +module Rake + + # A Promise object represents a promise to do work (a chore) in the + # future. The promise is created with a block and a list of + # arguments for the block. Calling value will return the value of + # the promised chore. + # + # Used by ThreadPool. + # + class Promise # :nodoc: all + NOT_SET = Object.new.freeze # :nodoc: + + attr_accessor :recorder + + # Create a promise to do the chore specified by the block. + def initialize(args, &block) + @mutex = Mutex.new + @result = NOT_SET + @error = NOT_SET + @args = args + @block = block + end + + # Return the value of this promise. + # + # If the promised chore is not yet complete, then do the work + # synchronously. We will wait. + def value + unless complete? + stat :sleeping_on, :item_id => object_id + @mutex.synchronize do + stat :has_lock_on, :item_id => object_id + chore + stat :releasing_lock_on, :item_id => object_id + end + end + error? ? raise(@error) : @result + end + + # If no one else is working this promise, go ahead and do the chore. + def work + stat :attempting_lock_on, :item_id => object_id + if @mutex.try_lock + stat :has_lock_on, :item_id => object_id + chore + stat :releasing_lock_on, :item_id => object_id + @mutex.unlock + else + stat :bailed_on, :item_id => object_id + end + end + + private + + # Perform the chore promised + def chore + if complete? + stat :found_completed, :item_id => object_id + return + end + stat :will_execute, :item_id => object_id + begin + @result = @block.call(*@args) + rescue Exception => e + @error = e + end + stat :did_execute, :item_id => object_id + discard + end + + # Do we have a result for the promise + def result? + ! @result.equal?(NOT_SET) + end + + # Did the promise throw an error + def error? + ! @error.equal?(NOT_SET) + end + + # Are we done with the promise + def complete? + result? || error? + end + + # free up these items for the GC + def discard + @args = nil + @block = nil + end + + # Record execution statistics if there is a recorder + def stat(*args) + @recorder.call(*args) if @recorder + end + + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/pseudo_status.rb b/ruby/lib/ruby/2.1.0/rake/pseudo_status.rb new file mode 100644 index 0000000..09d5c88 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/pseudo_status.rb @@ -0,0 +1,29 @@ +module Rake + + #################################################################### + # Exit status class for times the system just gives us a nil. + class PseudoStatus + attr_reader :exitstatus + + def initialize(code=0) + @exitstatus = code + end + + def to_i + @exitstatus << 8 + end + + def >>(n) + to_i >> n + end + + def stopped? + false + end + + def exited? + true + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/rake_module.rb b/ruby/lib/ruby/2.1.0/rake/rake_module.rb new file mode 100644 index 0000000..fcf5800 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/rake_module.rb @@ -0,0 +1,37 @@ +require 'rake/application' + +module Rake + + # Rake module singleton methods. + # + class << self + # Current Rake Application + def application + @application ||= Rake::Application.new + end + + # Set the current Rake application object. + def application=(app) + @application = app + end + + # Return the original directory where the Rake application was started. + def original_dir + application.original_dir + end + + # Load a rakefile. + def load_rakefile(path) + load(path) + end + + # Add files to the rakelib list + def add_rakelib(*files) + application.options.rakelib ||= [] + files.each do |file| + application.options.rakelib << file + end + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/rake_test_loader.rb b/ruby/lib/ruby/2.1.0/rake/rake_test_loader.rb new file mode 100644 index 0000000..7e3a6b3 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/rake_test_loader.rb @@ -0,0 +1,22 @@ +require 'rake' + +# Load the test files from the command line. +argv = ARGV.select do |argument| + case argument + when /^-/ then + argument + when /\*/ then + FileList[argument].to_a.each do |file| + require File.expand_path file + end + + false + else + require File.expand_path argument + + false + end +end + +ARGV.replace argv + diff --git a/ruby/lib/ruby/2.1.0/rake/rdoctask.rb b/ruby/lib/ruby/2.1.0/rake/rdoctask.rb new file mode 100644 index 0000000..50b7e26 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/rdoctask.rb @@ -0,0 +1,2 @@ +fail "ERROR: 'rake/rdoctask' is obsolete and no longer supported. " + + "Use 'rdoc/task' (available in RDoc 2.4.2+) instead." diff --git a/ruby/lib/ruby/2.1.0/rake/ruby182_test_unit_fix.rb b/ruby/lib/ruby/2.1.0/rake/ruby182_test_unit_fix.rb new file mode 100644 index 0000000..e47feeb --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/ruby182_test_unit_fix.rb @@ -0,0 +1,27 @@ +# Local Rake override to fix bug in Ruby 0.8.2 +module Test # :nodoc: + # Local Rake override to fix bug in Ruby 0.8.2 + module Unit # :nodoc: + # Local Rake override to fix bug in Ruby 0.8.2 + module Collector # :nodoc: + # Local Rake override to fix bug in Ruby 0.8.2 + class Dir # :nodoc: + undef collect_file + def collect_file(name, suites, already_gathered) # :nodoc: + dir = File.dirname(File.expand_path(name)) + $:.unshift(dir) unless $:.first == dir + if @req + @req.require(name) + else + require(name) + end + find_test_cases(already_gathered).each do |t| + add_suite(suites, t.suite) + end + ensure + $:.delete_at $:.rindex(dir) + end + end + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/rule_recursion_overflow_error.rb b/ruby/lib/ruby/2.1.0/rake/rule_recursion_overflow_error.rb new file mode 100644 index 0000000..da4318d --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/rule_recursion_overflow_error.rb @@ -0,0 +1,20 @@ + +module Rake + + # Error indicating a recursion overflow error in task selection. + class RuleRecursionOverflowError < StandardError + def initialize(*args) + super + @targets = [] + end + + def add_target(target) + @targets << target + end + + def message + super + ": [" + @targets.reverse.join(' => ') + "]" + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/runtest.rb b/ruby/lib/ruby/2.1.0/rake/runtest.rb new file mode 100644 index 0000000..3f01b28 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/runtest.rb @@ -0,0 +1,22 @@ +require 'test/unit' +require 'test/unit/assertions' +require 'rake/file_list' + +module Rake + include Test::Unit::Assertions + + def run_tests(pattern='test/test*.rb', log_enabled=false) + FileList.glob(pattern).each do |fn| + $stderr.puts fn if log_enabled + begin + require fn + rescue Exception => ex + $stderr.puts "Error in #{fn}: #{ex.message}" + $stderr.puts ex.backtrace + assert false + end + end + end + + extend self +end diff --git a/ruby/lib/ruby/2.1.0/rake/scope.rb b/ruby/lib/ruby/2.1.0/rake/scope.rb new file mode 100644 index 0000000..33e1c08 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/scope.rb @@ -0,0 +1,42 @@ +module Rake + class Scope < LinkedList + + # Path for the scope. + def path + map { |item| item.to_s }.reverse.join(":") + end + + # Path for the scope + the named path. + def path_with_task_name(task_name) + "#{path}:#{task_name}" + end + + # Trim +n+ innermost scope levels from the scope. In no case will + # this trim beyond the toplevel scope. + def trim(n) + result = self + while n > 0 && ! result.empty? + result = result.tail + n -= 1 + end + result + end + + # Scope lists always end with an EmptyScope object. See Null + # Object Pattern) + class EmptyScope < EmptyLinkedList + @parent = Scope + + def path + "" + end + + def path_with_task_name(task_name) + task_name + end + end + + # Singleton null object for an empty scope. + EMPTY = EmptyScope.new + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/task.rb b/ruby/lib/ruby/2.1.0/rake/task.rb new file mode 100644 index 0000000..5e4dd64 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/task.rb @@ -0,0 +1,378 @@ +require 'rake/invocation_exception_mixin' + +module Rake + + # ######################################################################### + # A Task is the basic unit of work in a Rakefile. Tasks have associated + # actions (possibly more than one) and a list of prerequisites. When + # invoked, a task will first ensure that all of its prerequisites have an + # opportunity to run and then it will execute its own actions. + # + # Tasks are not usually created directly using the new method, but rather + # use the +file+ and +task+ convenience methods. + # + class Task + # List of prerequisites for a task. + attr_reader :prerequisites + + # List of actions attached to a task. + attr_reader :actions + + # Application owning this task. + attr_accessor :application + + # Array of nested namespaces names used for task lookup by this task. + attr_reader :scope + + # File/Line locations of each of the task definitions for this + # task (only valid if the task was defined with the detect + # location option set). + attr_reader :locations + + # Return task name + def to_s + name + end + + def inspect + "<#{self.class} #{name} => [#{prerequisites.join(', ')}]>" + end + + # List of sources for task. + attr_writer :sources + def sources + @sources ||= [] + end + + # List of prerequisite tasks + def prerequisite_tasks + prerequisites.map { |pre| lookup_prerequisite(pre) } + end + + def lookup_prerequisite(prerequisite_name) + application[prerequisite_name, @scope] + end + private :lookup_prerequisite + + # List of all unique prerequisite tasks including prerequisite tasks' + # prerequisites. + # Includes self when cyclic dependencies are found. + def all_prerequisite_tasks + seen = {} + collect_prerequisites(seen) + seen.values + end + + def collect_prerequisites(seen) + prerequisite_tasks.each do |pre| + next if seen[pre.name] + seen[pre.name] = pre + pre.collect_prerequisites(seen) + end + end + protected :collect_prerequisites + + # First source from a rule (nil if no sources) + def source + @sources.first if defined?(@sources) + end + + # Create a task named +task_name+ with no actions or prerequisites. Use + # +enhance+ to add actions and prerequisites. + def initialize(task_name, app) + @name = task_name.to_s + @prerequisites = [] + @actions = [] + @already_invoked = false + @comments = [] + @lock = Monitor.new + @application = app + @scope = app.current_scope + @arg_names = nil + @locations = [] + end + + # Enhance a task with prerequisites or actions. Returns self. + def enhance(deps=nil, &block) + @prerequisites |= deps if deps + @actions << block if block_given? + self + end + + # Name of the task, including any namespace qualifiers. + def name + @name.to_s + end + + # Name of task with argument list description. + def name_with_args # :nodoc: + if arg_description + "#{name}#{arg_description}" + else + name + end + end + + # Argument description (nil if none). + def arg_description # :nodoc: + @arg_names ? "[#{arg_names.join(',')}]" : nil + end + + # Name of arguments for this task. + def arg_names + @arg_names || [] + end + + # Reenable the task, allowing its tasks to be executed if the task + # is invoked again. + def reenable + @already_invoked = false + end + + # Clear the existing prerequisites and actions of a rake task. + def clear + clear_prerequisites + clear_actions + clear_comments + self + end + + # Clear the existing prerequisites of a rake task. + def clear_prerequisites + prerequisites.clear + self + end + + # Clear the existing actions on a rake task. + def clear_actions + actions.clear + self + end + + # Clear the existing comments on a rake task. + def clear_comments + @comments = [] + self + end + + # Invoke the task if it is needed. Prerequisites are invoked first. + def invoke(*args) + task_args = TaskArguments.new(arg_names, args) + invoke_with_call_chain(task_args, InvocationChain::EMPTY) + end + + # Same as invoke, but explicitly pass a call chain to detect + # circular dependencies. + def invoke_with_call_chain(task_args, invocation_chain) # :nodoc: + new_chain = InvocationChain.append(self, invocation_chain) + @lock.synchronize do + if application.options.trace + application.trace "** Invoke #{name} #{format_trace_flags}" + end + return if @already_invoked + @already_invoked = true + invoke_prerequisites(task_args, new_chain) + execute(task_args) if needed? + end + rescue Exception => ex + add_chain_to(ex, new_chain) + raise ex + end + protected :invoke_with_call_chain + + def add_chain_to(exception, new_chain) + exception.extend(InvocationExceptionMixin) unless + exception.respond_to?(:chain) + exception.chain = new_chain if exception.chain.nil? + end + private :add_chain_to + + # Invoke all the prerequisites of a task. + def invoke_prerequisites(task_args, invocation_chain) # :nodoc: + if application.options.always_multitask + invoke_prerequisites_concurrently(task_args, invocation_chain) + else + prerequisite_tasks.each { |p| + prereq_args = task_args.new_scope(p.arg_names) + p.invoke_with_call_chain(prereq_args, invocation_chain) + } + end + end + + # Invoke all the prerequisites of a task in parallel. + def invoke_prerequisites_concurrently(task_args, invocation_chain)# :nodoc: + futures = prerequisite_tasks.map do |p| + prereq_args = task_args.new_scope(p.arg_names) + application.thread_pool.future(p) do |r| + r.invoke_with_call_chain(prereq_args, invocation_chain) + end + end + futures.each { |f| f.value } + end + + # Format the trace flags for display. + def format_trace_flags + flags = [] + flags << "first_time" unless @already_invoked + flags << "not_needed" unless needed? + flags.empty? ? "" : "(" + flags.join(", ") + ")" + end + private :format_trace_flags + + # Execute the actions associated with this task. + def execute(args=nil) + args ||= EMPTY_TASK_ARGS + if application.options.dryrun + application.trace "** Execute (dry run) #{name}" + return + end + application.trace "** Execute #{name}" if application.options.trace + application.enhance_with_matching_rule(name) if @actions.empty? + @actions.each do |act| + case act.arity + when 1 + act.call(self) + else + act.call(self, args) + end + end + end + + # Is this task needed? + def needed? + true + end + + # Timestamp for this task. Basic tasks return the current time for their + # time stamp. Other tasks can be more sophisticated. + def timestamp + Time.now + end + + # Add a description to the task. The description can consist of an option + # argument list (enclosed brackets) and an optional comment. + def add_description(description) + return unless description + comment = description.strip + add_comment(comment) if comment && ! comment.empty? + end + + def comment=(comment) + add_comment(comment) + end + + def add_comment(comment) + @comments << comment unless @comments.include?(comment) + end + private :add_comment + + # Full collection of comments. Multiple comments are separated by + # newlines. + def full_comment + transform_comments("\n") + end + + # First line (or sentence) of all comments. Multiple comments are + # separated by a "/". + def comment + transform_comments(" / ") { |c| first_sentence(c) } + end + + # Transform the list of comments as specified by the block and + # join with the separator. + def transform_comments(separator, &block) + if @comments.empty? + nil + else + block ||= lambda { |c| c } + @comments.map(&block).join(separator) + end + end + private :transform_comments + + # Get the first sentence in a string. The sentence is terminated + # by the first period or the end of the line. Decimal points do + # not count as periods. + def first_sentence(string) + string.split(/\.[ \t]|\.$|\n/).first + end + private :first_sentence + + # Set the names of the arguments for this task. +args+ should be + # an array of symbols, one for each argument name. + def set_arg_names(args) + @arg_names = args.map { |a| a.to_sym } + end + + # Return a string describing the internal state of a task. Useful for + # debugging. + def investigation + result = "------------------------------\n" + result << "Investigating #{name}\n" + result << "class: #{self.class}\n" + result << "task needed: #{needed?}\n" + result << "timestamp: #{timestamp}\n" + result << "pre-requisites: \n" + prereqs = prerequisite_tasks + prereqs.sort! { |a, b| a.timestamp <=> b.timestamp } + prereqs.each do |p| + result << "--#{p.name} (#{p.timestamp})\n" + end + latest_prereq = prerequisite_tasks.map { |pre| pre.timestamp }.max + result << "latest-prerequisite time: #{latest_prereq}\n" + result << "................................\n\n" + return result + end + + # ---------------------------------------------------------------- + # Rake Module Methods + # + class << self + + # Clear the task list. This cause rake to immediately forget all the + # tasks that have been assigned. (Normally used in the unit tests.) + def clear + Rake.application.clear + end + + # List of all defined tasks. + def tasks + Rake.application.tasks + end + + # Return a task with the given name. If the task is not currently + # known, try to synthesize one from the defined rules. If no rules are + # found, but an existing file matches the task name, assume it is a file + # task with no dependencies or actions. + def [](task_name) + Rake.application[task_name] + end + + # TRUE if the task name is already defined. + def task_defined?(task_name) + Rake.application.lookup(task_name) != nil + end + + # Define a task given +args+ and an option block. If a rule with the + # given name already exists, the prerequisites and actions are added to + # the existing task. Returns the defined task. + def define_task(*args, &block) + Rake.application.define_task(self, *args, &block) + end + + # Define a rule for synthesizing tasks. + def create_rule(*args, &block) + Rake.application.create_rule(*args, &block) + end + + # Apply the scope to the task name according to the rules for + # this kind of task. Generic tasks will accept the scope as + # part of the name. + def scope_name(scope, task_name) +# (scope + [task_name]).join(':') + scope.path_with_task_name(task_name) + end + + end # class << Rake::Task + end # class Rake::Task +end diff --git a/ruby/lib/ruby/2.1.0/rake/task_argument_error.rb b/ruby/lib/ruby/2.1.0/rake/task_argument_error.rb new file mode 100644 index 0000000..3e1dda6 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/task_argument_error.rb @@ -0,0 +1,7 @@ +module Rake + + # Error indicating an ill-formed task declaration. + class TaskArgumentError < ArgumentError + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/task_arguments.rb b/ruby/lib/ruby/2.1.0/rake/task_arguments.rb new file mode 100644 index 0000000..0094682 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/task_arguments.rb @@ -0,0 +1,89 @@ +module Rake + + #################################################################### + # TaskArguments manage the arguments passed to a task. + # + class TaskArguments + include Enumerable + + attr_reader :names + + # Create a TaskArgument object with a list of named arguments + # (given by :names) and a set of associated values (given by + # :values). :parent is the parent argument object. + def initialize(names, values, parent=nil) + @names = names + @parent = parent + @hash = {} + @values = values + names.each_with_index { |name, i| + @hash[name.to_sym] = values[i] unless values[i].nil? + } + end + + # Retrive the complete array of sequential values + def to_a + @values.dup + end + + # Retrive the list of values not associated with named arguments + def extras + @values[@names.length..-1] || [] + end + + # Create a new argument scope using the prerequisite argument + # names. + def new_scope(names) + values = names.map { |n| self[n] } + self.class.new(names, values + extras, self) + end + + # Find an argument value by name or index. + def [](index) + lookup(index.to_sym) + end + + # Specify a hash of default values for task arguments. Use the + # defaults only if there is no specific value for the given + # argument. + def with_defaults(defaults) + @hash = defaults.merge(@hash) + end + + def each(&block) + @hash.each(&block) + end + + def values_at(*keys) + keys.map { |k| lookup(k) } + end + + def method_missing(sym, *args) + lookup(sym.to_sym) + end + + def to_hash + @hash + end + + def to_s + @hash.inspect + end + + def inspect + to_s + end + + protected + + def lookup(name) + if @hash.has_key?(name) + @hash[name] + elsif @parent + @parent.lookup(name) + end + end + end + + EMPTY_TASK_ARGS = TaskArguments.new([], []) +end diff --git a/ruby/lib/ruby/2.1.0/rake/task_manager.rb b/ruby/lib/ruby/2.1.0/rake/task_manager.rb new file mode 100644 index 0000000..06c243a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/task_manager.rb @@ -0,0 +1,297 @@ +module Rake + + # The TaskManager module is a mixin for managing tasks. + module TaskManager + # Track the last comment made in the Rakefile. + attr_accessor :last_description + alias :last_comment :last_description # Backwards compatibility + + def initialize + super + @tasks = Hash.new + @rules = Array.new + @scope = Scope.make + @last_description = nil + end + + def create_rule(*args, &block) + pattern, args, deps = resolve_args(args) + pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern + @rules << [pattern, args, deps, block] + end + + def define_task(task_class, *args, &block) + task_name, arg_names, deps = resolve_args(args) + task_name = task_class.scope_name(@scope, task_name) + deps = [deps] unless deps.respond_to?(:to_ary) + deps = deps.map { |d| d.to_s } + task = intern(task_class, task_name) + task.set_arg_names(arg_names) unless arg_names.empty? + if Rake::TaskManager.record_task_metadata + add_location(task) + task.add_description(get_description(task)) + end + task.enhance(deps, &block) + end + + # Lookup a task. Return an existing task if found, otherwise + # create a task of the current type. + def intern(task_class, task_name) + @tasks[task_name.to_s] ||= task_class.new(task_name, self) + end + + # Find a matching task for +task_name+. + def [](task_name, scopes=nil) + task_name = task_name.to_s + self.lookup(task_name, scopes) or + enhance_with_matching_rule(task_name) or + synthesize_file_task(task_name) or + fail "Don't know how to build task '#{task_name}'" + end + + def synthesize_file_task(task_name) + return nil unless File.exist?(task_name) + define_task(Rake::FileTask, task_name) + end + + # Resolve the arguments for a task/rule. Returns a triplet of + # [task_name, arg_name_list, prerequisites]. + def resolve_args(args) + if args.last.is_a?(Hash) + deps = args.pop + resolve_args_with_dependencies(args, deps) + else + resolve_args_without_dependencies(args) + end + end + + # Resolve task arguments for a task or rule when there are no + # dependencies declared. + # + # The patterns recognized by this argument resolving function are: + # + # task :t + # task :t, [:a] + # + def resolve_args_without_dependencies(args) + task_name = args.shift + if args.size == 1 && args.first.respond_to?(:to_ary) + arg_names = args.first.to_ary + else + arg_names = args + end + [task_name, arg_names, []] + end + private :resolve_args_without_dependencies + + # Resolve task arguments for a task or rule when there are + # dependencies declared. + # + # The patterns recognized by this argument resolving function are: + # + # task :t => [:d] + # task :t, [a] => [:d] + # + def resolve_args_with_dependencies(args, hash) # :nodoc: + fail "Task Argument Error" if hash.size != 1 + key, value = hash.map { |k, v| [k, v] }.first + if args.empty? + task_name = key + arg_names = [] + deps = value + else + task_name = args.shift + arg_names = key + deps = value + end + deps = [deps] unless deps.respond_to?(:to_ary) + [task_name, arg_names, deps] + end + private :resolve_args_with_dependencies + + # If a rule can be found that matches the task name, enhance the + # task with the prerequisites and actions from the rule. Set the + # source attribute of the task appropriately for the rule. Return + # the enhanced task or nil of no rule was found. + def enhance_with_matching_rule(task_name, level=0) + fail Rake::RuleRecursionOverflowError, + "Rule Recursion Too Deep" if level >= 16 + @rules.each do |pattern, args, extensions, block| + if pattern.match(task_name) + task = attempt_rule(task_name, args, extensions, block, level) + return task if task + end + end + nil + rescue Rake::RuleRecursionOverflowError => ex + ex.add_target(task_name) + fail ex + end + + # List of all defined tasks in this application. + def tasks + @tasks.values.sort_by { |t| t.name } + end + + # List of all the tasks defined in the given scope (and its + # sub-scopes). + def tasks_in_scope(scope) + prefix = scope.path + tasks.select { |t| + /^#{prefix}:/ =~ t.name + } + end + + # Clear all tasks in this application. + def clear + @tasks.clear + @rules.clear + end + + # Lookup a task, using scope and the scope hints in the task name. + # This method performs straight lookups without trying to + # synthesize file tasks or rules. Special scope names (e.g. '^') + # are recognized. If no scope argument is supplied, use the + # current scope. Return nil if the task cannot be found. + def lookup(task_name, initial_scope=nil) + initial_scope ||= @scope + task_name = task_name.to_s + if task_name =~ /^rake:/ + scopes = Scope.make + task_name = task_name.sub(/^rake:/, '') + elsif task_name =~ /^(\^+)/ + scopes = initial_scope.trim($1.size) + task_name = task_name.sub(/^(\^+)/, '') + else + scopes = initial_scope + end + lookup_in_scope(task_name, scopes) + end + + # Lookup the task name + def lookup_in_scope(name, scope) + loop do + tn = scope.path_with_task_name(name) + task = @tasks[tn] + return task if task + break if scope.empty? + scope = scope.tail + end + nil + end + private :lookup_in_scope + + # Return the list of scope names currently active in the task + # manager. + def current_scope + @scope + end + + # Evaluate the block in a nested namespace named +name+. Create + # an anonymous namespace if +name+ is nil. + def in_namespace(name) + name ||= generate_name + @scope = Scope.new(name, @scope) + ns = NameSpace.new(self, @scope) + yield(ns) + ns + ensure + @scope = @scope.tail + end + + private + + # Add a location to the locations field of the given task. + def add_location(task) + loc = find_location + task.locations << loc if loc + task + end + + # Find the location that called into the dsl layer. + def find_location + locations = caller + i = 0 + while locations[i] + return locations[i + 1] if locations[i] =~ /rake\/dsl_definition.rb/ + i += 1 + end + nil + end + + # Generate an anonymous namespace name. + def generate_name + @seed ||= 0 + @seed += 1 + "_anon_#{@seed}" + end + + def trace_rule(level, message) + options.trace_output.puts "#{" " * level}#{message}" if + Rake.application.options.trace_rules + end + + # Attempt to create a rule given the list of prerequisites. + def attempt_rule(task_name, args, extensions, block, level) + sources = make_sources(task_name, extensions) + prereqs = sources.map { |source| + trace_rule level, "Attempting Rule #{task_name} => #{source}" + if File.exist?(source) || Rake::Task.task_defined?(source) + trace_rule level, "(#{task_name} => #{source} ... EXIST)" + source + elsif parent = enhance_with_matching_rule(source, level + 1) + trace_rule level, "(#{task_name} => #{source} ... ENHANCE)" + parent.name + else + trace_rule level, "(#{task_name} => #{source} ... FAIL)" + return nil + end + } + task = FileTask.define_task(task_name, {args => prereqs}, &block) + task.sources = prereqs + task + end + + # Make a list of sources from the list of file name extensions / + # translation procs. + def make_sources(task_name, extensions) + result = extensions.map { |ext| + case ext + when /%/ + task_name.pathmap(ext) + when %r{/} + ext + when /^\./ + task_name.ext(ext) + when String + ext + when Proc + if ext.arity == 1 + ext.call(task_name) + else + ext.call + end + else + fail "Don't know how to handle rule dependent: #{ext.inspect}" + end + } + result.flatten + end + + + private + + # Return the current description, clearing it in the process. + def get_description(task) + desc = @last_description + @last_description = nil + desc + end + + class << self + attr_accessor :record_task_metadata + TaskManager.record_task_metadata = false + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/tasklib.rb b/ruby/lib/ruby/2.1.0/rake/tasklib.rb new file mode 100644 index 0000000..48d27df --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/tasklib.rb @@ -0,0 +1,22 @@ +require 'rake' + +module Rake + + # Base class for Task Libraries. + class TaskLib + include Cloneable + include Rake::DSL + + # Make a symbol by pasting two strings together. + # + # NOTE: DEPRECATED! This method is kinda stupid. I don't know why + # I didn't just use string interpolation. But now other task + # libraries depend on this so I can't remove it without breaking + # other people's code. So for now it stays for backwards + # compatibility. BUT DON'T USE IT. + def paste(a, b) # :nodoc: + (a.to_s + b.to_s).intern + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/testtask.rb b/ruby/lib/ruby/2.1.0/rake/testtask.rb new file mode 100644 index 0000000..c693dd2 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/testtask.rb @@ -0,0 +1,201 @@ +# Define a task library for running unit tests. + +require 'rake' +require 'rake/tasklib' + +module Rake + + # Create a task that runs a set of tests. + # + # Example: + # + # Rake::TestTask.new do |t| + # t.libs << "test" + # t.test_files = FileList['test/test*.rb'] + # t.verbose = true + # end + # + # If rake is invoked with a "TEST=filename" command line option, + # then the list of test files will be overridden to include only the + # filename specified on the command line. This provides an easy way + # to run just one test. + # + # If rake is invoked with a "TESTOPTS=options" command line option, + # then the given options are passed to the test process after a + # '--'. This allows Test::Unit options to be passed to the test + # suite. + # + # Examples: + # + # rake test # run tests normally + # rake test TEST=just_one_file.rb # run just one test file. + # rake test TESTOPTS="-v" # run in verbose mode + # rake test TESTOPTS="--runner=fox" # use the fox test runner + # + class TestTask < TaskLib + + # Name of test task. (default is :test) + attr_accessor :name + + # List of directories to added to $LOAD_PATH before running the + # tests. (default is 'lib') + attr_accessor :libs + + # True if verbose test output desired. (default is false) + attr_accessor :verbose + + # Test options passed to the test suite. An explicit + # TESTOPTS=opts on the command line will override this. (default + # is NONE) + attr_accessor :options + + # Request that the tests be run with the warning flag set. + # E.g. warning=true implies "ruby -w" used to run the tests. + attr_accessor :warning + + # Glob pattern to match test files. (default is 'test/test*.rb') + attr_accessor :pattern + + # Style of test loader to use. Options are: + # + # * :rake -- Rake provided test loading script (default). + # * :testrb -- Ruby provided test loading script. + # * :direct -- Load tests using command line loader. + # + attr_accessor :loader + + # Array of commandline options to pass to ruby when running test loader. + attr_accessor :ruby_opts + + # Explicitly define the list of test files to be included in a + # test. +list+ is expected to be an array of file names (a + # FileList is acceptable). If both +pattern+ and +test_files+ are + # used, then the list of test files is the union of the two. + def test_files=(list) + @test_files = list + end + + # Create a testing task. + def initialize(name=:test) + @name = name + @libs = ["lib"] + @pattern = nil + @options = nil + @test_files = nil + @verbose = false + @warning = false + @loader = :rake + @ruby_opts = [] + yield self if block_given? + @pattern = 'test/test*.rb' if @pattern.nil? && @test_files.nil? + define + end + + # Create the tasks defined by this task lib. + def define + desc "Run tests" + (@name == :test ? "" : " for #{@name}") + task @name do + FileUtilsExt.verbose(@verbose) do + args = + "#{ruby_opts_string} #{run_code} " + + "#{file_list_string} #{option_list}" + ruby args do |ok, status| + if !ok && status.respond_to?(:signaled?) && status.signaled? + raise SignalException.new(status.termsig) + elsif !ok + fail "Command failed with status (#{status.exitstatus}): " + + "[ruby #{args}]" + end + end + end + end + self + end + + def option_list # :nodoc: + (ENV['TESTOPTS'] || + ENV['TESTOPT'] || + ENV['TEST_OPTS'] || + ENV['TEST_OPT'] || + @options || + "") + end + + def ruby_opts_string + opts = @ruby_opts.dup + opts.unshift("-I\"#{lib_path}\"") unless @libs.empty? + opts.unshift("-w") if @warning + opts.join(" ") + end + + def lib_path + @libs.join(File::PATH_SEPARATOR) + end + + def file_list_string + file_list.map { |fn| "\"#{fn}\"" }.join(' ') + end + + def file_list # :nodoc: + if ENV['TEST'] + FileList[ENV['TEST']] + else + result = [] + result += @test_files.to_a if @test_files + result << @pattern if @pattern + result + end + end + + def fix # :nodoc: + case ruby_version + when '1.8.2' + "\"#{find_file 'rake/ruby182_test_unit_fix'}\"" + else + nil + end || '' + end + + def ruby_version + RUBY_VERSION + end + + def run_code + case @loader + when :direct + "-e \"ARGV.each{|f| require f}\"" + when :testrb + "-S testrb #{fix}" + when :rake + "-I\"#{rake_lib_dir}\" \"#{rake_loader}\"" + end + end + + def rake_loader # :nodoc: + find_file('rake/rake_test_loader') or + fail "unable to find rake test loader" + end + + def find_file(fn) # :nodoc: + $LOAD_PATH.each do |path| + file_path = File.join(path, "#{fn}.rb") + return file_path if File.exist? file_path + end + nil + end + + def rake_lib_dir # :nodoc: + find_dir('rake') or + fail "unable to find rake lib" + end + + def find_dir(fn) # :nodoc: + $LOAD_PATH.each do |path| + file_path = File.join(path, "#{fn}.rb") + return path if File.exist? file_path + end + nil + end + + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/thread_history_display.rb b/ruby/lib/ruby/2.1.0/rake/thread_history_display.rb new file mode 100644 index 0000000..c2af9ec --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/thread_history_display.rb @@ -0,0 +1,48 @@ +require 'rake/private_reader' + +module Rake + + class ThreadHistoryDisplay # :nodoc: all + include Rake::PrivateReader + + private_reader :stats, :items, :threads + + def initialize(stats) + @stats = stats + @items = { :_seq_ => 1 } + @threads = { :_seq_ => "A" } + end + + def show + puts "Job History:" + stats.each do |stat| + stat[:data] ||= {} + rename(stat, :thread, threads) + rename(stat[:data], :item_id, items) + rename(stat[:data], :new_thread, threads) + rename(stat[:data], :deleted_thread, threads) + printf("%8d %2s %-20s %s\n", + (stat[:time] * 1_000_000).round, + stat[:thread], + stat[:event], + stat[:data].map do |k, v| "#{k}:#{v}" end.join(" ")) + end + end + + private + + def rename(hash, key, renames) + if hash && hash[key] + original = hash[key] + value = renames[original] + unless value + value = renames[:_seq_] + renames[:_seq_] = renames[:_seq_].succ + renames[original] = value + end + hash[key] = value + end + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/thread_pool.rb b/ruby/lib/ruby/2.1.0/rake/thread_pool.rb new file mode 100644 index 0000000..44bc748 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/thread_pool.rb @@ -0,0 +1,161 @@ +require 'thread' +require 'set' + +require 'rake/promise' + +module Rake + + class ThreadPool # :nodoc: all + + # Creates a ThreadPool object. + # The parameter is the size of the pool. + def initialize(thread_count) + @max_active_threads = [thread_count, 0].max + @threads = Set.new + @threads_mon = Monitor.new + @queue = Queue.new + @join_cond = @threads_mon.new_cond + + @history_start_time = nil + @history = [] + @history_mon = Monitor.new + @total_threads_in_play = 0 + end + + # Creates a future executed by the +ThreadPool+. + # + # The args are passed to the block when executing (similarly to + # Thread#new) The return value is an object representing + # a future which has been created and added to the queue in the + # pool. Sending #value to the object will sleep the + # current thread until the future is finished and will return the + # result (or raise an exception thrown from the future) + def future(*args, &block) + promise = Promise.new(args, &block) + promise.recorder = lambda { |*stats| stat(*stats) } + + @queue.enq promise + stat :queued, :item_id => promise.object_id + start_thread + promise + end + + # Waits until the queue of futures is empty and all threads have exited. + def join + @threads_mon.synchronize do + begin + stat :joining + @join_cond.wait unless @threads.empty? + stat :joined + rescue Exception => e + stat :joined + $stderr.puts e + $stderr.print "Queue contains #{@queue.size} items. " + + "Thread pool contains #{@threads.count} threads\n" + $stderr.print "Current Thread #{Thread.current} status = " + + "#{Thread.current.status}\n" + $stderr.puts e.backtrace.join("\n") + @threads.each do |t| + $stderr.print "Thread #{t} status = #{t.status}\n" + # 1.8 doesn't support Thread#backtrace + $stderr.puts t.backtrace.join("\n") if t.respond_to? :backtrace + end + raise e + end + end + end + + # Enable the gathering of history events. + def gather_history #:nodoc: + @history_start_time = Time.now if @history_start_time.nil? + end + + # Return a array of history events for the thread pool. + # + # History gathering must be enabled to be able to see the events + # (see #gather_history). Best to call this when the job is + # complete (i.e. after ThreadPool#join is called). + def history # :nodoc: + @history_mon.synchronize { @history.dup }. + sort_by { |i| i[:time] }. + each { |i| i[:time] -= @history_start_time } + end + + # Return a hash of always collected statistics for the thread pool. + def statistics # :nodoc: + { + :total_threads_in_play => @total_threads_in_play, + :max_active_threads => @max_active_threads, + } + end + + private + + # processes one item on the queue. Returns true if there was an + # item to process, false if there was no item + def process_queue_item #:nodoc: + return false if @queue.empty? + + # Even though we just asked if the queue was empty, it + # still could have had an item which by this statement + # is now gone. For this reason we pass true to Queue#deq + # because we will sleep indefinitely if it is empty. + promise = @queue.deq(true) + stat :dequeued, :item_id => promise.object_id + promise.work + return true + + rescue ThreadError # this means the queue is empty + false + end + + def start_thread # :nodoc: + @threads_mon.synchronize do + next unless @threads.count < @max_active_threads + + t = Thread.new do + begin + while @threads.count <= @max_active_threads + break unless process_queue_item + end + ensure + @threads_mon.synchronize do + @threads.delete Thread.current + stat :ended, :thread_count => @threads.count + @join_cond.broadcast if @threads.empty? + end + end + end + @threads << t + stat( + :spawned, + :new_thread => t.object_id, + :thread_count => @threads.count) + @total_threads_in_play = @threads.count if + @threads.count > @total_threads_in_play + end + end + + def stat(event, data=nil) # :nodoc: + return if @history_start_time.nil? + info = { + :event => event, + :data => data, + :time => Time.now, + :thread => Thread.current.object_id, + } + @history_mon.synchronize { @history << info } + end + + # for testing only + + def __queue__ # :nodoc: + @queue + end + + def __threads__ # :nodoc: + @threads.dup + end + end + +end diff --git a/ruby/lib/ruby/2.1.0/rake/trace_output.rb b/ruby/lib/ruby/2.1.0/rake/trace_output.rb new file mode 100644 index 0000000..1cd1945 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/trace_output.rb @@ -0,0 +1,22 @@ +module Rake + module TraceOutput + + # Write trace output to output stream +out+. + # + # The write is done as a single IO call (to print) to lessen the + # chance that the trace output is interrupted by other tasks also + # producing output. + def trace_on(out, *strings) + sep = $\ || "\n" + if strings.empty? + output = sep + else + output = strings.map { |s| + next if s.nil? + s =~ /#{sep}$/ ? s : s + sep + }.join + end + out.print(output) + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/version.rb b/ruby/lib/ruby/2.1.0/rake/version.rb new file mode 100644 index 0000000..05c934d --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/version.rb @@ -0,0 +1,9 @@ +module Rake + VERSION = '10.1.0' + + module Version # :nodoc: all + MAJOR, MINOR, BUILD, *OTHER = Rake::VERSION.split '.' + + NUMBERS = [MAJOR, MINOR, BUILD, *OTHER] + end +end diff --git a/ruby/lib/ruby/2.1.0/rake/win32.rb b/ruby/lib/ruby/2.1.0/rake/win32.rb new file mode 100644 index 0000000..edb3393 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rake/win32.rb @@ -0,0 +1,56 @@ + +module Rake + require 'rake/alt_system' + + # Win 32 interface methods for Rake. Windows specific functionality + # will be placed here to collect that knowledge in one spot. + module Win32 + + # Error indicating a problem in locating the home directory on a + # Win32 system. + class Win32HomeError < RuntimeError + end + + class << self + # True if running on a windows system. + def windows? + AltSystem::WINDOWS + end + + # Run a command line on windows. + def rake_system(*cmd) + AltSystem.system(*cmd) + end + + # The standard directory containing system wide rake files on + # Win 32 systems. Try the following environment variables (in + # order): + # + # * HOME + # * HOMEDRIVE + HOMEPATH + # * APPDATA + # * USERPROFILE + # + # If the above are not defined, the return nil. + def win32_system_dir #:nodoc: + win32_shared_path = ENV['HOME'] + if win32_shared_path.nil? && ENV['HOMEDRIVE'] && ENV['HOMEPATH'] + win32_shared_path = ENV['HOMEDRIVE'] + ENV['HOMEPATH'] + end + + win32_shared_path ||= ENV['APPDATA'] + win32_shared_path ||= ENV['USERPROFILE'] + raise Win32HomeError, + "Unable to determine home path environment variable." if + win32_shared_path.nil? or win32_shared_path.empty? + normalize(File.join(win32_shared_path, 'Rake')) + end + + # Normalize a win32 path so that the slashes are all forward slashes. + def normalize(path) + path.gsub(/\\/, '/') + end + + end + end +end diff --git a/ruby/lib/ruby/2.1.0/rational.rb b/ruby/lib/ruby/2.1.0/rational.rb new file mode 100644 index 0000000..a1aeca1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rational.rb @@ -0,0 +1,23 @@ +# :enddoc: + +warn('lib/rational.rb is deprecated') if $VERBOSE + +class Fixnum + + alias quof fdiv + alias rdiv quo + + alias power! ** unless method_defined? :power! + alias rpower ** + +end + +class Bignum + + alias quof fdiv + alias rdiv quo + + alias power! ** unless method_defined? :power! + alias rpower ** + +end diff --git a/ruby/lib/ruby/2.1.0/rbconfig/datadir.rb b/ruby/lib/ruby/2.1.0/rbconfig/datadir.rb new file mode 100644 index 0000000..9b7eabb --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rbconfig/datadir.rb @@ -0,0 +1,13 @@ +#-- +# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others. +# All rights reserved. +# See LICENSE.txt for permissions. +#++ + +# N.B. This file is used by Config.datadir in rubygems.rb, and must not be +# removed before that require is removed. I require to avoid warning more than +# once. + +warn 'rbconfig/datadir.rb and {Rb}Config.datadir is being deprecated from '\ + 'RubyGems. It will be removed completely on or after June 2011. If you '\ + 'wish to rely on a datadir, please use Gem.datadir.' diff --git a/ruby/lib/ruby/2.1.0/rbconfig/obsolete.rb b/ruby/lib/ruby/2.1.0/rbconfig/obsolete.rb new file mode 100644 index 0000000..7025fb4 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rbconfig/obsolete.rb @@ -0,0 +1,38 @@ +module ::RbConfig + module Obsolete + end + class << Obsolete + def _warn_ + loc, = caller_locations(2, 1) + loc = "#{loc.to_s}: " if loc + warn "#{loc}Use RbConfig instead of obsolete and deprecated Config." + self + end + + def const_missing(name) + _warn_ + ::RbConfig.const_get(name) + end + + def method_missing(*args, &block) + _warn_ + rbconfig = ::RbConfig + result = rbconfig.__send__(*args, &block) + result = rbconfig if rbconfig.equal?(result) + result + end + + def respond_to_missing?(*args, &block) + _warn_ + ::RbConfig.send(:respond_to_missing?, *args, &block) + end + end +end + +::Config = ::RbConfig::Obsolete._warn_ +=begin +def Object.const_missing(name) + return super unless name == :Config + ::RbConfig::Obsolete._warn_ +end +=end diff --git a/ruby/lib/ruby/2.1.0/rdoc.rb b/ruby/lib/ruby/2.1.0/rdoc.rb new file mode 100644 index 0000000..133888c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc.rb @@ -0,0 +1,183 @@ +$DEBUG_RDOC = nil + +# :main: README.rdoc + +## +# RDoc produces documentation for Ruby source files by parsing the source and +# extracting the definition for classes, modules, methods, includes and +# requires. It associates these with optional documentation contained in an +# immediately preceding comment block then renders the result using an output +# formatter. +# +# For a simple introduction to writing or generating documentation using RDoc +# see the README. +# +# == Roadmap +# +# If you think you found a bug in RDoc see CONTRIBUTING@Bugs +# +# If you want to use RDoc to create documentation for your Ruby source files, +# see RDoc::Markup and refer to rdoc --help for command line usage. +# +# If you want to set the default markup format see +# RDoc::Markup@Supported+Formats +# +# If you want to store rdoc configuration in your gem (such as the default +# markup format) see RDoc::Options@Saved+Options +# +# If you want to write documentation for Ruby files see RDoc::Parser::Ruby +# +# If you want to write documentation for extensions written in C see +# RDoc::Parser::C +# +# If you want to generate documentation using rake see RDoc::Task. +# +# If you want to drive RDoc programmatically, see RDoc::RDoc. +# +# If you want to use the library to format text blocks into HTML or other +# formats, look at RDoc::Markup. +# +# If you want to make an RDoc plugin such as a generator or directive handler +# see RDoc::RDoc. +# +# If you want to write your own output generator see RDoc::Generator. +# +# If you want an overview of how RDoc works see CONTRIBUTING +# +# == Credits +# +# RDoc is currently being maintained by Eric Hodel . +# +# Dave Thomas is the original author of RDoc. +# +# * The Ruby parser in rdoc/parse.rb is based heavily on the outstanding +# work of Keiju ISHITSUKA of Nippon Rational Inc, who produced the Ruby +# parser for irb and the rtags package. + +module RDoc + + ## + # Exception thrown by any rdoc error. + + class Error < RuntimeError; end + + ## + # RDoc version you are using + + VERSION = '4.1.0' + + ## + # Method visibilities + + VISIBILITIES = [:public, :protected, :private] + + ## + # Name of the dotfile that contains the description of files to be processed + # in the current directory + + DOT_DOC_FILENAME = ".document" + + ## + # General RDoc modifiers + + GENERAL_MODIFIERS = %w[nodoc].freeze + + ## + # RDoc modifiers for classes + + CLASS_MODIFIERS = GENERAL_MODIFIERS + + ## + # RDoc modifiers for attributes + + ATTR_MODIFIERS = GENERAL_MODIFIERS + + ## + # RDoc modifiers for constants + + CONSTANT_MODIFIERS = GENERAL_MODIFIERS + + ## + # RDoc modifiers for methods + + METHOD_MODIFIERS = GENERAL_MODIFIERS + + %w[arg args yield yields notnew not-new not_new doc] + + ## + # Loads the best available YAML library. + + def self.load_yaml + begin + gem 'psych' + rescue Gem::LoadError + end + + begin + require 'psych' + rescue ::LoadError + ensure + require 'yaml' + end + end + + autoload :RDoc, 'rdoc/rdoc' + + autoload :TestCase, 'rdoc/test_case' + + autoload :CrossReference, 'rdoc/cross_reference' + autoload :ERBIO, 'rdoc/erbio' + autoload :ERBPartial, 'rdoc/erb_partial' + autoload :Encoding, 'rdoc/encoding' + autoload :Generator, 'rdoc/generator' + autoload :Options, 'rdoc/options' + autoload :Parser, 'rdoc/parser' + autoload :Servlet, 'rdoc/servlet' + autoload :RI, 'rdoc/ri' + autoload :Stats, 'rdoc/stats' + autoload :Store, 'rdoc/store' + autoload :Task, 'rdoc/task' + autoload :Text, 'rdoc/text' + + autoload :Markdown, 'rdoc/markdown' + autoload :Markup, 'rdoc/markup' + autoload :RD, 'rdoc/rd' + autoload :TomDoc, 'rdoc/tom_doc' + + autoload :KNOWN_CLASSES, 'rdoc/known_classes' + + autoload :RubyLex, 'rdoc/ruby_lex' + autoload :RubyToken, 'rdoc/ruby_token' + autoload :TokenStream, 'rdoc/token_stream' + + autoload :Comment, 'rdoc/comment' + + # code objects + # + # We represent the various high-level code constructs that appear in Ruby + # programs: classes, modules, methods, and so on. + autoload :CodeObject, 'rdoc/code_object' + + autoload :Context, 'rdoc/context' + autoload :TopLevel, 'rdoc/top_level' + + autoload :AnonClass, 'rdoc/anon_class' + autoload :ClassModule, 'rdoc/class_module' + autoload :NormalClass, 'rdoc/normal_class' + autoload :NormalModule, 'rdoc/normal_module' + autoload :SingleClass, 'rdoc/single_class' + + autoload :Alias, 'rdoc/alias' + autoload :AnyMethod, 'rdoc/any_method' + autoload :MethodAttr, 'rdoc/method_attr' + autoload :GhostMethod, 'rdoc/ghost_method' + autoload :MetaMethod, 'rdoc/meta_method' + autoload :Attr, 'rdoc/attr' + + autoload :Constant, 'rdoc/constant' + autoload :Mixin, 'rdoc/mixin' + autoload :Include, 'rdoc/include' + autoload :Extend, 'rdoc/extend' + autoload :Require, 'rdoc/require' + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/alias.rb b/ruby/lib/ruby/2.1.0/rdoc/alias.rb new file mode 100644 index 0000000..39d2694 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/alias.rb @@ -0,0 +1,111 @@ +## +# Represent an alias, which is an old_name/new_name pair associated with a +# particular context +#-- +# TODO implement Alias as a proxy to a method/attribute, inheriting from +# MethodAttr + +class RDoc::Alias < RDoc::CodeObject + + ## + # Aliased method's name + + attr_reader :new_name + + alias name new_name + + ## + # Aliasee method's name + + attr_reader :old_name + + ## + # Is this an alias declared in a singleton context? + + attr_accessor :singleton + + ## + # Source file token stream + + attr_reader :text + + ## + # Creates a new Alias with a token stream of +text+ that aliases +old_name+ + # to +new_name+, has +comment+ and is a +singleton+ context. + + def initialize(text, old_name, new_name, comment, singleton = false) + super() + + @text = text + @singleton = singleton + @old_name = old_name + @new_name = new_name + self.comment = comment + end + + ## + # Order by #singleton then #new_name + + def <=>(other) + [@singleton ? 0 : 1, new_name] <=> [other.singleton ? 0 : 1, other.new_name] + end + + ## + # HTML fragment reference for this alias + + def aref + type = singleton ? 'c' : 'i' + "#alias-#{type}-#{html_name}" + end + + ## + # Full old name including namespace + + def full_old_name + @full_name || "#{parent.name}#{pretty_old_name}" + end + + ## + # HTML id-friendly version of +#new_name+. + + def html_name + CGI.escape(@new_name.gsub('-', '-2D')).gsub('%','-').sub(/^-/, '') + end + + def inspect # :nodoc: + parent_name = parent ? parent.name : '(unknown)' + "#<%s:0x%x %s.alias_method %s, %s>" % [ + self.class, object_id, + parent_name, @old_name, @new_name, + ] + end + + ## + # '::' for the alias of a singleton method/attribute, '#' for instance-level. + + def name_prefix + singleton ? '::' : '#' + end + + ## + # Old name with prefix '::' or '#'. + + def pretty_old_name + "#{singleton ? '::' : '#'}#{@old_name}" + end + + ## + # New name with prefix '::' or '#'. + + def pretty_new_name + "#{singleton ? '::' : '#'}#{@new_name}" + end + + alias pretty_name pretty_new_name + + def to_s # :nodoc: + "alias: #{self.new_name} -> #{self.pretty_old_name} in: #{parent}" + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/anon_class.rb b/ruby/lib/ruby/2.1.0/rdoc/anon_class.rb new file mode 100644 index 0000000..c23d8e5 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/anon_class.rb @@ -0,0 +1,10 @@ +## +# An anonymous class like: +# +# c = Class.new do end +# +# AnonClass is currently not used. + +class RDoc::AnonClass < RDoc::ClassModule +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/any_method.rb b/ruby/lib/ruby/2.1.0/rdoc/any_method.rb new file mode 100644 index 0000000..3afafc8 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/any_method.rb @@ -0,0 +1,308 @@ +## +# AnyMethod is the base class for objects representing methods + +class RDoc::AnyMethod < RDoc::MethodAttr + + ## + # 2:: + # RDoc 4 + # Added calls_super + # Added parent name and class + # Added section title + # 3:: + # RDoc 4.1 + # Added is_alias_for + + MARSHAL_VERSION = 3 # :nodoc: + + ## + # Don't rename \#initialize to \::new + + attr_accessor :dont_rename_initialize + + ## + # The C function that implements this method (if it was defined in a C file) + + attr_accessor :c_function + + ## + # Different ways to call this method + + attr_reader :call_seq + + ## + # Parameters for this method + + attr_accessor :params + + ## + # If true this method uses +super+ to call a superclass version + + attr_accessor :calls_super + + include RDoc::TokenStream + + ## + # Creates a new AnyMethod with a token stream +text+ and +name+ + + def initialize text, name + super + + @c_function = nil + @dont_rename_initialize = false + @token_stream = nil + @calls_super = false + @superclass_method = nil + end + + ## + # Adds +an_alias+ as an alias for this method in +context+. + + def add_alias an_alias, context = nil + method = self.class.new an_alias.text, an_alias.new_name + + method.record_location an_alias.file + method.singleton = self.singleton + method.params = self.params + method.visibility = self.visibility + method.comment = an_alias.comment + method.is_alias_for = self + @aliases << method + context.add_method method if context + method + end + + ## + # Prefix for +aref+ is 'method'. + + def aref_prefix + 'method' + end + + ## + # The call_seq or the param_seq with method name, if there is no call_seq. + # + # Use this for displaying a method's argument lists. + + def arglists + if @call_seq then + @call_seq + elsif @params then + "#{name}#{param_seq}" + end + end + + ## + # Sets the different ways you can call this method. If an empty +call_seq+ + # is given nil is assumed. + # + # See also #param_seq + + def call_seq= call_seq + return if call_seq.empty? + + @call_seq = call_seq + end + + ## + # Loads is_alias_for from the internal name. Returns nil if the alias + # cannot be found. + + def is_alias_for # :nodoc: + case @is_alias_for + when RDoc::MethodAttr then + @is_alias_for + when Array then + return nil unless @store + + klass_name, singleton, method_name = @is_alias_for + + return nil unless klass = @store.find_class_or_module(klass_name) + + @is_alias_for = klass.find_method method_name, singleton + end + end + + ## + # Dumps this AnyMethod for use by ri. See also #marshal_load + + def marshal_dump + aliases = @aliases.map do |a| + [a.name, parse(a.comment)] + end + + is_alias_for = [ + @is_alias_for.parent.full_name, + @is_alias_for.singleton, + @is_alias_for.name + ] if @is_alias_for + + [ MARSHAL_VERSION, + @name, + full_name, + @singleton, + @visibility, + parse(@comment), + @call_seq, + @block_params, + aliases, + @params, + @file.relative_name, + @calls_super, + @parent.name, + @parent.class, + @section.title, + is_alias_for, + ] + end + + ## + # Loads this AnyMethod from +array+. For a loaded AnyMethod the following + # methods will return cached values: + # + # * #full_name + # * #parent_name + + def marshal_load array + initialize_visibility + + @dont_rename_initialize = nil + @token_stream = nil + @aliases = [] + @parent = nil + @parent_name = nil + @parent_class = nil + @section = nil + @file = nil + + version = array[0] + @name = array[1] + @full_name = array[2] + @singleton = array[3] + @visibility = array[4] + @comment = array[5] + @call_seq = array[6] + @block_params = array[7] + # 8 handled below + @params = array[9] + # 10 handled below + @calls_super = array[11] + @parent_name = array[12] + @parent_title = array[13] + @section_title = array[14] + @is_alias_for = array[15] + + array[8].each do |new_name, comment| + add_alias RDoc::Alias.new(nil, @name, new_name, comment, @singleton) + end + + @parent_name ||= if @full_name =~ /#/ then + $` + else + name = @full_name.split('::') + name.pop + name.join '::' + end + + @file = RDoc::TopLevel.new array[10] if version > 0 + end + + ## + # Method name + # + # If the method has no assigned name, it extracts it from #call_seq. + + def name + return @name if @name + + @name = + @call_seq[/^.*?\.(\w+)/, 1] || + @call_seq[/^.*?(\w+)/, 1] || + @call_seq if @call_seq + end + + ## + # A list of this method's method and yield parameters. +call-seq+ params + # are preferred over parsed method and block params. + + def param_list + if @call_seq then + params = @call_seq.split("\n").last + params = params.sub(/.*?\((.*)\)/, '\1') + params = params.sub(/(\{|do)\s*\|([^|]*)\|.*/, ',\2') + elsif @params then + params = @params.sub(/\((.*)\)/, '\1') + + params << ",#{@block_params}" if @block_params + elsif @block_params then + params = @block_params + else + return [] + end + + params = params.gsub(/\s+/, '').split ',' + + params.map { |param| param.sub(/=.*/, '') } + end + + ## + # Pretty parameter list for this method. If the method's parameters were + # given by +call-seq+ it is preferred over the parsed values. + + def param_seq + if @call_seq then + params = @call_seq.split("\n").last + params = params.sub(/[^( ]+/, '') + params = params.sub(/(\|[^|]+\|)\s*\.\.\.\s*(end|\})/, '\1 \2') + elsif @params then + params = @params.gsub(/\s*\#.*/, '') + params = params.tr("\n", " ").squeeze(" ") + params = "(#{params})" unless params[0] == ?( + else + params = '' + end + + if @block_params then + # If this method has explicit block parameters, remove any explicit + # &block + params.sub!(/,?\s*&\w+/, '') + + block = @block_params.gsub(/\s*\#.*/, '') + block = block.tr("\n", " ").squeeze(" ") + if block[0] == ?( + block.sub!(/^\(/, '').sub!(/\)/, '') + end + params << " { |#{block}| ... }" + end + + params + end + + ## + # Sets the store for this method and its referenced code objects. + + def store= store + super + + @file = @store.add_file @file.full_name if @file + end + + ## + # For methods that +super+, find the superclass method that would be called. + + def superclass_method + return unless @calls_super + return @superclass_method if @superclass_method + + parent.each_ancestor do |ancestor| + if method = ancestor.method_list.find { |m| m.name == @name } then + @superclass_method = method + break + end + end + + @superclass_method + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/attr.rb b/ruby/lib/ruby/2.1.0/rdoc/attr.rb new file mode 100644 index 0000000..960e1d1 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/attr.rb @@ -0,0 +1,175 @@ +## +# An attribute created by \#attr, \#attr_reader, \#attr_writer or +# \#attr_accessor + +class RDoc::Attr < RDoc::MethodAttr + + ## + # 3:: + # RDoc 4 + # Added parent name and class + # Added section title + + MARSHAL_VERSION = 3 # :nodoc: + + ## + # Is the attribute readable ('R'), writable ('W') or both ('RW')? + + attr_accessor :rw + + ## + # Creates a new Attr with body +text+, +name+, read/write status +rw+ and + # +comment+. +singleton+ marks this as a class attribute. + + def initialize(text, name, rw, comment, singleton = false) + super text, name + + @rw = rw + @singleton = singleton + self.comment = comment + end + + ## + # Attributes are equal when their names, singleton and rw are identical + + def == other + self.class == other.class and + self.name == other.name and + self.rw == other.rw and + self.singleton == other.singleton + end + + ## + # Add +an_alias+ as an attribute in +context+. + + def add_alias(an_alias, context) + new_attr = self.class.new(self.text, an_alias.new_name, self.rw, + self.comment, self.singleton) + + new_attr.record_location an_alias.file + new_attr.visibility = self.visibility + new_attr.is_alias_for = self + @aliases << new_attr + context.add_attribute new_attr + new_attr + end + + ## + # The #aref prefix for attributes + + def aref_prefix + 'attribute' + end + + ## + # Attributes never call super. See RDoc::AnyMethod#calls_super + # + # An RDoc::Attr can show up in the method list in some situations (see + # Gem::ConfigFile) + + def calls_super # :nodoc: + false + end + + ## + # Returns attr_reader, attr_writer or attr_accessor as appropriate. + + def definition + case @rw + when 'RW' then 'attr_accessor' + when 'R' then 'attr_reader' + when 'W' then 'attr_writer' + end + end + + def inspect # :nodoc: + alias_for = @is_alias_for ? " (alias for #{@is_alias_for.name})" : nil + visibility = self.visibility + visibility = "forced #{visibility}" if force_documentation + "#<%s:0x%x %s %s (%s)%s>" % [ + self.class, object_id, + full_name, + rw, + visibility, + alias_for, + ] + end + + ## + # Dumps this Attr for use by ri. See also #marshal_load + + def marshal_dump + [ MARSHAL_VERSION, + @name, + full_name, + @rw, + @visibility, + parse(@comment), + singleton, + @file.relative_name, + @parent.full_name, + @parent.class, + @section.title + ] + end + + ## + # Loads this Attr from +array+. For a loaded Attr the following + # methods will return cached values: + # + # * #full_name + # * #parent_name + + def marshal_load array + initialize_visibility + + @aliases = [] + @parent = nil + @parent_name = nil + @parent_class = nil + @section = nil + @file = nil + + version = array[0] + @name = array[1] + @full_name = array[2] + @rw = array[3] + @visibility = array[4] + @comment = array[5] + @singleton = array[6] || false # MARSHAL_VERSION == 0 + # 7 handled below + @parent_name = array[8] + @parent_class = array[9] + @section_title = array[10] + + @file = RDoc::TopLevel.new array[7] if version > 1 + + @parent_name ||= @full_name.split('#', 2).first + end + + def pretty_print q # :nodoc: + q.group 2, "[#{self.class.name} #{full_name} #{rw} #{visibility}", "]" do + unless comment.empty? then + q.breakable + q.text "comment:" + q.breakable + q.pp @comment + end + end + end + + def to_s # :nodoc: + "#{definition} #{name} in: #{parent}" + end + + ## + # Attributes do not have token streams. + # + # An RDoc::Attr can show up in the method list in some situations (see + # Gem::ConfigFile) + + def token_stream # :nodoc: + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/class_module.rb b/ruby/lib/ruby/2.1.0/rdoc/class_module.rb new file mode 100644 index 0000000..71566f0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/class_module.rb @@ -0,0 +1,799 @@ +## +# ClassModule is the base class for objects representing either a class or a +# module. + +class RDoc::ClassModule < RDoc::Context + + ## + # 1:: + # RDoc 3.7 + # * Added visibility, singleton and file to attributes + # * Added file to constants + # * Added file to includes + # * Added file to methods + # 2:: + # RDoc 3.13 + # * Added extends + # 3:: + # RDoc 4.0 + # * Added sections + # * Added in_files + # * Added parent name + # * Complete Constant dump + + MARSHAL_VERSION = 3 # :nodoc: + + ## + # Constants that are aliases for this class or module + + attr_accessor :constant_aliases + + ## + # Comment and the location it came from. Use #add_comment to add comments + + attr_accessor :comment_location + + attr_accessor :diagram # :nodoc: + + ## + # Class or module this constant is an alias for + + attr_accessor :is_alias_for + + ## + # Return a RDoc::ClassModule of class +class_type+ that is a copy + # of module +module+. Used to promote modules to classes. + #-- + # TODO move to RDoc::NormalClass (I think) + + def self.from_module class_type, mod + klass = class_type.new mod.name + + mod.comment_location.each do |comment, location| + klass.add_comment comment, location + end + + klass.parent = mod.parent + klass.section = mod.section + klass.viewer = mod.viewer + + klass.attributes.concat mod.attributes + klass.method_list.concat mod.method_list + klass.aliases.concat mod.aliases + klass.external_aliases.concat mod.external_aliases + klass.constants.concat mod.constants + klass.includes.concat mod.includes + klass.extends.concat mod.extends + + klass.methods_hash.update mod.methods_hash + klass.constants_hash.update mod.constants_hash + + klass.current_section = mod.current_section + klass.in_files.concat mod.in_files + klass.sections.concat mod.sections + klass.unmatched_alias_lists = mod.unmatched_alias_lists + klass.current_section = mod.current_section + klass.visibility = mod.visibility + + klass.classes_hash.update mod.classes_hash + klass.modules_hash.update mod.modules_hash + klass.metadata.update mod.metadata + + klass.document_self = mod.received_nodoc ? nil : mod.document_self + klass.document_children = mod.document_children + klass.force_documentation = mod.force_documentation + klass.done_documenting = mod.done_documenting + + # update the parent of all children + + (klass.attributes + + klass.method_list + + klass.aliases + + klass.external_aliases + + klass.constants + + klass.includes + + klass.extends + + klass.classes + + klass.modules).each do |obj| + obj.parent = klass + obj.full_name = nil + end + + klass + end + + ## + # Creates a new ClassModule with +name+ with optional +superclass+ + # + # This is a constructor for subclasses, and must never be called directly. + + def initialize(name, superclass = nil) + @constant_aliases = [] + @diagram = nil + @is_alias_for = nil + @name = name + @superclass = superclass + @comment_location = [] # [[comment, location]] + + super() + end + + ## + # Adds +comment+ to this ClassModule's list of comments at +location+. This + # method is preferred over #comment= since it allows ri data to be updated + # across multiple runs. + + def add_comment comment, location + return unless document_self + + original = comment + + comment = case comment + when RDoc::Comment then + comment.normalize + else + normalize_comment comment + end + + @comment_location.delete_if { |(_, l)| l == location } + + @comment_location << [comment, location] + + self.comment = original + end + + def add_things my_things, other_things # :nodoc: + other_things.each do |group, things| + my_things[group].each { |thing| yield false, thing } if + my_things.include? group + + things.each do |thing| + yield true, thing + end + end + end + + ## + # Ancestors list for this ClassModule: the list of included modules + # (classes will add their superclass if any). + # + # Returns the included classes or modules, not the includes + # themselves. The returned values are either String or + # RDoc::NormalModule instances (see RDoc::Include#module). + # + # The values are returned in reverse order of their inclusion, + # which is the order suitable for searching methods/attributes + # in the ancestors. The superclass, if any, comes last. + + def ancestors + includes.map { |i| i.module }.reverse + end + + def aref_prefix # :nodoc: + raise NotImplementedError, "missing aref_prefix for #{self.class}" + end + + ## + # HTML fragment reference for this module or class. See + # RDoc::NormalClass#aref and RDoc::NormalModule#aref + + def aref + "#{aref_prefix}-#{full_name}" + end + + ## + # Ancestors of this class or module only + + alias direct_ancestors ancestors + + ## + # Clears the comment. Used by the Ruby parser. + + def clear_comment + @comment = '' + end + + ## + # This method is deprecated, use #add_comment instead. + # + # Appends +comment+ to the current comment, but separated by a rule. Works + # more like +=. + + def comment= comment # :nodoc: + comment = case comment + when RDoc::Comment then + comment.normalize + else + normalize_comment comment + end + + comment = "#{@comment}\n---\n#{comment}" unless @comment.empty? + + super comment + end + + ## + # Prepares this ClassModule for use by a generator. + # + # See RDoc::Store#complete + + def complete min_visibility + update_aliases + remove_nodoc_children + update_includes + remove_invisible min_visibility + end + + ## + # Does this ClassModule or any of its methods have document_self set? + + def document_self_or_methods + document_self || method_list.any?{ |m| m.document_self } + end + + ## + # Does this class or module have a comment with content or is + # #received_nodoc true? + + def documented? + return true if @received_nodoc + return false if @comment_location.empty? + @comment_location.any? { |comment, _| not comment.empty? } + end + + ## + # Iterates the ancestors of this class or module for which an + # RDoc::ClassModule exists. + + def each_ancestor # :yields: module + return enum_for __method__ unless block_given? + + ancestors.each do |mod| + next if String === mod + next if self == mod + yield mod + end + end + + ## + # Looks for a symbol in the #ancestors. See Context#find_local_symbol. + + def find_ancestor_local_symbol symbol + each_ancestor do |m| + res = m.find_local_symbol(symbol) + return res if res + end + + nil + end + + ## + # Finds a class or module with +name+ in this namespace or its descendants + + def find_class_named name + return self if full_name == name + return self if @name == name + + @classes.values.find do |klass| + next if klass == self + klass.find_class_named name + end + end + + ## + # Return the fully qualified name of this class or module + + def full_name + @full_name ||= if RDoc::ClassModule === parent then + "#{parent.full_name}::#{@name}" + else + @name + end + end + + ## + # TODO: filter included items by #display? + + def marshal_dump # :nodoc: + attrs = attributes.sort.map do |attr| + next unless attr.display? + [ attr.name, attr.rw, + attr.visibility, attr.singleton, attr.file_name, + ] + end.compact + + method_types = methods_by_type.map do |type, visibilities| + visibilities = visibilities.map do |visibility, methods| + method_names = methods.map do |method| + next unless method.display? + [method.name, method.file_name] + end.compact + + [visibility, method_names.uniq] + end + + [type, visibilities] + end + + [ MARSHAL_VERSION, + @name, + full_name, + @superclass, + parse(@comment_location), + attrs, + constants.select { |constant| constant.display? }, + includes.map do |incl| + next unless incl.display? + [incl.name, parse(incl.comment), incl.file_name] + end.compact, + method_types, + extends.map do |ext| + next unless ext.display? + [ext.name, parse(ext.comment), ext.file_name] + end.compact, + @sections.values, + @in_files.map do |tl| + tl.relative_name + end, + parent.full_name, + parent.class, + ] + end + + def marshal_load array # :nodoc: + initialize_visibility + initialize_methods_etc + @current_section = nil + @document_self = true + @done_documenting = false + @parent = nil + @temporary_section = nil + @visibility = nil + @classes = {} + @modules = {} + + @name = array[1] + @full_name = array[2] + @superclass = array[3] + @comment = array[4] + + @comment_location = if RDoc::Markup::Document === @comment.parts.first then + @comment + else + RDoc::Markup::Document.new @comment + end + + array[5].each do |name, rw, visibility, singleton, file| + singleton ||= false + visibility ||= :public + + attr = RDoc::Attr.new nil, name, rw, nil, singleton + + add_attribute attr + attr.visibility = visibility + attr.record_location RDoc::TopLevel.new file + end + + array[6].each do |constant, comment, file| + case constant + when RDoc::Constant then + add_constant constant + else + constant = add_constant RDoc::Constant.new(constant, nil, comment) + constant.record_location RDoc::TopLevel.new file + end + end + + array[7].each do |name, comment, file| + incl = add_include RDoc::Include.new(name, comment) + incl.record_location RDoc::TopLevel.new file + end + + array[8].each do |type, visibilities| + visibilities.each do |visibility, methods| + @visibility = visibility + + methods.each do |name, file| + method = RDoc::AnyMethod.new nil, name + method.singleton = true if type == 'class' + method.record_location RDoc::TopLevel.new file + add_method method + end + end + end + + array[9].each do |name, comment, file| + ext = add_extend RDoc::Extend.new(name, comment) + ext.record_location RDoc::TopLevel.new file + end if array[9] # Support Marshal version 1 + + sections = (array[10] || []).map do |section| + [section.title, section] + end + + @sections = Hash[*sections.flatten] + @current_section = add_section nil + + @in_files = [] + + (array[11] || []).each do |filename| + record_location RDoc::TopLevel.new filename + end + + @parent_name = array[12] + @parent_class = array[13] + end + + ## + # Merges +class_module+ into this ClassModule. + # + # The data in +class_module+ is preferred over the receiver. + + def merge class_module + @parent = class_module.parent + @parent_name = class_module.parent_name + + other_document = parse class_module.comment_location + + if other_document then + document = parse @comment_location + + document = document.merge other_document + + @comment = @comment_location = document + end + + cm = class_module + other_files = cm.in_files + + merge_collections attributes, cm.attributes, other_files do |add, attr| + if add then + add_attribute attr + else + @attributes.delete attr + @methods_hash.delete attr.pretty_name + end + end + + merge_collections constants, cm.constants, other_files do |add, const| + if add then + add_constant const + else + @constants.delete const + @constants_hash.delete const.name + end + end + + merge_collections includes, cm.includes, other_files do |add, incl| + if add then + add_include incl + else + @includes.delete incl + end + end + + @includes.uniq! # clean up + + merge_collections extends, cm.extends, other_files do |add, ext| + if add then + add_extend ext + else + @extends.delete ext + end + end + + @extends.uniq! # clean up + + merge_collections method_list, cm.method_list, other_files do |add, meth| + if add then + add_method meth + else + @method_list.delete meth + @methods_hash.delete meth.pretty_name + end + end + + merge_sections cm + + self + end + + ## + # Merges collection +mine+ with +other+ preferring other. +other_files+ is + # used to help determine which items should be deleted. + # + # Yields whether the item should be added or removed (true or false) and the + # item to be added or removed. + # + # merge_collections things, other.things, other.in_files do |add, thing| + # if add then + # # add the thing + # else + # # remove the thing + # end + # end + + def merge_collections mine, other, other_files, &block # :nodoc: + my_things = mine. group_by { |thing| thing.file } + other_things = other.group_by { |thing| thing.file } + + remove_things my_things, other_files, &block + add_things my_things, other_things, &block + end + + ## + # Merges the comments in this ClassModule with the comments in the other + # ClassModule +cm+. + + def merge_sections cm # :nodoc: + my_sections = sections.group_by { |section| section.title } + other_sections = cm.sections.group_by { |section| section.title } + + other_files = cm.in_files + + remove_things my_sections, other_files do |_, section| + @sections.delete section.title + end + + other_sections.each do |group, sections| + if my_sections.include? group + my_sections[group].each do |my_section| + other_section = cm.sections_hash[group] + + my_comments = my_section.comments + other_comments = other_section.comments + + other_files = other_section.in_files + + merge_collections my_comments, other_comments, other_files do |add, comment| + if add then + my_section.add_comment comment + else + my_section.remove_comment comment + end + end + end + else + sections.each do |section| + add_section group, section.comments + end + end + end + end + + ## + # Does this object represent a module? + + def module? + false + end + + ## + # Allows overriding the initial name. + # + # Used for modules and classes that are constant aliases. + + def name= new_name + @name = new_name + end + + ## + # Parses +comment_location+ into an RDoc::Markup::Document composed of + # multiple RDoc::Markup::Documents with their file set. + + def parse comment_location + case comment_location + when String then + super + when Array then + docs = comment_location.map do |comment, location| + doc = super comment + doc.file = location + doc + end + + RDoc::Markup::Document.new(*docs) + when RDoc::Comment then + doc = super comment_location.text, comment_location.format + doc.file = comment_location.location + doc + when RDoc::Markup::Document then + return comment_location + else + raise ArgumentError, "unknown comment class #{comment_location.class}" + end + end + + ## + # Path to this class or module for use with HTML generator output. + + def path + http_url @store.rdoc.generator.class_dir + end + + ## + # Name to use to generate the url: + # modules and classes that are aliases for another + # module or class return the name of the latter. + + def name_for_path + is_alias_for ? is_alias_for.full_name : full_name + end + + ## + # Returns the classes and modules that are not constants + # aliasing another class or module. For use by formatters + # only (caches its result). + + def non_aliases + @non_aliases ||= classes_and_modules.reject { |cm| cm.is_alias_for } + end + + ## + # Updates the child modules or classes of class/module +parent+ by + # deleting the ones that have been removed from the documentation. + # + # +parent_hash+ is either parent.modules_hash or + # parent.classes_hash and +all_hash+ is ::all_modules_hash or + # ::all_classes_hash. + + def remove_nodoc_children + prefix = self.full_name + '::' + + modules_hash.each_key do |name| + full_name = prefix + name + modules_hash.delete name unless @store.modules_hash[full_name] + end + + classes_hash.each_key do |name| + full_name = prefix + name + classes_hash.delete name unless @store.classes_hash[full_name] + end + end + + def remove_things my_things, other_files # :nodoc: + my_things.delete_if do |file, things| + next false unless other_files.include? file + + things.each do |thing| + yield false, thing + end + + true + end + end + + ## + # Search record used by RDoc::Generator::JsonIndex + + def search_record + [ + name, + full_name, + full_name, + '', + path, + '', + snippet(@comment_location), + ] + end + + ## + # Sets the store for this class or module and its contained code objects. + + def store= store + super + + @attributes .each do |attr| attr.store = store end + @constants .each do |const| const.store = store end + @includes .each do |incl| incl.store = store end + @extends .each do |ext| ext.store = store end + @method_list.each do |meth| meth.store = store end + end + + ## + # Get the superclass of this class. Attempts to retrieve the superclass + # object, returns the name if it is not known. + + def superclass + @store.find_class_named(@superclass) || @superclass + end + + ## + # Set the superclass of this class to +superclass+ + + def superclass=(superclass) + raise NoMethodError, "#{full_name} is a module" if module? + @superclass = superclass + end + + def to_s # :nodoc: + if is_alias_for then + "#{self.class.name} #{self.full_name} -> #{is_alias_for}" + else + super + end + end + + ## + # 'module' or 'class' + + def type + module? ? 'module' : 'class' + end + + ## + # Updates the child modules & classes by replacing the ones that are + # aliases through a constant. + # + # The aliased module/class is replaced in the children and in + # RDoc::Store#modules_hash or RDoc::Store#classes_hash + # by a copy that has RDoc::ClassModule#is_alias_for set to + # the aliased module/class, and this copy is added to #aliases + # of the aliased module/class. + # + # Formatters can use the #non_aliases method to retrieve children that + # are not aliases, for instance to list the namespace content, since + # the aliased modules are included in the constants of the class/module, + # that are listed separately. + + def update_aliases + constants.each do |const| + next unless cm = const.is_alias_for + cm_alias = cm.dup + cm_alias.name = const.name + + # Don't move top-level aliases under Object, they look ugly there + unless RDoc::TopLevel === cm_alias.parent then + cm_alias.parent = self + cm_alias.full_name = nil # force update for new parent + end + + cm_alias.aliases.clear + cm_alias.is_alias_for = cm + + if cm.module? then + @store.modules_hash[cm_alias.full_name] = cm_alias + modules_hash[const.name] = cm_alias + else + @store.classes_hash[cm_alias.full_name] = cm_alias + classes_hash[const.name] = cm_alias + end + + cm.aliases << cm_alias + end + end + + ## + # Deletes from #includes those whose module has been removed from the + # documentation. + #-- + # FIXME: includes are not reliably removed, see _possible_bug test case + + def update_includes + includes.reject! do |include| + mod = include.module + !(String === mod) && @store.modules_hash[mod.full_name].nil? + end + + includes.uniq! + end + + ## + # Deletes from #extends those whose module has been removed from the + # documentation. + #-- + # FIXME: like update_includes, extends are not reliably removed + + def update_extends + extends.reject! do |ext| + mod = ext.module + + !(String === mod) && @store.modules_hash[mod.full_name].nil? + end + + extends.uniq! + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/code_object.rb b/ruby/lib/ruby/2.1.0/rdoc/code_object.rb new file mode 100644 index 0000000..4620fa5 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/code_object.rb @@ -0,0 +1,429 @@ +## +# Base class for the RDoc code tree. +# +# We contain the common stuff for contexts (which are containers) and other +# elements (methods, attributes and so on) +# +# Here's the tree of the CodeObject subclasses: +# +# * RDoc::Context +# * RDoc::TopLevel +# * RDoc::ClassModule +# * RDoc::AnonClass (never used so far) +# * RDoc::NormalClass +# * RDoc::NormalModule +# * RDoc::SingleClass +# * RDoc::MethodAttr +# * RDoc::Attr +# * RDoc::AnyMethod +# * RDoc::GhostMethod +# * RDoc::MetaMethod +# * RDoc::Alias +# * RDoc::Constant +# * RDoc::Mixin +# * RDoc::Require +# * RDoc::Include + +class RDoc::CodeObject + + include RDoc::Text + + ## + # Our comment + + attr_reader :comment + + ## + # Do we document our children? + + attr_reader :document_children + + ## + # Do we document ourselves? + + attr_reader :document_self + + ## + # Are we done documenting (ie, did we come across a :enddoc:)? + + attr_reader :done_documenting + + ## + # Which file this code object was defined in + + attr_reader :file + + ## + # Force documentation of this CodeObject + + attr_reader :force_documentation + + ## + # Line in #file where this CodeObject was defined + + attr_accessor :line + + ## + # Hash of arbitrary metadata for this CodeObject + + attr_reader :metadata + + ## + # Offset in #file where this CodeObject was defined + #-- + # TODO character or byte? + + attr_accessor :offset + + ## + # Sets the parent CodeObject + + attr_writer :parent + + ## + # Did we ever receive a +:nodoc:+ directive? + + attr_reader :received_nodoc + + ## + # Set the section this CodeObject is in + + attr_writer :section + + ## + # The RDoc::Store for this object. + + attr_reader :store + + ## + # We are the model of the code, but we know that at some point we will be + # worked on by viewers. By implementing the Viewable protocol, viewers can + # associated themselves with these objects. + + attr_accessor :viewer + + ## + # Creates a new CodeObject that will document itself and its children + + def initialize + @metadata = {} + @comment = '' + @parent = nil + @parent_name = nil # for loading + @parent_class = nil # for loading + @section = nil + @section_title = nil # for loading + @file = nil + @full_name = nil + @store = nil + @track_visibility = true + + initialize_visibility + end + + ## + # Initializes state for visibility of this CodeObject and its children. + + def initialize_visibility # :nodoc: + @document_children = true + @document_self = true + @done_documenting = false + @force_documentation = false + @received_nodoc = false + @ignored = false + @suppressed = false + @track_visibility = true + end + + ## + # Replaces our comment with +comment+, unless it is empty. + + def comment=(comment) + @comment = case comment + when NilClass then '' + when RDoc::Markup::Document then comment + when RDoc::Comment then comment.normalize + else + if comment and not comment.empty? then + normalize_comment comment + else + # HACK correct fix is to have #initialize create @comment + # with the correct encoding + if String === @comment and + Object.const_defined? :Encoding and @comment.empty? then + @comment.force_encoding comment.encoding + end + @comment + end + end + end + + ## + # Should this CodeObject be displayed in output? + # + # A code object should be displayed if: + # + # * The item didn't have a nodoc or wasn't in a container that had nodoc + # * The item wasn't ignored + # * The item has documentation and was not suppressed + + def display? + @document_self and not @ignored and + (documented? or not @suppressed) + end + + ## + # Enables or disables documentation of this CodeObject's children unless it + # has been turned off by :enddoc: + + def document_children=(document_children) + return unless @track_visibility + + @document_children = document_children unless @done_documenting + end + + ## + # Enables or disables documentation of this CodeObject unless it has been + # turned off by :enddoc:. If the argument is +nil+ it means the + # documentation is turned off by +:nodoc:+. + + def document_self=(document_self) + return unless @track_visibility + return if @done_documenting + + @document_self = document_self + @received_nodoc = true if document_self.nil? + end + + ## + # Does this object have a comment with content or is #received_nodoc true? + + def documented? + @received_nodoc or !@comment.empty? + end + + ## + # Turns documentation on/off, and turns on/off #document_self + # and #document_children. + # + # Once documentation has been turned off (by +:enddoc:+), + # the object will refuse to turn #document_self or + # #document_children on, so +:doc:+ and +:start_doc:+ directives + # will have no effect in the current file. + + def done_documenting=(value) + return unless @track_visibility + @done_documenting = value + @document_self = !value + @document_children = @document_self + end + + ## + # Yields each parent of this CodeObject. See also + # RDoc::ClassModule#each_ancestor + + def each_parent + code_object = self + + while code_object = code_object.parent do + yield code_object + end + + self + end + + ## + # File name where this CodeObject was found. + # + # See also RDoc::Context#in_files + + def file_name + return unless @file + + @file.absolute_name + end + + ## + # Force the documentation of this object unless documentation + # has been turned off by :enddoc: + #-- + # HACK untested, was assigning to an ivar + + def force_documentation=(value) + @force_documentation = value unless @done_documenting + end + + ## + # Sets the full_name overriding any computed full name. + # + # Set to +nil+ to clear RDoc's cached value + + def full_name= full_name + @full_name = full_name + end + + ## + # Use this to ignore a CodeObject and all its children until found again + # (#record_location is called). An ignored item will not be displayed in + # documentation. + # + # See github issue #55 + # + # The ignored status is temporary in order to allow implementation details + # to be hidden. At the end of processing a file RDoc allows all classes + # and modules to add new documentation to previously created classes. + # + # If a class was ignored (via stopdoc) then reopened later with additional + # documentation it should be displayed. If a class was ignored and never + # reopened it should not be displayed. The ignore flag allows this to + # occur. + + def ignore + return unless @track_visibility + + @ignored = true + + stop_doc + end + + ## + # Has this class been ignored? + # + # See also #ignore + + def ignored? + @ignored + end + + ## + # The options instance from the store this CodeObject is attached to, or a + # default options instance if the CodeObject is not attached. + # + # This is used by Text#snippet + + def options + if @store and @store.rdoc then + @store.rdoc.options + else + RDoc::Options.new + end + end + + ## + # Our parent CodeObject. The parent may be missing for classes loaded from + # legacy RI data stores. + + def parent + return @parent if @parent + return nil unless @parent_name + + if @parent_class == RDoc::TopLevel then + @parent = @store.add_file @parent_name + else + @parent = @store.find_class_or_module @parent_name + + return @parent if @parent + + begin + @parent = @store.load_class @parent_name + rescue RDoc::Store::MissingFileError + nil + end + end + end + + ## + # File name of our parent + + def parent_file_name + @parent ? @parent.base_name : '(unknown)' + end + + ## + # Name of our parent + + def parent_name + @parent ? @parent.full_name : '(unknown)' + end + + ## + # Records the RDoc::TopLevel (file) where this code object was defined + + def record_location top_level + @ignored = false + @suppressed = false + @file = top_level + end + + ## + # The section this CodeObject is in. Sections allow grouping of constants, + # attributes and methods inside a class or module. + + def section + return @section if @section + + @section = parent.add_section @section_title if parent + end + + ## + # Enable capture of documentation unless documentation has been + # turned off by :enddoc: + + def start_doc + return if @done_documenting + + @document_self = true + @document_children = true + @ignored = false + @suppressed = false + end + + ## + # Disable capture of documentation + + def stop_doc + return unless @track_visibility + + @document_self = false + @document_children = false + end + + ## + # Sets the +store+ that contains this CodeObject + + def store= store + @store = store + + return unless @track_visibility + + if :nodoc == options.visibility then + initialize_visibility + @track_visibility = false + end + end + + ## + # Use this to suppress a CodeObject and all its children until the next file + # it is seen in or documentation is discovered. A suppressed item with + # documentation will be displayed while an ignored item with documentation + # may not be displayed. + + def suppress + return unless @track_visibility + + @suppressed = true + + stop_doc + end + + ## + # Has this class been suppressed? + # + # See also #suppress + + def suppressed? + @suppressed + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/code_objects.rb b/ruby/lib/ruby/2.1.0/rdoc/code_objects.rb new file mode 100644 index 0000000..f1a626c --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/code_objects.rb @@ -0,0 +1,5 @@ +# This file was used to load all the RDoc::CodeObject subclasses at once. Now +# autoload handles this. + +require 'rdoc' + diff --git a/ruby/lib/ruby/2.1.0/rdoc/comment.rb b/ruby/lib/ruby/2.1.0/rdoc/comment.rb new file mode 100644 index 0000000..33ced18 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/comment.rb @@ -0,0 +1,229 @@ +## +# A comment holds the text comment for a RDoc::CodeObject and provides a +# unified way of cleaning it up and parsing it into an RDoc::Markup::Document. +# +# Each comment may have a different markup format set by #format=. By default +# 'rdoc' is used. The :markup: directive tells RDoc which format to use. +# +# See RDoc::Markup@Other+directives for instructions on adding an alternate +# format. + +class RDoc::Comment + + include RDoc::Text + + ## + # The format of this comment. Defaults to RDoc::Markup + + attr_reader :format + + ## + # The RDoc::TopLevel this comment was found in + + attr_accessor :location + + ## + # For duck-typing when merging classes at load time + + alias file location # :nodoc: + + ## + # The text for this comment + + attr_reader :text + + ## + # Overrides the content returned by #parse. Use when there is no #text + # source for this comment + + attr_writer :document + + ## + # Creates a new comment with +text+ that is found in the RDoc::TopLevel + # +location+. + + def initialize text = nil, location = nil + @location = location + @text = text + + @document = nil + @format = 'rdoc' + @normalized = false + end + + ## + #-- + # TODO deep copy @document + + def initialize_copy copy # :nodoc: + @text = copy.text.dup + end + + def == other # :nodoc: + self.class === other and + other.text == @text and other.location == @location + end + + ## + # Look for a 'call-seq' in the comment to override the normal parameter + # handling. The :call-seq: is indented from the baseline. All lines of the + # same indentation level and prefix are consumed. + # + # For example, all of the following will be used as the :call-seq: + # + # # :call-seq: + # # ARGF.readlines(sep=$/) -> array + # # ARGF.readlines(limit) -> array + # # ARGF.readlines(sep, limit) -> array + # # + # # ARGF.to_a(sep=$/) -> array + # # ARGF.to_a(limit) -> array + # # ARGF.to_a(sep, limit) -> array + + def extract_call_seq method + # we must handle situations like the above followed by an unindented first + # comment. The difficulty is to make sure not to match lines starting + # with ARGF at the same indent, but that are after the first description + # paragraph. + if @text =~ /^\s*:?call-seq:(.*?(?:\S).*?)^\s*$/m then + all_start, all_stop = $~.offset(0) + seq_start, seq_stop = $~.offset(1) + + # we get the following lines that start with the leading word at the + # same indent, even if they have blank lines before + if $1 =~ /(^\s*\n)+^(\s*\w+)/m then + leading = $2 # ' * ARGF' in the example above + re = %r% + \A( + (^\s*\n)+ + (^#{Regexp.escape leading}.*?\n)+ + )+ + ^\s*$ + %xm + + if @text[seq_stop..-1] =~ re then + all_stop = seq_stop + $~.offset(0).last + seq_stop = seq_stop + $~.offset(1).last + end + end + + seq = @text[seq_start..seq_stop] + seq.gsub!(/^\s*(\S|\n)/m, '\1') + @text.slice! all_start...all_stop + + method.call_seq = seq.chomp + + elsif @text.sub!(/^\s*:?call-seq:(.*?)(^\s*$|\z)/m, '') then + seq = $1 + seq.gsub!(/^\s*/, '') + method.call_seq = seq + end + + method + end + + ## + # A comment is empty if its text String is empty. + + def empty? + @text.empty? + end + + ## + # HACK dubious + + def force_encoding encoding + @text.force_encoding encoding + end + + ## + # Sets the format of this comment and resets any parsed document + + def format= format + @format = format + @document = nil + end + + def inspect # :nodoc: + location = @location ? @location.relative_name : '(unknown)' + + "#<%s:%x %s %p>" % [self.class, object_id, location, @text] + end + + ## + # Normalizes the text. See RDoc::Text#normalize_comment for details + + def normalize + return self unless @text + return self if @normalized # TODO eliminate duplicate normalization + + @text = normalize_comment @text + + @normalized = true + + self + end + + ## + # Was this text normalized? + + def normalized? # :nodoc: + @normalized + end + + ## + # Parses the comment into an RDoc::Markup::Document. The parsed document is + # cached until the text is changed. + + def parse + return @document if @document + + @document = super @text, @format + @document.file = @location + @document + end + + ## + # Removes private sections from this comment. Private sections are flush to + # the comment marker and start with -- and end with ++. + # For C-style comments, a private marker may not start at the opening of the + # comment. + # + # /* + # *-- + # * private + # *++ + # * public + # */ + + def remove_private + # Workaround for gsub encoding for Ruby 1.9.2 and earlier + empty = '' + empty.force_encoding @text.encoding if Object.const_defined? :Encoding + + @text = @text.gsub(%r%^\s*([#*]?)--.*?^\s*(\1)\+\+\n?%m, empty) + @text = @text.sub(%r%^\s*[#*]?--.*%m, '') + end + + ## + # Replaces this comment's text with +text+ and resets the parsed document. + # + # An error is raised if the comment contains a document but no text. + + def text= text + raise RDoc::Error, 'replacing document-only comment is not allowed' if + @text.nil? and @document + + @document = nil + @text = text + end + + ## + # Returns true if this comment is in TomDoc format. + + def tomdoc? + @format == 'tomdoc' + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/constant.rb b/ruby/lib/ruby/2.1.0/rdoc/constant.rb new file mode 100644 index 0000000..97985cb --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/constant.rb @@ -0,0 +1,186 @@ +## +# A constant + +class RDoc::Constant < RDoc::CodeObject + + MARSHAL_VERSION = 0 # :nodoc: + + ## + # Sets the module or class this is constant is an alias for. + + attr_writer :is_alias_for + + ## + # The constant's name + + attr_accessor :name + + ## + # The constant's value + + attr_accessor :value + + ## + # The constant's visibility + + attr_accessor :visibility + + ## + # Creates a new constant with +name+, +value+ and +comment+ + + def initialize(name, value, comment) + super() + + @name = name + @value = value + + @is_alias_for = nil + @visibility = nil + + self.comment = comment + end + + ## + # Constants are ordered by name + + def <=> other + return unless self.class === other + + [parent_name, name] <=> [other.parent_name, other.name] + end + + ## + # Constants are equal when their #parent and #name is the same + + def == other + self.class == other.class and + @parent == other.parent and + @name == other.name + end + + ## + # A constant is documented if it has a comment, or is an alias + # for a documented class or module. + + def documented? + return true if super + return false unless @is_alias_for + case @is_alias_for + when String then + found = @store.find_class_or_module @is_alias_for + return false unless found + @is_alias_for = found + end + @is_alias_for.documented? + end + + ## + # Full constant name including namespace + + def full_name + @full_name ||= "#{parent_name}::#{@name}" + end + + ## + # The module or class this constant is an alias for + + def is_alias_for + case @is_alias_for + when String then + found = @store.find_class_or_module @is_alias_for + @is_alias_for = found if found + @is_alias_for + else + @is_alias_for + end + end + + def inspect # :nodoc: + "#<%s:0x%x %s::%s>" % [ + self.class, object_id, + parent_name, @name, + ] + end + + ## + # Dumps this Constant for use by ri. See also #marshal_load + + def marshal_dump + alias_name = case found = is_alias_for + when RDoc::CodeObject then found.full_name + else found + end + + [ MARSHAL_VERSION, + @name, + full_name, + @visibility, + alias_name, + parse(@comment), + @file.relative_name, + parent.name, + parent.class, + section.title, + ] + end + + ## + # Loads this Constant from +array+. For a loaded Constant the following + # methods will return cached values: + # + # * #full_name + # * #parent_name + + def marshal_load array + initialize array[1], nil, array[5] + + @full_name = array[2] + @visibility = array[3] + @is_alias_for = array[4] + # 5 handled above + # 6 handled below + @parent_name = array[7] + @parent_class = array[8] + @section_title = array[9] + + @file = RDoc::TopLevel.new array[6] + end + + ## + # Path to this constant for use with HTML generator output. + + def path + "#{@parent.path}##{@name}" + end + + def pretty_print q # :nodoc: + q.group 2, "[#{self.class.name} #{full_name}", "]" do + unless comment.empty? then + q.breakable + q.text "comment:" + q.breakable + q.pp @comment + end + end + end + + ## + # Sets the store for this class or module and its contained code objects. + + def store= store + super + + @file = @store.add_file @file.full_name if @file + end + + def to_s # :nodoc: + parent_name = parent ? parent.full_name : '(unknown)' + if is_alias_for + "constant #{parent_name}::#@name -> #{is_alias_for}" + else + "constant #{parent_name}::#@name" + end + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/context.rb b/ruby/lib/ruby/2.1.0/rdoc/context.rb new file mode 100644 index 0000000..892a43e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/context.rb @@ -0,0 +1,1208 @@ +require 'cgi' + +## +# A Context is something that can hold modules, classes, methods, attributes, +# aliases, requires, and includes. Classes, modules, and files are all +# Contexts. + +class RDoc::Context < RDoc::CodeObject + + include Comparable + + ## + # Types of methods + + TYPES = %w[class instance] + + ## + # If a context has these titles it will be sorted in this order. + + TOMDOC_TITLES = [nil, 'Public', 'Internal', 'Deprecated'] # :nodoc: + TOMDOC_TITLES_SORT = TOMDOC_TITLES.sort_by { |title| title.to_s } # :nodoc: + + ## + # Class/module aliases + + attr_reader :aliases + + ## + # All attr* methods + + attr_reader :attributes + + ## + # Block params to be used in the next MethodAttr parsed under this context + + attr_accessor :block_params + + ## + # Constants defined + + attr_reader :constants + + ## + # Sets the current documentation section of documentation + + attr_writer :current_section + + ## + # Files this context is found in + + attr_reader :in_files + + ## + # Modules this context includes + + attr_reader :includes + + ## + # Modules this context is extended with + + attr_reader :extends + + ## + # Methods defined in this context + + attr_reader :method_list + + ## + # Name of this class excluding namespace. See also full_name + + attr_reader :name + + ## + # Files this context requires + + attr_reader :requires + + ## + # Use this section for the next method, attribute or constant added. + + attr_accessor :temporary_section + + ## + # Hash old_name => [aliases], for aliases + # that haven't (yet) been resolved to a method/attribute. + # (Not to be confused with the aliases of the context.) + + attr_accessor :unmatched_alias_lists + + ## + # Aliases that could not be resolved. + + attr_reader :external_aliases + + ## + # Current visibility of this context + + attr_accessor :visibility + + ## + # Hash of registered methods. Attributes are also registered here, + # twice if they are RW. + + attr_reader :methods_hash + + ## + # Params to be used in the next MethodAttr parsed under this context + + attr_accessor :params + + ## + # Hash of registered constants. + + attr_reader :constants_hash + + ## + # Creates an unnamed empty context with public current visibility + + def initialize + super + + @in_files = [] + + @name ||= "unknown" + @parent = nil + @visibility = :public + + @current_section = Section.new self, nil, nil + @sections = { nil => @current_section } + @temporary_section = nil + + @classes = {} + @modules = {} + + initialize_methods_etc + end + + ## + # Sets the defaults for methods and so-forth + + def initialize_methods_etc + @method_list = [] + @attributes = [] + @aliases = [] + @requires = [] + @includes = [] + @extends = [] + @constants = [] + @external_aliases = [] + + # This Hash maps a method name to a list of unmatched aliases (aliases of + # a method not yet encountered). + @unmatched_alias_lists = {} + + @methods_hash = {} + @constants_hash = {} + + @params = nil + + @store ||= nil + end + + ## + # Contexts are sorted by full_name + + def <=>(other) + full_name <=> other.full_name + end + + ## + # Adds an item of type +klass+ with the given +name+ and +comment+ to the + # context. + # + # Currently only RDoc::Extend and RDoc::Include are supported. + + def add klass, name, comment + if RDoc::Extend == klass then + ext = RDoc::Extend.new name, comment + add_extend ext + elsif RDoc::Include == klass then + incl = RDoc::Include.new name, comment + add_include incl + else + raise NotImplementedError, "adding a #{klass} is not implemented" + end + end + + ## + # Adds +an_alias+ that is automatically resolved + + def add_alias an_alias + return an_alias unless @document_self + + method_attr = find_method(an_alias.old_name, an_alias.singleton) || + find_attribute(an_alias.old_name, an_alias.singleton) + + if method_attr then + method_attr.add_alias an_alias, self + else + add_to @external_aliases, an_alias + unmatched_alias_list = + @unmatched_alias_lists[an_alias.pretty_old_name] ||= [] + unmatched_alias_list.push an_alias + end + + an_alias + end + + ## + # Adds +attribute+ if not already there. If it is (as method(s) or attribute), + # updates the comment if it was empty. + # + # The attribute is registered only if it defines a new method. + # For instance, attr_reader :foo will not be registered + # if method +foo+ exists, but attr_accessor :foo will be registered + # if method +foo+ exists, but foo= does not. + + def add_attribute attribute + return attribute unless @document_self + + # mainly to check for redefinition of an attribute as a method + # TODO find a policy for 'attr_reader :foo' + 'def foo=()' + register = false + + key = nil + + if attribute.rw.index 'R' then + key = attribute.pretty_name + known = @methods_hash[key] + + if known then + known.comment = attribute.comment if known.comment.empty? + elsif registered = @methods_hash[attribute.pretty_name << '='] and + RDoc::Attr === registered then + registered.rw = 'RW' + else + @methods_hash[key] = attribute + register = true + end + end + + if attribute.rw.index 'W' then + key = attribute.pretty_name << '=' + known = @methods_hash[key] + + if known then + known.comment = attribute.comment if known.comment.empty? + elsif registered = @methods_hash[attribute.pretty_name] and + RDoc::Attr === registered then + registered.rw = 'RW' + else + @methods_hash[key] = attribute + register = true + end + end + + if register then + attribute.visibility = @visibility + add_to @attributes, attribute + resolve_aliases attribute + end + + attribute + end + + ## + # Adds a class named +given_name+ with +superclass+. + # + # Both +given_name+ and +superclass+ may contain '::', and are + # interpreted relative to the +self+ context. This allows handling correctly + # examples like these: + # class RDoc::Gauntlet < Gauntlet + # module Mod + # class Object # implies < ::Object + # class SubObject < Object # this is _not_ ::Object + # + # Given class Container::Item RDoc assumes +Container+ is a module + # unless it later sees class Container. +add_class+ automatically + # upgrades +given_name+ to a class in this case. + + def add_class class_type, given_name, superclass = '::Object' + # superclass +nil+ is passed by the C parser in the following cases: + # - registering Object in 1.8 (correct) + # - registering BasicObject in 1.9 (correct) + # - registering RubyVM in 1.9 in iseq.c (incorrect: < Object in vm.c) + # + # If we later find a superclass for a registered class with a nil + # superclass, we must honor it. + + # find the name & enclosing context + if given_name =~ /^:+(\w+)$/ then + full_name = $1 + enclosing = top_level + name = full_name.split(/:+/).last + else + full_name = child_name given_name + + if full_name =~ /^(.+)::(\w+)$/ then + name = $2 + ename = $1 + enclosing = @store.classes_hash[ename] || @store.modules_hash[ename] + # HACK: crashes in actionpack/lib/action_view/helpers/form_helper.rb (metaprogramming) + unless enclosing then + # try the given name at top level (will work for the above example) + enclosing = @store.classes_hash[given_name] || + @store.modules_hash[given_name] + return enclosing if enclosing + # not found: create the parent(s) + names = ename.split('::') + enclosing = self + names.each do |n| + enclosing = enclosing.classes_hash[n] || + enclosing.modules_hash[n] || + enclosing.add_module(RDoc::NormalModule, n) + end + end + else + name = full_name + enclosing = self + end + end + + # fix up superclass + superclass = nil if full_name == 'BasicObject' + superclass = nil if full_name == 'Object' and defined?(::BasicObject) + superclass = '::BasicObject' if + defined?(::BasicObject) and full_name == 'Object' + + # find the superclass full name + if superclass then + if superclass =~ /^:+/ then + superclass = $' #' + else + if superclass =~ /^(\w+):+(.+)$/ then + suffix = $2 + mod = find_module_named($1) + superclass = mod.full_name + '::' + suffix if mod + else + mod = find_module_named(superclass) + superclass = mod.full_name if mod + end + end + + # did we believe it was a module? + mod = @store.modules_hash.delete superclass + + upgrade_to_class mod, RDoc::NormalClass, mod.parent if mod + + # e.g., Object < Object + superclass = nil if superclass == full_name + end + + klass = @store.classes_hash[full_name] + + if klass then + # if TopLevel, it may not be registered in the classes: + enclosing.classes_hash[name] = klass + + # update the superclass if needed + if superclass then + existing = klass.superclass + existing = existing.full_name unless existing.is_a?(String) if existing + if existing.nil? || + (existing == 'Object' && superclass != 'Object') then + klass.superclass = superclass + end + end + else + # this is a new class + mod = @store.modules_hash.delete full_name + + if mod then + klass = upgrade_to_class mod, RDoc::NormalClass, enclosing + + klass.superclass = superclass unless superclass.nil? + else + klass = class_type.new name, superclass + + enclosing.add_class_or_module(klass, enclosing.classes_hash, + @store.classes_hash) + end + end + + klass.parent = self + + klass + end + + ## + # Adds the class or module +mod+ to the modules or + # classes Hash +self_hash+, and to +all_hash+ (either + # TopLevel::modules_hash or TopLevel::classes_hash), + # unless #done_documenting is +true+. Sets the #parent of +mod+ + # to +self+, and its #section to #current_section. Returns +mod+. + + def add_class_or_module mod, self_hash, all_hash + mod.section = current_section # TODO declaring context? something is + # wrong here... + mod.parent = self + mod.store = @store + + unless @done_documenting then + self_hash[mod.name] = mod + # this must be done AFTER adding mod to its parent, so that the full + # name is correct: + all_hash[mod.full_name] = mod + end + + mod + end + + ## + # Adds +constant+ if not already there. If it is, updates the comment, + # value and/or is_alias_for of the known constant if they were empty/nil. + + def add_constant constant + return constant unless @document_self + + # HACK: avoid duplicate 'PI' & 'E' in math.c (1.8.7 source code) + # (this is a #ifdef: should be handled by the C parser) + known = @constants_hash[constant.name] + + if known then + known.comment = constant.comment if known.comment.empty? + + known.value = constant.value if + known.value.nil? or known.value.strip.empty? + + known.is_alias_for ||= constant.is_alias_for + else + @constants_hash[constant.name] = constant + add_to @constants, constant + end + + constant + end + + ## + # Adds included module +include+ which should be an RDoc::Include + + def add_include include + add_to @includes, include + + include + end + + ## + # Adds extension module +ext+ which should be an RDoc::Extend + + def add_extend ext + add_to @extends, ext + + ext + end + + ## + # Adds +method+ if not already there. If it is (as method or attribute), + # updates the comment if it was empty. + + def add_method method + return method unless @document_self + + # HACK: avoid duplicate 'new' in io.c & struct.c (1.8.7 source code) + key = method.pretty_name + known = @methods_hash[key] + + if known then + if @store then # otherwise we are loading + known.comment = method.comment if known.comment.empty? + previously = ", previously in #{known.file}" unless + method.file == known.file + @store.rdoc.options.warn \ + "Duplicate method #{known.full_name} in #{method.file}#{previously}" + end + else + @methods_hash[key] = method + method.visibility = @visibility + add_to @method_list, method + resolve_aliases method + end + + method + end + + ## + # Adds a module named +name+. If RDoc already knows +name+ is a class then + # that class is returned instead. See also #add_class. + + def add_module(class_type, name) + mod = @classes[name] || @modules[name] + return mod if mod + + full_name = child_name name + mod = @store.modules_hash[full_name] || class_type.new(name) + + add_class_or_module mod, @modules, @store.modules_hash + end + + ## + # Adds an alias from +from+ (a class or module) to +name+ which was defined + # in +file+. + + def add_module_alias from, name, file + return from if @done_documenting + + to_name = child_name name + + # if we already know this name, don't register an alias: + # see the metaprogramming in lib/active_support/basic_object.rb, + # where we already know BasicObject is a class when we find + # BasicObject = BlankSlate + return from if @store.find_class_or_module to_name + + to = from.dup + to.name = name + to.full_name = nil + + if to.module? then + @store.modules_hash[to_name] = to + @modules[name] = to + else + @store.classes_hash[to_name] = to + @classes[name] = to + end + + # Registers a constant for this alias. The constant value and comment + # will be updated later, when the Ruby parser adds the constant + const = RDoc::Constant.new name, nil, to.comment + const.record_location file + const.is_alias_for = from + add_constant const + + to + end + + ## + # Adds +require+ to this context's top level + + def add_require(require) + return require unless @document_self + + if RDoc::TopLevel === self then + add_to @requires, require + else + parent.add_require require + end + end + + ## + # Returns a section with +title+, creating it if it doesn't already exist. + # +comment+ will be appended to the section's comment. + # + # A section with a +title+ of +nil+ will return the default section. + # + # See also RDoc::Context::Section + + def add_section title, comment = nil + if section = @sections[title] then + section.add_comment comment if comment + else + section = Section.new self, title, comment + @sections[title] = section + end + + section + end + + ## + # Adds +thing+ to the collection +array+ + + def add_to array, thing + array << thing if @document_self + + thing.parent = self + thing.store = @store if @store + thing.section = current_section + end + + ## + # Is there any content? + # + # This means any of: comment, aliases, methods, attributes, external + # aliases, require, constant. + # + # Includes and extends are also checked unless includes == false. + + def any_content(includes = true) + @any_content ||= !( + @comment.empty? && + @method_list.empty? && + @attributes.empty? && + @aliases.empty? && + @external_aliases.empty? && + @requires.empty? && + @constants.empty? + ) + @any_content || (includes && !(@includes + @extends).empty? ) + end + + ## + # Creates the full name for a child with +name+ + + def child_name name + if name =~ /^:+/ + $' #' + elsif RDoc::TopLevel === self then + name + else + "#{self.full_name}::#{name}" + end + end + + ## + # Class attributes + + def class_attributes + @class_attributes ||= attributes.select { |a| a.singleton } + end + + ## + # Class methods + + def class_method_list + @class_method_list ||= method_list.select { |a| a.singleton } + end + + ## + # Array of classes in this context + + def classes + @classes.values + end + + ## + # All classes and modules in this namespace + + def classes_and_modules + classes + modules + end + + ## + # Hash of classes keyed by class name + + def classes_hash + @classes + end + + ## + # The current documentation section that new items will be added to. If + # temporary_section is available it will be used. + + def current_section + if section = @temporary_section then + @temporary_section = nil + else + section = @current_section + end + + section + end + + ## + # Is part of this thing was defined in +file+? + + def defined_in?(file) + @in_files.include?(file) + end + + def display(method_attr) # :nodoc: + if method_attr.is_a? RDoc::Attr + "#{method_attr.definition} #{method_attr.pretty_name}" + else + "method #{method_attr.pretty_name}" + end + end + + ## + # Iterator for ancestors for duck-typing. Does nothing. See + # RDoc::ClassModule#each_ancestor. + # + # This method exists to make it easy to work with Context subclasses that + # aren't part of RDoc. + + def each_ancestor # :nodoc: + end + + ## + # Iterator for attributes + + def each_attribute # :yields: attribute + @attributes.each { |a| yield a } + end + + ## + # Iterator for classes and modules + + def each_classmodule(&block) # :yields: module + classes_and_modules.sort.each(&block) + end + + ## + # Iterator for constants + + def each_constant # :yields: constant + @constants.each {|c| yield c} + end + + ## + # Iterator for included modules + + def each_include # :yields: include + @includes.each do |i| yield i end + end + + ## + # Iterator for extension modules + + def each_extend # :yields: extend + @extends.each do |e| yield e end + end + + ## + # Iterator for methods + + def each_method # :yields: method + return enum_for __method__ unless block_given? + + @method_list.sort.each { |m| yield m } + end + + ## + # Iterator for each section's contents sorted by title. The +section+, the + # section's +constants+ and the sections +attributes+ are yielded. The + # +constants+ and +attributes+ collections are sorted. + # + # To retrieve methods in a section use #methods_by_type with the optional + # +section+ parameter. + # + # NOTE: Do not edit collections yielded by this method + + def each_section # :yields: section, constants, attributes + return enum_for __method__ unless block_given? + + constants = @constants.group_by do |constant| constant.section end + attributes = @attributes.group_by do |attribute| attribute.section end + + constants.default = [] + attributes.default = [] + + sort_sections.each do |section| + yield section, constants[section].sort, attributes[section].sort + end + end + + ## + # Finds an attribute +name+ with singleton value +singleton+. + + def find_attribute(name, singleton) + name = $1 if name =~ /^(.*)=$/ + @attributes.find { |a| a.name == name && a.singleton == singleton } + end + + ## + # Finds an attribute with +name+ in this context + + def find_attribute_named(name) + case name + when /\A#/ then + find_attribute name[1..-1], false + when /\A::/ then + find_attribute name[2..-1], true + else + @attributes.find { |a| a.name == name } + end + end + + ## + # Finds a class method with +name+ in this context + + def find_class_method_named(name) + @method_list.find { |meth| meth.singleton && meth.name == name } + end + + ## + # Finds a constant with +name+ in this context + + def find_constant_named(name) + @constants.find {|m| m.name == name} + end + + ## + # Find a module at a higher scope + + def find_enclosing_module_named(name) + parent && parent.find_module_named(name) + end + + ## + # Finds an external alias +name+ with singleton value +singleton+. + + def find_external_alias(name, singleton) + @external_aliases.find { |m| m.name == name && m.singleton == singleton } + end + + ## + # Finds an external alias with +name+ in this context + + def find_external_alias_named(name) + case name + when /\A#/ then + find_external_alias name[1..-1], false + when /\A::/ then + find_external_alias name[2..-1], true + else + @external_aliases.find { |a| a.name == name } + end + end + + ## + # Finds a file with +name+ in this context + + def find_file_named name + @store.find_file_named name + end + + ## + # Finds an instance method with +name+ in this context + + def find_instance_method_named(name) + @method_list.find { |meth| !meth.singleton && meth.name == name } + end + + ## + # Finds a method, constant, attribute, external alias, module or file + # named +symbol+ in this context. + + def find_local_symbol(symbol) + find_method_named(symbol) or + find_constant_named(symbol) or + find_attribute_named(symbol) or + find_external_alias_named(symbol) or + find_module_named(symbol) or + find_file_named(symbol) + end + + ## + # Finds a method named +name+ with singleton value +singleton+. + + def find_method(name, singleton) + @method_list.find { |m| m.name == name && m.singleton == singleton } + end + + ## + # Finds a instance or module method with +name+ in this context + + def find_method_named(name) + case name + when /\A#/ then + find_method name[1..-1], false + when /\A::/ then + find_method name[2..-1], true + else + @method_list.find { |meth| meth.name == name } + end + end + + ## + # Find a module with +name+ using ruby's scoping rules + + def find_module_named(name) + res = @modules[name] || @classes[name] + return res if res + return self if self.name == name + find_enclosing_module_named name + end + + ## + # Look up +symbol+, first as a module, then as a local symbol. + + def find_symbol(symbol) + find_symbol_module(symbol) || find_local_symbol(symbol) + end + + ## + # Look up a module named +symbol+. + + def find_symbol_module(symbol) + result = nil + + # look for a class or module 'symbol' + case symbol + when /^::/ then + result = @store.find_class_or_module symbol + when /^(\w+):+(.+)$/ + suffix = $2 + top = $1 + searched = self + while searched do + mod = searched.find_module_named(top) + break unless mod + result = @store.find_class_or_module "#{mod.full_name}::#{suffix}" + break if result || searched.is_a?(RDoc::TopLevel) + searched = searched.parent + end + else + searched = self + while searched do + result = searched.find_module_named(symbol) + break if result || searched.is_a?(RDoc::TopLevel) + searched = searched.parent + end + end + + result + end + + ## + # The full name for this context. This method is overridden by subclasses. + + def full_name + '(unknown)' + end + + ## + # Does this context and its methods and constants all have documentation? + # + # (Yes, fully documented doesn't mean everything.) + + def fully_documented? + documented? and + attributes.all? { |a| a.documented? } and + method_list.all? { |m| m.documented? } and + constants.all? { |c| c.documented? } + end + + ## + # URL for this with a +prefix+ + + def http_url(prefix) + path = name_for_path + path = path.gsub(/<<\s*(\w*)/, 'from-\1') if path =~ /<'class' or + # 'instance') and visibility (+:public+, +:protected+, +:private+). + # + # If +section+ is provided only methods in that RDoc::Context::Section will + # be returned. + + def methods_by_type section = nil + methods = {} + + TYPES.each do |type| + visibilities = {} + RDoc::VISIBILITIES.each do |vis| + visibilities[vis] = [] + end + + methods[type] = visibilities + end + + each_method do |method| + next if section and not method.section == section + methods[method.type][method.visibility] << method + end + + methods + end + + ## + # Yields AnyMethod and Attr entries matching the list of names in +methods+. + + def methods_matching(methods, singleton = false, &block) + (@method_list + @attributes).each do |m| + yield m if methods.include?(m.name) and m.singleton == singleton + end + + each_ancestor do |parent| + parent.methods_matching(methods, singleton, &block) + end + end + + ## + # Array of modules in this context + + def modules + @modules.values + end + + ## + # Hash of modules keyed by module name + + def modules_hash + @modules + end + + ## + # Name to use to generate the url. + # #full_name by default. + + def name_for_path + full_name + end + + ## + # Changes the visibility for new methods to +visibility+ + + def ongoing_visibility=(visibility) + @visibility = visibility + end + + ## + # Record +top_level+ as a file +self+ is in. + + def record_location(top_level) + @in_files << top_level unless @in_files.include?(top_level) + end + + ## + # Should we remove this context from the documentation? + # + # The answer is yes if: + # * #received_nodoc is +true+ + # * #any_content is +false+ (not counting includes) + # * All #includes are modules (not a string), and their module has + # #remove_from_documentation? == true + # * All classes and modules have #remove_from_documentation? == true + + def remove_from_documentation? + @remove_from_documentation ||= + @received_nodoc && + !any_content(false) && + @includes.all? { |i| !i.module.is_a?(String) && i.module.remove_from_documentation? } && + classes_and_modules.all? { |cm| cm.remove_from_documentation? } + end + + ## + # Removes methods and attributes with a visibility less than +min_visibility+. + #-- + # TODO mark the visibility of attributes in the template (if not public?) + + def remove_invisible min_visibility + return if [:private, :nodoc].include? min_visibility + remove_invisible_in @method_list, min_visibility + remove_invisible_in @attributes, min_visibility + end + + ## + # Only called when min_visibility == :public or :private + + def remove_invisible_in array, min_visibility # :nodoc: + if min_visibility == :public then + array.reject! { |e| + e.visibility != :public and not e.force_documentation + } + else + array.reject! { |e| + e.visibility == :private and not e.force_documentation + } + end + end + + ## + # Tries to resolve unmatched aliases when a method or attribute has just + # been added. + + def resolve_aliases added + # resolve any pending unmatched aliases + key = added.pretty_name + unmatched_alias_list = @unmatched_alias_lists[key] + return unless unmatched_alias_list + unmatched_alias_list.each do |unmatched_alias| + added.add_alias unmatched_alias, self + @external_aliases.delete unmatched_alias + end + @unmatched_alias_lists.delete key + end + + ## + # Returns RDoc::Context::Section objects referenced in this context for use + # in a table of contents. + + def section_contents + used_sections = {} + + each_method do |method| + next unless method.display? + + used_sections[method.section] = true + end + + # order found sections + sections = sort_sections.select do |section| + used_sections[section] + end + + # only the default section is used + return [] if + sections.length == 1 and not sections.first.title + + sections + end + + ## + # Sections in this context + + def sections + @sections.values + end + + def sections_hash # :nodoc: + @sections + end + + ## + # Sets the current section to a section with +title+. See also #add_section + + def set_current_section title, comment + @current_section = add_section title, comment + end + + ## + # Given an array +methods+ of method names, set the visibility of each to + # +visibility+ + + def set_visibility_for(methods, visibility, singleton = false) + methods_matching methods, singleton do |m| + m.visibility = visibility + end + end + + ## + # Sorts sections alphabetically (default) or in TomDoc fashion (none, + # Public, Internal, Deprecated) + + def sort_sections + titles = @sections.map { |title, _| title } + + if titles.length > 1 and + TOMDOC_TITLES_SORT == + (titles | TOMDOC_TITLES).sort_by { |title| title.to_s } then + @sections.values_at(*TOMDOC_TITLES).compact + else + @sections.sort_by { |title, _| + title.to_s + }.map { |_, section| + section + } + end + end + + def to_s # :nodoc: + "#{self.class.name} #{self.full_name}" + end + + ## + # Return the TopLevel that owns us + #-- + # FIXME we can be 'owned' by several TopLevel (see #record_location & + # #in_files) + + def top_level + return @top_level if defined? @top_level + @top_level = self + @top_level = @top_level.parent until RDoc::TopLevel === @top_level + @top_level + end + + ## + # Upgrades NormalModule +mod+ in +enclosing+ to a +class_type+ + + def upgrade_to_class mod, class_type, enclosing + enclosing.modules_hash.delete mod.name + + klass = RDoc::ClassModule.from_module class_type, mod + klass.store = @store + + # if it was there, then we keep it even if done_documenting + @store.classes_hash[mod.full_name] = klass + enclosing.classes_hash[mod.name] = klass + + klass + end + + autoload :Section, 'rdoc/context/section' + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/context/section.rb b/ruby/lib/ruby/2.1.0/rdoc/context/section.rb new file mode 100644 index 0000000..580f07d --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/context/section.rb @@ -0,0 +1,238 @@ +## +# A section of documentation like: +# +# # :section: The title +# # The body +# +# Sections can be referenced multiple times and will be collapsed into a +# single section. + +class RDoc::Context::Section + + include RDoc::Text + + MARSHAL_VERSION = 0 # :nodoc: + + ## + # Section comment + + attr_reader :comment + + ## + # Section comments + + attr_reader :comments + + ## + # Context this Section lives in + + attr_reader :parent + + ## + # Section title + + attr_reader :title + + @@sequence = "SEC00000" + + ## + # Creates a new section with +title+ and +comment+ + + def initialize parent, title, comment + @parent = parent + @title = title ? title.strip : title + + @@sequence.succ! + @sequence = @@sequence.dup + + @comments = [] + + add_comment comment + end + + ## + # Sections are equal when they have the same #title + + def == other + self.class === other and @title == other.title + end + + ## + # Adds +comment+ to this section + + def add_comment comment + comment = extract_comment comment + + return if comment.empty? + + case comment + when RDoc::Comment then + @comments << comment + when RDoc::Markup::Document then + @comments.concat comment.parts + when Array then + @comments.concat comment + else + raise TypeError, "unknown comment type: #{comment.inspect}" + end + end + + ## + # Anchor reference for linking to this section + + def aref + title = @title || '[untitled]' + + CGI.escape(title).gsub('%', '-').sub(/^-/, '') + end + + ## + # Extracts the comment for this section from the original comment block. + # If the first line contains :section:, strip it and use the rest. + # Otherwise remove lines up to the line containing :section:, and look + # for those lines again at the end and remove them. This lets us write + # + # # :section: The title + # # The body + + def extract_comment comment + case comment + when Array then + comment.map do |c| + extract_comment c + end + when nil + RDoc::Comment.new '' + when RDoc::Comment then + if comment.text =~ /^#[ \t]*:section:.*\n/ then + start = $` + rest = $' + + comment.text = if start.empty? then + rest + else + rest.sub(/#{start.chomp}\Z/, '') + end + end + + comment + when RDoc::Markup::Document then + comment + else + raise TypeError, "unknown comment #{comment.inspect}" + end + end + + def inspect # :nodoc: + "#<%s:0x%x %p>" % [self.class, object_id, title] + end + + ## + # The files comments in this section come from + + def in_files + return [] if @comments.empty? + + case @comments + when Array then + @comments.map do |comment| + comment.file + end + when RDoc::Markup::Document then + @comment.parts.map do |document| + document.file + end + else + raise RDoc::Error, "BUG: unknown comment class #{@comments.class}" + end + end + + ## + # Serializes this Section. The title and parsed comment are saved, but not + # the section parent which must be restored manually. + + def marshal_dump + [ + MARSHAL_VERSION, + @title, + parse, + ] + end + + ## + # De-serializes this Section. The section parent must be restored manually. + + def marshal_load array + @parent = nil + + @title = array[1] + @comments = array[2] + end + + ## + # Parses +comment_location+ into an RDoc::Markup::Document composed of + # multiple RDoc::Markup::Documents with their file set. + + def parse + case @comments + when String then + super + when Array then + docs = @comments.map do |comment, location| + doc = super comment + doc.file = location if location + doc + end + + RDoc::Markup::Document.new(*docs) + when RDoc::Comment then + doc = super @comments.text, comments.format + doc.file = @comments.location + doc + when RDoc::Markup::Document then + return @comments + else + raise ArgumentError, "unknown comment class #{comments.class}" + end + end + + ## + # The section's title, or 'Top Section' if the title is nil. + # + # This is used by the table of contents template so the name is silly. + + def plain_html + @title || 'Top Section' + end + + ## + # Removes a comment from this section if it is from the same file as + # +comment+ + + def remove_comment comment + return if @comments.empty? + + case @comments + when Array then + @comments.delete_if do |my_comment| + my_comment.file == comment.file + end + when RDoc::Markup::Document then + @comments.parts.delete_if do |document| + document.file == comment.file.name + end + else + raise RDoc::Error, "BUG: unknown comment class #{@comments.class}" + end + end + + ## + # Section sequence number (deprecated) + + def sequence + warn "RDoc::Context::Section#sequence is deprecated, use #aref" + @sequence + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/cross_reference.rb b/ruby/lib/ruby/2.1.0/rdoc/cross_reference.rb new file mode 100644 index 0000000..5b08d52 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/cross_reference.rb @@ -0,0 +1,183 @@ +## +# RDoc::CrossReference is a reusable way to create cross references for names. + +class RDoc::CrossReference + + ## + # Regular expression to match class references + # + # 1. There can be a '\\' in front of text to suppress the cross-reference + # 2. There can be a '::' in front of class names to reference from the + # top-level namespace. + # 3. The method can be followed by parenthesis (not recommended) + + CLASS_REGEXP_STR = '\\\\?((?:\:{2})?[A-Z]\w*(?:\:\:\w+)*)' + + ## + # Regular expression to match method references. + # + # See CLASS_REGEXP_STR + + METHOD_REGEXP_STR = '([a-z]\w*[!?=]?|%|===|\[\]=?|<<|>>)(?:\([\w.+*/=<>-]*\))?' + + ## + # Regular expressions matching text that should potentially have + # cross-reference links generated are passed to add_special. Note that + # these expressions are meant to pick up text for which cross-references + # have been suppressed, since the suppression characters are removed by the + # code that is triggered. + + CROSSREF_REGEXP = /(?:^|\s) + ( + (?: + # A::B::C.meth + #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR} + + # Stand-alone method (preceded by a #) + | \\?\##{METHOD_REGEXP_STR} + + # Stand-alone method (preceded by ::) + | ::#{METHOD_REGEXP_STR} + + # A::B::C + # The stuff after CLASS_REGEXP_STR is a + # nasty hack. CLASS_REGEXP_STR unfortunately matches + # words like dog and cat (these are legal "class" + # names in Fortran 95). When a word is flagged as a + # potential cross-reference, limitations in the markup + # engine suppress other processing, such as typesetting. + # This is particularly noticeable for contractions. + # In order that words like "can't" not + # be flagged as potential cross-references, only + # flag potential class cross-references if the character + # after the cross-reference is a space, sentence + # punctuation, tag start character, or attribute + # marker. + | #{CLASS_REGEXP_STR}(?=[@\s).?!,;<\000]|\z) + + # Things that look like filenames + # The key thing is that there must be at least + # one special character (period, slash, or + # underscore). + | (?:\.\.\/)*[-\/\w]+[_\/.][-\w\/.]+ + + # Things that have markup suppressed + # Don't process things like '\<' in \, though. + # TODO: including < is a hack, not very satisfying. + | \\[^\s<] + ) + + # labels for headings + (?:@[\w+%-]+(?:\.[\w|%-]+)?)? + )/x + + ## + # Version of CROSSREF_REGEXP used when --hyperlink-all is specified. + + ALL_CROSSREF_REGEXP = / + (?:^|\s) + ( + (?: + # A::B::C.meth + #{CLASS_REGEXP_STR}(?:[.#]|::)#{METHOD_REGEXP_STR} + + # Stand-alone method + | \\?#{METHOD_REGEXP_STR} + + # A::B::C + | #{CLASS_REGEXP_STR}(?=[@\s).?!,;<\000]|\z) + + # Things that look like filenames + | (?:\.\.\/)*[-\/\w]+[_\/.][-\w\/.]+ + + # Things that have markup suppressed + | \\[^\s<] + ) + + # labels for headings + (?:@[\w+%-]+)? + )/x + + ## + # Hash of references that have been looked-up to their replacements + + attr_accessor :seen + + ## + # Allows cross-references to be created based on the given +context+ + # (RDoc::Context). + + def initialize context + @context = context + @store = context.store + + @seen = {} + end + + ## + # Returns a reference to +name+. + # + # If the reference is found and +name+ is not documented +text+ will be + # returned. If +name+ is escaped +name+ is returned. If +name+ is not + # found +text+ is returned. + + def resolve name, text + return @seen[name] if @seen.include? name + + if /#{CLASS_REGEXP_STR}([.#]|::)#{METHOD_REGEXP_STR}/o =~ name then + type = $2 + type = '' if type == '.' # will find either #method or ::method + method = "#{type}#{$3}" + container = @context.find_symbol_module($1) + elsif /^([.#]|::)#{METHOD_REGEXP_STR}/o =~ name then + type = $1 + type = '' if type == '.' + method = "#{type}#{$2}" + container = @context + else + container = nil + end + + if container then + ref = container.find_local_symbol method + + unless ref || RDoc::TopLevel === container then + ref = container.find_ancestor_local_symbol method + end + end + + ref = case name + when /^\\(#{CLASS_REGEXP_STR})$/o then + @context.find_symbol $1 + else + @context.find_symbol name + end unless ref + + # Try a page name + ref = @store.page name if not ref and name =~ /^\w+$/ + + ref = nil if RDoc::Alias === ref # external alias, can't link to it + + out = if name == '\\' then + name + elsif name =~ /^\\/ then + # we remove the \ only in front of what we know: + # other backslashes are treated later, only outside of + ref ? $' : name + elsif ref then + if ref.display? then + ref + else + text + end + else + text + end + + @seen[name] = out + + out + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/encoding.rb b/ruby/lib/ruby/2.1.0/rdoc/encoding.rb new file mode 100644 index 0000000..9fe3539 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/encoding.rb @@ -0,0 +1,97 @@ +# coding: US-ASCII + +## +# This class is a wrapper around File IO and Encoding that helps RDoc load +# files and convert them to the correct encoding. + +module RDoc::Encoding + + ## + # Reads the contents of +filename+ and handles any encoding directives in + # the file. + # + # The content will be converted to the +encoding+. If the file cannot be + # converted a warning will be printed and nil will be returned. + # + # If +force_transcode+ is true the document will be transcoded and any + # unknown character in the target encoding will be replaced with '?' + + def self.read_file filename, encoding, force_transcode = false + content = open filename, "rb" do |f| f.read end + content.gsub!("\r\n", "\n") if RUBY_PLATFORM =~ /mswin|mingw/ + + utf8 = content.sub!(/\A\xef\xbb\xbf/, '') + + RDoc::Encoding.set_encoding content + + if Object.const_defined? :Encoding then + begin + encoding ||= Encoding.default_external + orig_encoding = content.encoding + + if utf8 then + content.force_encoding Encoding::UTF_8 + content.encode! encoding + else + # assume the content is in our output encoding + content.force_encoding encoding + end + + unless content.valid_encoding? then + # revert and try to transcode + content.force_encoding orig_encoding + content.encode! encoding + end + + unless content.valid_encoding? then + warn "unable to convert #{filename} to #{encoding}, skipping" + content = nil + end + rescue Encoding::InvalidByteSequenceError, + Encoding::UndefinedConversionError => e + if force_transcode then + content.force_encoding orig_encoding + content.encode!(encoding, + :invalid => :replace, :undef => :replace, + :replace => '?') + return content + else + warn "unable to convert #{e.message} for #{filename}, skipping" + return nil + end + end + end + + content + rescue ArgumentError => e + raise unless e.message =~ /unknown encoding name - (.*)/ + warn "unknown encoding name \"#{$1}\" for #{filename}, skipping" + nil + rescue Errno::EISDIR, Errno::ENOENT + nil + end + + ## + # Sets the encoding of +string+ based on the magic comment + + def self.set_encoding string + string =~ /\A(?:#!.*\n)?(.*\n)/ + + first_line = $1 + + name = case first_line + when /^<\?xml[^?]*encoding=(["'])(.*?)\1/ then $2 + when /\b(?:en)?coding[=:]\s*([^\s;]+)/i then $1 + else return + end + + string.sub! first_line, '' + + return unless Object.const_defined? :Encoding + + enc = Encoding.find name + string.force_encoding enc if enc + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/erb_partial.rb b/ruby/lib/ruby/2.1.0/rdoc/erb_partial.rb new file mode 100644 index 0000000..910d1e0 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/erb_partial.rb @@ -0,0 +1,18 @@ +## +# Allows an ERB template to be rendered in the context (binding) of an +# existing ERB template evaluation. + +class RDoc::ERBPartial < ERB + + ## + # Overrides +compiler+ startup to set the +eoutvar+ to an empty string only + # if it isn't already set. + + def set_eoutvar compiler, eoutvar = '_erbout' + super + + compiler.pre_cmd = ["#{eoutvar} ||= ''"] + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/erbio.rb b/ruby/lib/ruby/2.1.0/rdoc/erbio.rb new file mode 100644 index 0000000..04a89fb --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/erbio.rb @@ -0,0 +1,37 @@ +require 'erb' + +## +# A subclass of ERB that writes directly to an IO. Credit to Aaron Patterson +# and Masatoshi SEKI. +# +# To use: +# +# erbio = RDoc::ERBIO.new '<%= "hello world" %>', nil, nil +# +# open 'hello.txt', 'w' do |io| +# erbio.result binding +# end +# +# Note that binding must enclose the io you wish to output on. + +class RDoc::ERBIO < ERB + + ## + # Defaults +eoutvar+ to 'io', otherwise is identical to ERB's initialize + + def initialize str, safe_level = nil, trim_mode = nil, eoutvar = 'io' + super + end + + ## + # Instructs +compiler+ how to write to +io_variable+ + + def set_eoutvar compiler, io_variable + compiler.put_cmd = "#{io_variable}.write" + compiler.insert_cmd = "#{io_variable}.write" + compiler.pre_cmd = [] + compiler.post_cmd = [] + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/extend.rb b/ruby/lib/ruby/2.1.0/rdoc/extend.rb new file mode 100644 index 0000000..efa2c69 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/extend.rb @@ -0,0 +1,9 @@ +## +# A Module extension to a class with \#extend +# +# RDoc::Extend.new 'Enumerable', 'comment ...' + +class RDoc::Extend < RDoc::Mixin + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator.rb b/ruby/lib/ruby/2.1.0/rdoc/generator.rb new file mode 100644 index 0000000..9051f8a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator.rb @@ -0,0 +1,50 @@ +## +# RDoc uses generators to turn parsed source code in the form of an +# RDoc::CodeObject tree into some form of output. RDoc comes with the HTML +# generator RDoc::Generator::Darkfish and an ri data generator +# RDoc::Generator::RI. +# +# == Registering a Generator +# +# Generators are registered by calling RDoc::RDoc.add_generator with the class +# of the generator: +# +# class My::Awesome::Generator +# RDoc::RDoc.add_generator self +# end +# +# == Adding Options to +rdoc+ +# +# Before option processing in +rdoc+, RDoc::Options will call ::setup_options +# on the generator class with an RDoc::Options instance. The generator can +# use RDoc::Options#option_parser to add command-line options to the +rdoc+ +# tool. See RDoc::Options@Custom+Options for an example and see OptionParser +# for details on how to add options. +# +# You can extend the RDoc::Options instance with additional accessors for your +# generator. +# +# == Generator Instantiation +# +# After parsing, RDoc::RDoc will instantiate a generator by calling +# #initialize with an RDoc::Store instance and an RDoc::Options instance. +# +# The RDoc::Store instance holds documentation for parsed source code. In +# RDoc 3 and earlier the RDoc::TopLevel class held this data. When upgrading +# a generator from RDoc 3 and earlier you should only need to replace +# RDoc::TopLevel with the store instance. +# +# RDoc will then call #generate on the generator instance. You can use the +# various methods on RDoc::Store and in the RDoc::CodeObject tree to create +# your desired output format. + +module RDoc::Generator + + autoload :Markup, 'rdoc/generator/markup' + + autoload :Darkfish, 'rdoc/generator/darkfish' + autoload :JsonIndex, 'rdoc/generator/json_index' + autoload :RI, 'rdoc/generator/ri' + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/darkfish.rb b/ruby/lib/ruby/2.1.0/rdoc/generator/darkfish.rb new file mode 100644 index 0000000..bd37b60 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/darkfish.rb @@ -0,0 +1,759 @@ +# -*- mode: ruby; ruby-indent-level: 2; tab-width: 2 -*- + +require 'erb' +require 'fileutils' +require 'pathname' +require 'rdoc/generator/markup' + +## +# Darkfish RDoc HTML Generator +# +# $Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $ +# +# == Author/s +# * Michael Granger (ged@FaerieMUD.org) +# +# == Contributors +# * Mahlon E. Smith (mahlon@martini.nu) +# * Eric Hodel (drbrain@segment7.net) +# +# == License +# +# Copyright (c) 2007, 2008, Michael Granger. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the author/s, nor the names of the project's +# contributors may be used to endorse or promote products derived from this +# software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# == Attributions +# +# Darkfish uses the {Silk Icons}[http://www.famfamfam.com/lab/icons/silk/] set +# by Mark James. + +class RDoc::Generator::Darkfish + + RDoc::RDoc.add_generator self + + include ERB::Util + + ## + # Stylesheets, fonts, etc. that are included in RDoc. + + BUILTIN_STYLE_ITEMS = # :nodoc: + %w[ + fonts.css + fonts/Lato-Light.ttf + fonts/Lato-LightItalic.ttf + fonts/Lato-Regular.ttf + fonts/Lato-RegularItalic.ttf + fonts/SourceCodePro-Bold.ttf + fonts/SourceCodePro-Regular.ttf + rdoc.css + ] + + ## + # Path to this file's parent directory. Used to find templates and other + # resources. + + GENERATOR_DIR = File.join 'rdoc', 'generator' + + ## + # Release Version + + VERSION = '3' + + ## + # Description of this generator + + DESCRIPTION = 'HTML generator, written by Michael Granger' + + ## + # The relative path to style sheets and javascript. By default this is set + # the same as the rel_prefix. + + attr_accessor :asset_rel_path + + ## + # The path to generate files into, combined with --op from the + # options for a full path. + + attr_reader :base_dir + + ## + # Classes and modules to be used by this generator, not necessarily + # displayed. See also #modsort + + attr_reader :classes + + ## + # No files will be written when dry_run is true. + + attr_accessor :dry_run + + ## + # When false the generate methods return a String instead of writing to a + # file. The default is true. + + attr_accessor :file_output + + ## + # Files to be displayed by this generator + + attr_reader :files + + ## + # The JSON index generator for this Darkfish generator + + attr_reader :json_index + + ## + # Methods to be displayed by this generator + + attr_reader :methods + + ## + # Sorted list of classes and modules to be displayed by this generator + + attr_reader :modsort + + ## + # The RDoc::Store that is the source of the generated content + + attr_reader :store + + ## + # The directory where the template files live + + attr_reader :template_dir # :nodoc: + + ## + # The output directory + + attr_reader :outputdir + + ## + # Initialize a few instance variables before we start + + def initialize store, options + @store = store + @options = options + + @asset_rel_path = '' + @base_dir = Pathname.pwd.expand_path + @dry_run = @options.dry_run + @file_output = true + @template_dir = Pathname.new options.template_dir + @template_cache = {} + + @classes = nil + @context = nil + @files = nil + @methods = nil + @modsort = nil + + @json_index = RDoc::Generator::JsonIndex.new self, options + end + + ## + # Output progress information if debugging is enabled + + def debug_msg *msg + return unless $DEBUG_RDOC + $stderr.puts(*msg) + end + + ## + # Directory where generated class HTML files live relative to the output + # dir. + + def class_dir + nil + end + + ## + # Directory where generated class HTML files live relative to the output + # dir. + + def file_dir + nil + end + + ## + # Create the directories the generated docs will live in if they don't + # already exist. + + def gen_sub_directories + @outputdir.mkpath + end + + ## + # Copy over the stylesheet into the appropriate place in the output + # directory. + + def write_style_sheet + debug_msg "Copying static files" + options = { :verbose => $DEBUG_RDOC, :noop => @dry_run } + + BUILTIN_STYLE_ITEMS.each do |item| + install_rdoc_static_file @template_dir + item, "./#{item}", options + end + + @options.template_stylesheets.each do |stylesheet| + FileUtils.cp stylesheet, '.', options + end + + Dir[(@template_dir + "{js,images}/**/*").to_s].each do |path| + next if File.directory? path + next if File.basename(path) =~ /^\./ + + dst = Pathname.new(path).relative_path_from @template_dir + + install_rdoc_static_file @template_dir + path, dst, options + end + end + + ## + # Build the initial indices and output objects based on an array of TopLevel + # objects containing the extracted information. + + def generate + setup + + write_style_sheet + generate_index + generate_class_files + generate_file_files + generate_table_of_contents + @json_index.generate + + copy_static + + rescue => e + debug_msg "%s: %s\n %s" % [ + e.class.name, e.message, e.backtrace.join("\n ") + ] + + raise + end + + ## + # Copies static files from the static_path into the output directory + + def copy_static + return if @options.static_path.empty? + + fu_options = { :verbose => $DEBUG_RDOC, :noop => @dry_run } + + @options.static_path.each do |path| + unless File.directory? path then + FileUtils.install path, @outputdir, fu_options.merge(:mode => 0644) + next + end + + Dir.chdir path do + Dir[File.join('**', '*')].each do |entry| + dest_file = @outputdir + entry + + if File.directory? entry then + FileUtils.mkdir_p entry, fu_options + else + FileUtils.install entry, dest_file, fu_options.merge(:mode => 0644) + end + end + end + end + end + + ## + # Return a list of the documented modules sorted by salience first, then + # by name. + + def get_sorted_module_list classes + classes.select do |klass| + klass.display? + end.sort + end + + ## + # Generate an index page which lists all the classes which are documented. + + def generate_index + setup + + template_file = @template_dir + 'index.rhtml' + return unless template_file.exist? + + debug_msg "Rendering the index page..." + + out_file = @base_dir + @options.op_dir + 'index.html' + rel_prefix = @outputdir.relative_path_from out_file.dirname + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output + + # suppress 1.9.3 warning + asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path + + @title = @options.title + + render_template template_file, out_file do |io| binding end + rescue => e + error = RDoc::Error.new \ + "error generating index.html: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace + + raise error + end + + ## + # Generates a class file for +klass+ + + def generate_class klass, template_file = nil + setup + + current = klass + + template_file ||= @template_dir + 'class.rhtml' + + debug_msg " working on %s (%s)" % [klass.full_name, klass.path] + out_file = @outputdir + klass.path + rel_prefix = @outputdir.relative_path_from out_file.dirname + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output + + # suppress 1.9.3 warning + asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path + svninfo = svninfo = get_svninfo(current) + + @title = "#{klass.type} #{klass.full_name} - #{@options.title}" + + debug_msg " rendering #{out_file}" + render_template template_file, out_file do |io| binding end + end + + ## + # Generate a documentation file for each class and module + + def generate_class_files + setup + + template_file = @template_dir + 'class.rhtml' + template_file = @template_dir + 'classpage.rhtml' unless + template_file.exist? + return unless template_file.exist? + debug_msg "Generating class documentation in #{@outputdir}" + + current = nil + + @classes.each do |klass| + current = klass + + generate_class klass, template_file + end + rescue => e + error = RDoc::Error.new \ + "error generating #{current.path}: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace + + raise error + end + + ## + # Generate a documentation file for each file + + def generate_file_files + setup + + page_file = @template_dir + 'page.rhtml' + fileinfo_file = @template_dir + 'fileinfo.rhtml' + + # for legacy templates + filepage_file = @template_dir + 'filepage.rhtml' unless + page_file.exist? or fileinfo_file.exist? + + return unless + page_file.exist? or fileinfo_file.exist? or filepage_file.exist? + + debug_msg "Generating file documentation in #{@outputdir}" + + out_file = nil + current = nil + + @files.each do |file| + current = file + + if file.text? and page_file.exist? then + generate_page file + next + end + + template_file = nil + out_file = @outputdir + file.path + debug_msg " working on %s (%s)" % [file.full_name, out_file] + rel_prefix = @outputdir.relative_path_from out_file.dirname + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output + + # suppress 1.9.3 warning + asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path + + unless filepage_file then + if file.text? then + next unless page_file.exist? + template_file = page_file + @title = file.page_name + else + next unless fileinfo_file.exist? + template_file = fileinfo_file + @title = "File: #{file.base_name}" + end + end + + @title += " - #{@options.title}" + template_file ||= filepage_file + + render_template template_file, out_file do |io| binding end + end + rescue => e + error = + RDoc::Error.new "error generating #{out_file}: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace + + raise error + end + + ## + # Generate a page file for +file+ + + def generate_page file + setup + + template_file = @template_dir + 'page.rhtml' + + out_file = @outputdir + file.path + debug_msg " working on %s (%s)" % [file.full_name, out_file] + rel_prefix = @outputdir.relative_path_from out_file.dirname + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output + + # suppress 1.9.3 warning + current = current = file + asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path + + @title = "#{file.page_name} - #{@options.title}" + + debug_msg " rendering #{out_file}" + render_template template_file, out_file do |io| binding end + end + + ## + # Generates the 404 page for the RDoc servlet + + def generate_servlet_not_found message + setup + + template_file = @template_dir + 'servlet_not_found.rhtml' + return unless template_file.exist? + + debug_msg "Rendering the servlet 404 Not Found page..." + + rel_prefix = rel_prefix = '' + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output + + # suppress 1.9.3 warning + asset_rel_prefix = asset_rel_prefix = '' + + @title = 'Not Found' + + render_template template_file do |io| binding end + rescue => e + error = RDoc::Error.new \ + "error generating servlet_not_found: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace + + raise error + end + + ## + # Generates the servlet root page for the RDoc servlet + + def generate_servlet_root installed + setup + + template_file = @template_dir + 'servlet_root.rhtml' + return unless template_file.exist? + + debug_msg 'Rendering the servlet root page...' + + rel_prefix = '.' + asset_rel_prefix = rel_prefix + search_index_rel_prefix = asset_rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output + + @title = 'Local RDoc Documentation' + + render_template template_file do |io| binding end + rescue => e + error = RDoc::Error.new \ + "error generating servlet_root: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace + + raise error + end + + ## + # Generate an index page which lists all the classes which are documented. + + def generate_table_of_contents + setup + + template_file = @template_dir + 'table_of_contents.rhtml' + return unless template_file.exist? + + debug_msg "Rendering the Table of Contents..." + + out_file = @outputdir + 'table_of_contents.html' + rel_prefix = @outputdir.relative_path_from out_file.dirname + search_index_rel_prefix = rel_prefix + search_index_rel_prefix += @asset_rel_path if @file_output + + # suppress 1.9.3 warning + asset_rel_prefix = asset_rel_prefix = rel_prefix + @asset_rel_path + + @title = "Table of Contents - #{@options.title}" + + render_template template_file, out_file do |io| binding end + rescue => e + error = RDoc::Error.new \ + "error generating table_of_contents.html: #{e.message} (#{e.class})" + error.set_backtrace e.backtrace + + raise error + end + + def install_rdoc_static_file source, destination, options # :nodoc: + return unless source.exist? + + begin + FileUtils.mkdir_p File.dirname(destination), options + + begin + FileUtils.ln source, destination, options + rescue Errno::EEXIST + FileUtils.rm destination + retry + end + rescue + FileUtils.cp source, destination, options + end + end + + ## + # Prepares for generation of output from the current directory + + def setup + return if instance_variable_defined? :@outputdir + + @outputdir = Pathname.new(@options.op_dir).expand_path @base_dir + + return unless @store + + @classes = @store.all_classes_and_modules.sort + @files = @store.all_files.sort + @methods = @classes.map { |m| m.method_list }.flatten.sort + @modsort = get_sorted_module_list @classes + end + + ## + # Return a string describing the amount of time in the given number of + # seconds in terms a human can understand easily. + + def time_delta_string seconds + return 'less than a minute' if seconds < 60 + return "#{seconds / 60} minute#{seconds / 60 == 1 ? '' : 's'}" if + seconds < 3000 # 50 minutes + return 'about one hour' if seconds < 5400 # 90 minutes + return "#{seconds / 3600} hours" if seconds < 64800 # 18 hours + return 'one day' if seconds < 86400 # 1 day + return 'about one day' if seconds < 172800 # 2 days + return "#{seconds / 86400} days" if seconds < 604800 # 1 week + return 'about one week' if seconds < 1209600 # 2 week + return "#{seconds / 604800} weeks" if seconds < 7257600 # 3 months + return "#{seconds / 2419200} months" if seconds < 31536000 # 1 year + return "#{seconds / 31536000} years" + end + + # %q$Id: darkfish.rb 52 2009-01-07 02:08:11Z deveiant $" + SVNID_PATTERN = / + \$Id:\s + (\S+)\s # filename + (\d+)\s # rev + (\d{4}-\d{2}-\d{2})\s # Date (YYYY-MM-DD) + (\d{2}:\d{2}:\d{2}Z)\s # Time (HH:MM:SSZ) + (\w+)\s # committer + \$$ + /x + + ## + # Try to extract Subversion information out of the first constant whose + # value looks like a subversion Id tag. If no matching constant is found, + # and empty hash is returned. + + def get_svninfo klass + constants = klass.constants or return {} + + constants.find { |c| c.value =~ SVNID_PATTERN } or return {} + + filename, rev, date, time, committer = $~.captures + commitdate = Time.parse "#{date} #{time}" + + return { + :filename => filename, + :rev => Integer(rev), + :commitdate => commitdate, + :commitdelta => time_delta_string(Time.now - commitdate), + :committer => committer, + } + end + + ## + # Creates a template from its components and the +body_file+. + # + # For backwards compatibility, if +body_file+ contains " + + + +#{head_file.read} + +#{body} + +#{footer_file.read} + TEMPLATE + end + + ## + # Renders the ERb contained in +file_name+ relative to the template + # directory and returns the result based on the current context. + + def render file_name + template_file = @template_dir + file_name + + template = template_for template_file, false, RDoc::ERBPartial + + template.filename = template_file.to_s + + template.result @context + end + + ## + # Load and render the erb template in the given +template_file+ and write + # it out to +out_file+. + # + # Both +template_file+ and +out_file+ should be Pathname-like objects. + # + # An io will be yielded which must be captured by binding in the caller. + + def render_template template_file, out_file = nil # :yield: io + io_output = out_file && !@dry_run && @file_output + erb_klass = io_output ? RDoc::ERBIO : ERB + + template = template_for template_file, true, erb_klass + + if io_output then + debug_msg "Outputting to %s" % [out_file.expand_path] + + out_file.dirname.mkpath + out_file.open 'w', 0644 do |io| + io.set_encoding @options.encoding if Object.const_defined? :Encoding + + @context = yield io + + template_result template, @context, template_file + end + else + @context = yield nil + + output = template_result template, @context, template_file + + debug_msg " would have written %d characters to %s" % [ + output.length, out_file.expand_path + ] if @dry_run + + output + end + end + + ## + # Creates the result for +template+ with +context+. If an error is raised a + # Pathname +template_file+ will indicate the file where the error occurred. + + def template_result template, context, template_file + template.filename = template_file.to_s + template.result context + rescue NoMethodError => e + raise RDoc::Error, "Error while evaluating %s: %s" % [ + template_file.expand_path, + e.message, + ], e.backtrace + end + + ## + # Retrieves a cache template for +file+, if present, or fills the cache. + + def template_for file, page = true, klass = ERB + template = @template_cache[file] + + return template if template + + if page then + template = assemble_template file + erbout = 'io' + else + template = file.read + template = template.encode @options.encoding if + Object.const_defined? :Encoding + + file_var = File.basename(file).sub(/\..*/, '') + + erbout = "_erbout_#{file_var}" + end + + template = klass.new template, nil, '<>', erbout + @template_cache[file] = template + template + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/json_index.rb b/ruby/lib/ruby/2.1.0/rdoc/generator/json_index.rb new file mode 100644 index 0000000..c303b2e --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/json_index.rb @@ -0,0 +1,248 @@ +require 'json' + +## +# The JsonIndex generator is designed to complement an HTML generator and +# produces a JSON search index. This generator is derived from sdoc by +# Vladimir Kolesnikov and contains verbatim code written by him. +# +# This generator is designed to be used with a regular HTML generator: +# +# class RDoc::Generator::Darkfish +# def initialize options +# # ... +# @base_dir = Pathname.pwd.expand_path +# +# @json_index = RDoc::Generator::JsonIndex.new self, options +# end +# +# def generate +# # ... +# @json_index.generate +# end +# end +# +# == Index Format +# +# The index is output as a JSON file assigned to the global variable +# +search_data+. The structure is: +# +# var search_data = { +# "index": { +# "searchIndex": +# ["a", "b", ...], +# "longSearchIndex": +# ["a", "a::b", ...], +# "info": [ +# ["A", "A", "A.html", "", ""], +# ["B", "A::B", "A::B.html", "", ""], +# ... +# ] +# } +# } +# +# The same item is described across the +searchIndex+, +longSearchIndex+ and +# +info+ fields. The +searchIndex+ field contains the item's short name, the +# +longSearchIndex+ field contains the full_name (when appropriate) and the +# +info+ field contains the item's name, full_name, path, parameters and a +# snippet of the item's comment. +# +# == LICENSE +# +# Copyright (c) 2009 Vladimir Kolesnikov +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +class RDoc::Generator::JsonIndex + + include RDoc::Text + + ## + # Where the search index lives in the generated output + + SEARCH_INDEX_FILE = File.join 'js', 'search_index.js' + + attr_reader :index # :nodoc: + + ## + # Creates a new generator. +parent_generator+ is used to determine the + # class_dir and file_dir of links in the output index. + # + # +options+ are the same options passed to the parent generator. + + def initialize parent_generator, options + @parent_generator = parent_generator + @store = parent_generator.store + @options = options + + @template_dir = File.expand_path '../template/json_index', __FILE__ + @base_dir = @parent_generator.base_dir + + @classes = nil + @files = nil + @index = nil + end + + ## + # Builds the JSON index as a Hash. + + def build_index + reset @store.all_files.sort, @store.all_classes_and_modules.sort + + index_classes + index_methods + index_pages + + { :index => @index } + end + + ## + # Output progress information if debugging is enabled + + def debug_msg *msg + return unless $DEBUG_RDOC + $stderr.puts(*msg) + end + + ## + # Writes the JSON index to disk + + def generate + debug_msg "Generating JSON index" + + debug_msg " writing search index to %s" % SEARCH_INDEX_FILE + data = build_index + + return if @options.dry_run + + out_dir = @base_dir + @options.op_dir + index_file = out_dir + SEARCH_INDEX_FILE + + FileUtils.mkdir_p index_file.dirname, :verbose => $DEBUG_RDOC + + index_file.open 'w', 0644 do |io| + io.set_encoding Encoding::UTF_8 if Object.const_defined? :Encoding + io.write 'var search_data = ' + + JSON.dump data, io, 0 + end + + Dir.chdir @template_dir do + Dir['**/*.js'].each do |source| + dest = File.join out_dir, source + + FileUtils.install source, dest, :mode => 0644, :verbose => $DEBUG_RDOC + end + end + end + + ## + # Adds classes and modules to the index + + def index_classes + debug_msg " generating class search index" + + documented = @classes.uniq.select do |klass| + klass.document_self_or_methods + end + + documented.each do |klass| + debug_msg " #{klass.full_name}" + record = klass.search_record + @index[:searchIndex] << search_string(record.shift) + @index[:longSearchIndex] << search_string(record.shift) + @index[:info] << record + end + end + + ## + # Adds methods to the index + + def index_methods + debug_msg " generating method search index" + + list = @classes.uniq.map do |klass| + klass.method_list + end.flatten.sort_by do |method| + [method.name, method.parent.full_name] + end + + list.each do |method| + debug_msg " #{method.full_name}" + record = method.search_record + @index[:searchIndex] << "#{search_string record.shift}()" + @index[:longSearchIndex] << "#{search_string record.shift}()" + @index[:info] << record + end + end + + ## + # Adds pages to the index + + def index_pages + debug_msg " generating pages search index" + + pages = @files.select do |file| + file.text? + end + + pages.each do |page| + debug_msg " #{page.page_name}" + record = page.search_record + @index[:searchIndex] << search_string(record.shift) + @index[:longSearchIndex] << '' + record.shift + @index[:info] << record + end + end + + ## + # The directory classes are written to + + def class_dir + @parent_generator.class_dir + end + + ## + # The directory files are written to + + def file_dir + @parent_generator.file_dir + end + + def reset files, classes # :nodoc: + @files = files + @classes = classes + + @index = { + :searchIndex => [], + :longSearchIndex => [], + :info => [] + } + end + + ## + # Removes whitespace and downcases +string+ + + def search_string string + string.downcase.gsub(/\s/, '') + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/markup.rb b/ruby/lib/ruby/2.1.0/rdoc/generator/markup.rb new file mode 100644 index 0000000..788e5a4 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/markup.rb @@ -0,0 +1,169 @@ +## +# Handle common RDoc::Markup tasks for various CodeObjects +# +# This module is loaded by generators. It allows RDoc's CodeObject tree to +# avoid loading generator code to improve startup time for +ri+. + +module RDoc::Generator::Markup + + ## + # Generates a relative URL from this object's path to +target_path+ + + def aref_to(target_path) + RDoc::Markup::ToHtml.gen_relative_url path, target_path + end + + ## + # Generates a relative URL from +from_path+ to this object's path + + def as_href(from_path) + RDoc::Markup::ToHtml.gen_relative_url from_path, path + end + + ## + # Handy wrapper for marking up this object's comment + + def description + markup @comment + end + + ## + # Creates an RDoc::Markup::ToHtmlCrossref formatter + + def formatter + return @formatter if defined? @formatter + + options = @store.rdoc.options + this = RDoc::Context === self ? self : @parent + + @formatter = RDoc::Markup::ToHtmlCrossref.new options, this.path, this + @formatter.code_object = self + @formatter + end + + ## + # Build a webcvs URL starting for the given +url+ with +full_path+ appended + # as the destination path. If +url+ contains '%s' +full_path+ will be + # will replace the %s using sprintf on the +url+. + + def cvs_url(url, full_path) + if /%s/ =~ url then + sprintf url, full_path + else + url + full_path + end + end + +end + +class RDoc::CodeObject + + include RDoc::Generator::Markup + +end + +class RDoc::MethodAttr + + @add_line_numbers = false + + class << self + ## + # Allows controlling whether #markup_code adds line numbers to + # the source code. + + attr_accessor :add_line_numbers + end + + ## + # Prepend +src+ with line numbers. Relies on the first line of a source + # code listing having: + # + # # File xxxxx, line dddd + # + # If it has this comment then line numbers are added to +src+ and the , + # line dddd portion of the comment is removed. + + def add_line_numbers(src) + return unless src.sub!(/\A(.*)(, line (\d+))/, '\1') + first = $3.to_i - 1 + last = first + src.count("\n") + size = last.to_s.length + + line = first + src.gsub!(/^/) do + res = if line == first then + " " * (size + 1) + else + "%2$*1$d " % [size, line] + end + + line += 1 + res + end + end + + ## + # Turns the method's token stream into HTML. + # + # Prepends line numbers if +add_line_numbers+ is true. + + def markup_code + return '' unless @token_stream + + src = RDoc::TokenStream.to_html @token_stream + + # dedent the source + indent = src.length + lines = src.lines.to_a + lines.shift if src =~ /\A.*#\ *File/i # remove '# File' comment + lines.each do |line| + if line =~ /^ *(?=\S)/ + n = $&.length + indent = n if n < indent + break if n == 0 + end + end + src.gsub!(/^#{' ' * indent}/, '') if indent > 0 + + add_line_numbers(src) if RDoc::MethodAttr.add_line_numbers + + src + end + +end + +class RDoc::ClassModule + + ## + # Handy wrapper for marking up this class or module's comment + + def description + markup @comment_location + end + +end + +class RDoc::Context::Section + + include RDoc::Generator::Markup + +end + +class RDoc::TopLevel + + ## + # Returns a URL for this source file on some web repository. Use the -W + # command line option to set. + + def cvs_url + url = @store.rdoc.options.webcvs + + if /%s/ =~ url then + url % @relative_name + else + url + @relative_name + end + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/ri.rb b/ruby/lib/ruby/2.1.0/rdoc/generator/ri.rb new file mode 100644 index 0000000..b9c4141 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/ri.rb @@ -0,0 +1,30 @@ +## +# Generates ri data files + +class RDoc::Generator::RI + + RDoc::RDoc.add_generator self + + ## + # Description of this generator + + DESCRIPTION = 'creates ri data files' + + ## + # Set up a new ri generator + + def initialize store, options #:not-new: + @options = options + @store = store + @store.path = '.' + end + + ## + # Writes the parsed data store to disk for use by ri. + + def generate + @store.save + end + +end + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_footer.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_footer.rhtml new file mode 100644 index 0000000..3d9526f --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_footer.rhtml @@ -0,0 +1,5 @@ + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_head.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_head.rhtml new file mode 100644 index 0000000..062160a --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_head.rhtml @@ -0,0 +1,22 @@ + + +<%= h @title %> + + + +<% if @options.template_stylesheets.flatten.any? then %> +<% @options.template_stylesheets.flatten.each do |stylesheet| %> + +<% end %> +<% end %> + + + + + + + + + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml new file mode 100644 index 0000000..e889f80 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml @@ -0,0 +1,19 @@ +<% if !svninfo.empty? then %> + +<% end %> diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_classes.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_classes.rhtml new file mode 100644 index 0000000..fe54d83 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_classes.rhtml @@ -0,0 +1,9 @@ + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_extends.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_extends.rhtml new file mode 100644 index 0000000..2bd8efe --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_extends.rhtml @@ -0,0 +1,15 @@ +<% unless klass.extends.empty? then %> + +<% end %> diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml new file mode 100644 index 0000000..0ba1d2b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml @@ -0,0 +1,9 @@ + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_includes.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_includes.rhtml new file mode 100644 index 0000000..d141098 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_includes.rhtml @@ -0,0 +1,15 @@ +<% unless klass.includes.empty? then %> + +<% end %> diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_installed.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_installed.rhtml new file mode 100644 index 0000000..1285bfd --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_installed.rhtml @@ -0,0 +1,15 @@ + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_methods.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_methods.rhtml new file mode 100644 index 0000000..45df08d --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_methods.rhtml @@ -0,0 +1,12 @@ +<% unless klass.method_list.empty? then %> + + +<% end %> diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml new file mode 100644 index 0000000..d7f3308 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_navigation.rhtml @@ -0,0 +1,11 @@ + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_pages.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_pages.rhtml new file mode 100644 index 0000000..5f39825 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_pages.rhtml @@ -0,0 +1,12 @@ +<% simple_files = @files.select { |f| f.text? } %> +<% unless simple_files.empty? then %> + +<% end %> diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_parent.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_parent.rhtml new file mode 100644 index 0000000..cc04852 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_parent.rhtml @@ -0,0 +1,11 @@ +<% if klass.type == 'class' then %> + +<% end %> diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_search.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_search.rhtml new file mode 100644 index 0000000..9c49b31 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_search.rhtml @@ -0,0 +1,14 @@ + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_sections.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_sections.rhtml new file mode 100644 index 0000000..15ff78b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_sections.rhtml @@ -0,0 +1,11 @@ +<% unless klass.sections.length == 1 then %> + +<% end %> diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml new file mode 100644 index 0000000..b58e6b3 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml @@ -0,0 +1,18 @@ +<% comment = if current.respond_to? :comment_location then + current.comment_location + else + current.comment + end + table = current.parse(comment).table_of_contents + + if table.length > 1 then %> + +<% end %> diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/class.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/class.rhtml new file mode 100644 index 0000000..b497000 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/class.rhtml @@ -0,0 +1,174 @@ + + + +
+

+ <%= klass.type %> <%= klass.full_name %> +

+ +
+ <%= klass.description %> +
+ + <% klass.each_section do |section, constants, attributes| %> + <% constants = constants.select { |const| const.display? } %> + <% attributes = attributes.select { |attr| attr.display? } %> +
+ <% if section.title then %> +
+

+ <%= section.title %> +

+ + ↑ top + +
+ <% end %> + + <% if section.comment then %> +
+ <%= section.description %> +
+ <% end %> + + <% unless constants.empty? then %> +
+
+

Constants

+
+
+ <% constants.each do |const| %> +
<%= const.name %> + <% if const.comment then %> +
<%= const.description.strip %> + <% else %> +
(Not documented) + <% end %> + <% end %> +
+
+ <% end %> + + <% unless attributes.empty? then %> +
+
+

Attributes

+
+ + <% attributes.each do |attrib| %> +
+
+ <%= h attrib.name %>[<%= attrib.rw %>] +
+ +
+ <% if attrib.comment then %> + <%= attrib.description.strip %> + <% else %> +

(Not documented) + <% end %> +

+
+ <% end %> +
+ <% end %> + + <% klass.methods_by_type(section).each do |type, visibilities| + next if visibilities.empty? + visibilities.each do |visibility, methods| + next if methods.empty? %> +
+
+

<%= visibility.to_s.capitalize %> <%= type.capitalize %> Methods

+
+ + <% methods.each do |method| %> +
"> + <% if method.call_seq then %> + <% method.call_seq.strip.split("\n").each_with_index do |call_seq, i| %> +
+ + <%= h(call_seq.strip. + gsub( /^\w+\./m, '')). + gsub(/(.*)[-=]>/, '\1→') %> + + <% if i == 0 and method.token_stream then %> + click to toggle source + <% end %> +
+ <% end %> + <% else %> +
+ <%= h method.name %><%= method.param_seq %> + <% if method.token_stream then %> + click to toggle source + <% end %> +
+ <% end %> + +
+ <% if method.comment then %> + <%= method.description.strip %> + <% else %> +

(Not documented) + <% end %> + <% if method.calls_super then %> +

+ Calls superclass method + <%= + method.superclass_method ? + method.formatter.link(method.superclass_method.full_name, method.superclass_method.full_name) : nil + %> +
+ <% end %> + + <% if method.token_stream then %> +
+
<%= method.markup_code %>
+
+ <% end %> +
+ + <% unless method.aliases.empty? then %> +
+ Also aliased as: <%= method.aliases.map do |aka| + if aka.parent then # HACK lib/rexml/encodings + %{#{h aka.name}} + else + h aka.name + end + end.join ", " %> +
+ <% end %> + + <% if method.is_alias_for then %> + + <% end %> +
+ + <% end %> +
+ <% end + end %> +
+<% end %> +
diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts.css b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts.css new file mode 100644 index 0000000..e9e7211 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts.css @@ -0,0 +1,167 @@ +/* + * Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), + * with Reserved Font Name "Source". All Rights Reserved. Source is a + * trademark of Adobe Systems Incorporated in the United States and/or other + * countries. + * + * This Font Software is licensed under the SIL Open Font License, Version + * 1.1. + * + * This license is copied below, and is also available with a FAQ at: + * http://scripts.sil.org/OFL + */ + +@font-face { + font-family: "Source Code Pro"; + font-style: normal; + font-weight: 400; + src: local("Source Code Pro"), + local("SourceCodePro-Regular"), + url("fonts/SourceCodePro-Regular.ttf") format("truetype"); +} + +@font-face { + font-family: "Source Code Pro"; + font-style: normal; + font-weight: 700; + src: local("Source Code Pro Bold"), + local("SourceCodePro-Bold"), + url("fonts/SourceCodePro-Bold.ttf") format("truetype"); +} + +/* + * Copyright (c) 2010, Åukasz Dziedzic (dziedzic@typoland.com), + * with Reserved Font Name Lato. + * + * This Font Software is licensed under the SIL Open Font License, Version + * 1.1. + * + * This license is copied below, and is also available with a FAQ at: + * http://scripts.sil.org/OFL + */ + +@font-face { + font-family: "Lato"; + font-style: normal; + font-weight: 300; + src: local("Lato Light"), + local("Lato-Light"), + url("fonts/Lato-Light.ttf") format("truetype"); +} + +@font-face { + font-family: "Lato"; + font-style: italic; + font-weight: 300; + src: local("Lato Light Italic"), + local("Lato-LightItalic"), + url("fonts/Lato-LightItalic.ttf") format("truetype"); +} + +@font-face { + font-family: "Lato"; + font-style: normal; + font-weight: 700; + src: local("Lato Regular"), + local("Lato-Regular"), + url("fonts/Lato-Regular.ttf") format("truetype"); +} + +@font-face { + font-family: "Lato"; + font-style: italic; + font-weight: 700; + src: local("Lato Italic"), + local("Lato-Italic"), + url("fonts/Lato-RegularItalic.ttf") format("truetype"); +} + +/* + * ----------------------------------------------------------- + * SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 + * ----------------------------------------------------------- + * + * PREAMBLE + * The goals of the Open Font License (OFL) are to stimulate worldwide + * development of collaborative font projects, to support the font creation + * efforts of academic and linguistic communities, and to provide a free and + * open framework in which fonts may be shared and improved in partnership + * with others. + * + * The OFL allows the licensed fonts to be used, studied, modified and + * redistributed freely as long as they are not sold by themselves. The + * fonts, including any derivative works, can be bundled, embedded, + * redistributed and/or sold with any software provided that any reserved + * names are not used by derivative works. The fonts and derivatives, + * however, cannot be released under any other type of license. The + * requirement for fonts to remain under this license does not apply + * to any document created using the fonts or their derivatives. + * + * DEFINITIONS + * "Font Software" refers to the set of files released by the Copyright + * Holder(s) under this license and clearly marked as such. This may + * include source files, build scripts and documentation. + * + * "Reserved Font Name" refers to any names specified as such after the + * copyright statement(s). + * + * "Original Version" refers to the collection of Font Software components as + * distributed by the Copyright Holder(s). + * + * "Modified Version" refers to any derivative made by adding to, deleting, + * or substituting -- in part or in whole -- any of the components of the + * Original Version, by changing formats or by porting the Font Software to a + * new environment. + * + * "Author" refers to any designer, engineer, programmer, technical + * writer or other person who contributed to the Font Software. + * + * PERMISSION & CONDITIONS + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of the Font Software, to use, study, copy, merge, embed, modify, + * redistribute, and sell modified and unmodified copies of the Font + * Software, subject to the following conditions: + * + * 1) Neither the Font Software nor any of its individual components, + * in Original or Modified Versions, may be sold by itself. + * + * 2) Original or Modified Versions of the Font Software may be bundled, + * redistributed and/or sold with any software, provided that each copy + * contains the above copyright notice and this license. These can be + * included either as stand-alone text files, human-readable headers or + * in the appropriate machine-readable metadata fields within text or + * binary files as long as those fields can be easily viewed by the user. + * + * 3) No Modified Version of the Font Software may use the Reserved Font + * Name(s) unless explicit written permission is granted by the corresponding + * Copyright Holder. This restriction only applies to the primary font name as + * presented to the users. + * + * 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font + * Software shall not be used to promote, endorse or advertise any + * Modified Version, except to acknowledge the contribution(s) of the + * Copyright Holder(s) and the Author(s) or with their explicit written + * permission. + * + * 5) The Font Software, modified or unmodified, in part or in whole, + * must be distributed entirely under this license, and must not be + * distributed under any other license. The requirement for fonts to + * remain under this license does not apply to any document created + * using the Font Software. + * + * TERMINATION + * This license becomes null and void if any of the above conditions are + * not met. + * + * DISCLAIMER + * THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL + * DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM + * OTHER DEALINGS IN THE FONT SOFTWARE. + */ + diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf new file mode 100644 index 0000000..b49dd43 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-Light.ttf differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf new file mode 100644 index 0000000..7959fef Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-LightItalic.ttf differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf new file mode 100644 index 0000000..839cd58 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-Regular.ttf differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf new file mode 100644 index 0000000..bababa0 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/Lato-RegularItalic.ttf differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf new file mode 100644 index 0000000..61e3090 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf new file mode 100644 index 0000000..85686d9 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/add.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/add.png new file mode 100644 index 0000000..6332fef Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/add.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/arrow_up.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/arrow_up.png new file mode 100644 index 0000000..1ebb193 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/arrow_up.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/brick.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/brick.png new file mode 100644 index 0000000..7851cf3 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/brick.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/brick_link.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/brick_link.png new file mode 100644 index 0000000..9ebf013 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/brick_link.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bug.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bug.png new file mode 100644 index 0000000..2d5fb90 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bug.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bullet_black.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bullet_black.png new file mode 100644 index 0000000..5761970 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bullet_black.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png new file mode 100644 index 0000000..b47ce55 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bullet_toggle_minus.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png new file mode 100644 index 0000000..9ab4a89 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/bullet_toggle_plus.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/date.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/date.png new file mode 100644 index 0000000..783c833 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/date.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/delete.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/delete.png new file mode 100644 index 0000000..08f2493 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/delete.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/find.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/find.png new file mode 100644 index 0000000..1547479 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/find.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/loadingAnimation.gif b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/loadingAnimation.gif new file mode 100644 index 0000000..82290f4 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/loadingAnimation.gif differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/macFFBgHack.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/macFFBgHack.png new file mode 100644 index 0000000..c6473b3 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/macFFBgHack.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/package.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/package.png new file mode 100644 index 0000000..da3c2a2 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/package.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/page_green.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/page_green.png new file mode 100644 index 0000000..de8e003 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/page_green.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/page_white_text.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/page_white_text.png new file mode 100644 index 0000000..813f712 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/page_white_text.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/page_white_width.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/page_white_width.png new file mode 100644 index 0000000..1eb8809 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/page_white_width.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/plugin.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/plugin.png new file mode 100644 index 0000000..6187b15 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/plugin.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/ruby.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/ruby.png new file mode 100644 index 0000000..f763a16 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/ruby.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/tag_blue.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/tag_blue.png new file mode 100644 index 0000000..3f02b5f Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/tag_blue.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/tag_green.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/tag_green.png new file mode 100644 index 0000000..83ec984 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/tag_green.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/transparent.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/transparent.png new file mode 100644 index 0000000..d665e17 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/transparent.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/wrench.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/wrench.png new file mode 100644 index 0000000..5c8213f Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/wrench.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/wrench_orange.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/wrench_orange.png new file mode 100644 index 0000000..565a933 Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/wrench_orange.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/zoom.png b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/zoom.png new file mode 100644 index 0000000..908612e Binary files /dev/null and b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/images/zoom.png differ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/index.rhtml b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/index.rhtml new file mode 100644 index 0000000..7d1c748 --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/index.rhtml @@ -0,0 +1,23 @@ + + + +
+<% if @options.main_page and + main_page = @files.find { |f| f.full_name == @options.main_page } then %> +<%= main_page.description %> +<% else %> +

This is the API documentation for <%= @title %>. +<% end %> +

+ diff --git a/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/js/darkfish.js b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/js/darkfish.js new file mode 100644 index 0000000..06fef3b --- /dev/null +++ b/ruby/lib/ruby/2.1.0/rdoc/generator/template/darkfish/js/darkfish.js @@ -0,0 +1,140 @@ +/** + * + * Darkfish Page Functions + * $Id: darkfish.js 53 2009-01-07 02:52:03Z deveiant $ + * + * Author: Michael Granger + * + */ + +/* Provide console simulation for firebug-less environments */ +if (!("console" in window) || !("firebug" in console)) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", + "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; + + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +}; + + +/** + * Unwrap the first element that matches the given @expr@ from the targets and return them. + */ +$.fn.unwrap = function( expr ) { + return this.each( function() { + $(this).parents( expr ).eq( 0 ).after( this ).remove(); + }); +}; + + +function showSource( e ) { + var target = e.target; + var codeSections = $(target). + parents('.method-detail'). + find('.method-source-code'); + + $(target). + parents('.method-detail'). + find('.method-source-code'). + slideToggle(); +}; + +function hookSourceViews() { + $('.method-heading').click( showSource ); +}; + +function toggleDebuggingSection() { + $('.debugging-section').slideToggle(); +}; + +function hookDebuggingToggle() { + $('#debugging-toggle img').click( toggleDebuggingSection ); +}; + +function hookSearch() { + var input = $('#search-field').eq(0); + var result = $('#search-results').eq(0); + $(result).show(); + + var search_section = $('#search-section').get(0); + $(search_section).show(); + + var search = new Search(search_data, input, result); + + search.renderItem = function(result) { + var li = document.createElement('li'); + var html = ''; + + // TODO add relative path to