-
-
Notifications
You must be signed in to change notification settings - Fork 925
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Truffle] Make a separate copy of MRI stdlib again, so we're isolated…
… from JRuby's update to 2.4
- 9.4.12.0
- 9.4.11.0
- 9.4.10.0
- 9.4.9.0
- 9.4.8.0
- 9.4.7.0
- 9.4.6.0
- 9.4.5.0
- 9.4.4.0
- 9.4.3.0
- 9.4.2.0
- 9.4.1.0
- 9.4.0.0
- 9.3.15.0
- 9.3.14.0
- 9.3.13.0
- 9.3.12.0
- 9.3.11.0
- 9.3.10.0
- 9.3.9.0
- 9.3.8.0
- 9.3.7.0
- 9.3.6.0
- 9.3.5.0
- 9.3.4.0
- 9.3.3.0
- 9.3.2.0
- 9.3.1.0
- 9.3.0.0
- 9.2.21.0
- 9.2.20.1
- 9.2.20.0
- 9.2.19.0
- 9.2.18.0
- 9.2.17.0
- 9.2.16.0
- 9.2.15.0
- 9.2.14.0
- 9.2.13.0
- 9.2.12.0
- 9.2.11.1
- 9.2.11.0
- 9.2.10.0
- 9.2.9.0
- 9.2.8.0
- 9.2.7.0
- 9.2.6.0
- 9.2.5.0
- 9.2.4.1
- 9.2.4.0
- 9.2.3.0
- 9.2.2.0
- 9.2.1.0
- 9.2.0.0
- 9.1.17.0
- 9.1.16.0
- 9.1.15.0
- 9.1.14.0
- 9.1.13.0
- 9.1.12.0
- 9.1.11.0
- 9.1.10.0
- 9.1.9.0
- 9.1.8.0
- 9.1.7.0
1 parent
c2d4c15
commit 14f3823
Showing
772 changed files
with
185,782 additions
and
669 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,191 @@ | ||
require_relative '../../stdlib/English' | ||
# frozen_string_literal: false | ||
# | ||
# Modified for JRuby | ||
# In JRuby, we define these aliases by default, so this file | ||
# does nothing. | ||
# | ||
# 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 $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. <tt>See Kernel.caller</tt> for details. Thread local. | ||
#alias $ERROR_POSITION $@ | ||
|
||
# The default separator pattern used by <tt>String.split</tt>. May be | ||
# set from the command line using the <tt>-F</tt> flag. | ||
#alias $FS $; | ||
|
||
# The default separator pattern used by <tt>String.split</tt>. May be | ||
# set from the command line using the <tt>-F</tt> flag. | ||
#alias $FIELD_SEPARATOR $; | ||
|
||
# The separator string output between the parameters to methods such | ||
# as <tt>Kernel.print</tt> and <tt>Array.join</tt>. Defaults to +nil+, | ||
# which adds no text. | ||
#alias $OFS $, | ||
|
||
# The separator string output between the parameters to methods such | ||
# as <tt>Kernel.print</tt> and <tt>Array.join</tt>. 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 <tt>Kernel.gets</tt> 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 <tt>Kernel.gets</tt> 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 | ||
# <tt>Kernel.print</tt> and <tt>IO.write</tt>. The default value is | ||
# +nil+. | ||
#alias $ORS $\ | ||
|
||
# The string appended to the output of every call to methods such as | ||
# <tt>Kernel.print</tt> and <tt>IO.write</tt>. 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 <tt>Kernel.gets</tt> or | ||
# <tt>Kernel.readline</tt>. Many string-related functions in the | ||
# +Kernel+ module operate on <tt>$_</tt> by default. The variable is | ||
# local to the current scope. Thread local. | ||
#alias $LAST_READ_LINE $_ | ||
|
||
# The destination of output for <tt>Kernel.print</tt> | ||
# and <tt>Kernel.printf</tt>. The default value is | ||
# <tt>$stdout</tt>. | ||
#alias $DEFAULT_OUTPUT $> | ||
|
||
# An object that provides access to the concatenation | ||
# of the contents of all the files | ||
# given as command-line arguments, or <tt>$stdin</tt> | ||
# (in the case where there are no | ||
# arguments). <tt>$<</tt> supports methods similar to a | ||
# +File+ object: | ||
# +inmode+, +close+, | ||
# <tt>closed?</tt>, +each+, | ||
# <tt>each_byte</tt>, <tt>each_line</tt>, | ||
# +eof+, <tt>eof?</tt>, +file+, | ||
# +filename+, +fileno+, | ||
# +getc+, +gets+, +lineno+, | ||
# <tt>lineno=</tt>, +path+, | ||
# +pos+, <tt>pos=</tt>, | ||
# +read+, +readchar+, | ||
# +readline+, +readlines+, | ||
# +rewind+, +seek+, +skip+, | ||
# +tell+, <tt>to_a</tt>, <tt>to_i</tt>, | ||
# <tt>to_io</tt>, <tt>to_s</tt>, along with the | ||
# methods in +Enumerable+. The method +file+ | ||
# returns a +File+ object for the file currently | ||
# being read. This may change as <tt>$<</tt> 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 <tt>$&</tt>, <tt>$`</tt>, <tt>$'</tt>, | ||
# and <tt>$1</tt> to <tt>$9</tt> are all derived from | ||
# <tt>$~</tt>. Assigning to <tt>$~</tt> 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 <tt>"cat" =~ /(c|a)(t|z)/</tt>, | ||
# <tt>$+</tt> will be set to "t". This variable is local to the | ||
# current scope. Read only. | ||
#alias $LAST_PAREN_MATCH $+ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,132 @@ | ||
require_relative '../../stdlib/abbrev' | ||
# frozen_string_literal: false | ||
#-- | ||
# Copyright (c) 2001,2003 Akinori MUSHA <knu@iDaemons.org> | ||
# | ||
# 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$ | ||
#++ | ||
|
||
## | ||
# Calculates the set of unambiguous abbreviations for a given set of strings. | ||
# | ||
# require 'abbrev' | ||
# require 'pp' | ||
# | ||
# pp Abbrev.abbrev(['ruby']) | ||
# #=> {"ruby"=>"ruby", "rub"=>"ruby", "ru"=>"ruby", "r"=>"ruby"} | ||
# | ||
# pp Abbrev.abbrev(%w{ ruby rules }) | ||
# | ||
# _Generates:_ | ||
# { "ruby" => "ruby", | ||
# "rub" => "ruby", | ||
# "rules" => "rules", | ||
# "rule" => "rules", | ||
# "rul" => "rules" } | ||
# | ||
# It also provides an array core extension, Array#abbrev. | ||
# | ||
# pp %w{ summer winter }.abbrev | ||
# | ||
# _Generates:_ | ||
# { "summer" => "summer", | ||
# "summe" => "summer", | ||
# "summ" => "summer", | ||
# "sum" => "summer", | ||
# "su" => "summer", | ||
# "s" => "summer", | ||
# "winter" => "winter", | ||
# "winte" => "winter", | ||
# "wint" => "winter", | ||
# "win" => "winter", | ||
# "wi" => "winter", | ||
# "w" => "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(%w{ 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 crab}, /b/) | ||
# #=> {"box"=>"box", "bo"=>"box", "b"=>"box", "crab" => "crab"} | ||
# | ||
# Abbrev.abbrev(%w{car box cone}, 'ca') | ||
# #=> {"car"=>"car", "ca"=>"car"} | ||
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 | ||
# #=> {"car"=>"car", "ca"=>"car", "cone"=>"cone", "con"=>"cone", "co"=>"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/) | ||
# #=> {"fast"=>"fast", "fas"=>"fast", "fa"=>"fast", "day"=>"day", "da"=>"day"} | ||
# | ||
# Abbrev.abbrev(%w{car box cone}, "ca") | ||
# #=> {"car"=>"car", "ca"=>"car"} | ||
# | ||
# See also Abbrev.abbrev | ||
def abbrev(pattern = nil) | ||
Abbrev::abbrev(self, pattern) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,107 @@ | ||
require_relative '../../stdlib/base64' | ||
# frozen_string_literal: false | ||
# | ||
# = 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") | ||
# | ||
# <i>Generates:</i> | ||
# | ||
# 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) | ||
# | ||
# <i>Generates:</i> | ||
# | ||
# 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 '/'. | ||
# Note that the result can still contain '='. | ||
# You can remove the padding by setting +padding+ as false. | ||
def urlsafe_encode64(bin, padding: true) | ||
str = strict_encode64(bin).tr("+/", "-_") | ||
str = str.delete("=") unless padding | ||
str | ||
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 '/'. | ||
# | ||
# The padding character is optional. | ||
# This method accepts both correctly-padded and unpadded input. | ||
# Note that it still rejects incorrectly-padded input. | ||
def urlsafe_decode64(str) | ||
# NOTE: RFC 4648 does say nothing about unpadded input, but says that | ||
# "the excess pad characters MAY also be ignored", so it is inferred that | ||
# unpadded input is also acceptable. | ||
str = str.tr("-_", "+/") | ||
if !str.end_with?("=") && str.length % 4 != 0 | ||
str = str.ljust((str.length + 3) & ~3, "=") | ||
end | ||
strict_decode64(str) | ||
end | ||
end |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,88 @@ | ||
require_relative '../../../stdlib/bigdecimal/jacobian' | ||
# frozen_string_literal: false | ||
# | ||
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,89 @@ | ||
require_relative '../../../stdlib/bigdecimal/ludcmp' | ||
# frozen_string_literal: false | ||
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 |i2| | ||
dot = zero | ||
psin = ps[i2]*n | ||
for j in (i2+1)...n do | ||
dot = a[psin+j].mult(x[j],prec) + dot | ||
end | ||
x[i2] = (x[i2]-dot).div(a[psin+i2],prec) | ||
end | ||
x | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,246 @@ | ||
require_relative '../../../stdlib/bigdecimal/math' | ||
# frozen_string_literal: false | ||
require 'bigdecimal' | ||
require 'bigdecimal/util' | ||
|
||
# | ||
#-- | ||
# 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.1414213562373095048801688724E1" | ||
# | ||
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 precision 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 | ||
n = prec + BigDecimal.double_fig | ||
one = BigDecimal("1") | ||
y = one | ||
d = y | ||
z = one | ||
i = 0 | ||
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0) | ||
m = BigDecimal.double_fig if m < BigDecimal.double_fig | ||
i += 1 | ||
z *= i | ||
d = one.div(z,m) | ||
y += d | ||
end | ||
y | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,80 @@ | ||
require_relative '../../../stdlib/bigdecimal/newton' | ||
# frozen_string_literal: false | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,128 @@ | ||
require_relative '../../../stdlib/bigdecimal/util' | ||
# frozen_string_literal: false | ||
# 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 | ||
# # => #<BigDecimal:1008ef070,'0.42E2',9(36)> | ||
# | ||
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 | ||
# # => #<BigDecimal:1dc69e0,'0.5E0',9(18)> | ||
# | ||
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 | ||
# # => #<BigDecimal:1dc69e0,'0.5E0',9(18)> | ||
# | ||
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) | ||
# # => #<BigDecimal:1a44d08,'0.314E1',18(36)> | ||
def to_d(precision) | ||
if precision <= 0 | ||
raise ArgumentError, "negative precision" | ||
end | ||
num = self.numerator | ||
BigDecimal(num).div(self.denominator, precision) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,296 @@ | ||
require_relative '../../stdlib/cgi' | ||
# frozen_string_literal: false | ||
# | ||
# cgi.rb - cgi support library | ||
# | ||
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc. | ||
# | ||
# Copyright (C) 2000 Information-technology Promotion Agency, Japan | ||
# | ||
# Author: Wakou Aoyama <wakou@ruby-lang.org> | ||
# | ||
# Documentation: Wakou Aoyama (RDoc'd and embellished by William Webber) | ||
# | ||
|
||
# == 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 behavior 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" <baz>') | ||
# | ||
# | ||
# === Some utility methods like a function | ||
# | ||
# require 'cgi/util' | ||
# include CGI::Util | ||
# escapeHTML('Usage: foo "bar" <baz>') | ||
# h('Usage: foo "bar" <baz>') # alias | ||
# | ||
# | ||
|
||
class CGI | ||
end | ||
|
||
require 'cgi/core' | ||
require 'cgi/cookie' | ||
require 'cgi/util' | ||
CGI.autoload(:HtmlExtension, 'cgi/html') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,188 @@ | ||
require_relative '../../../stdlib/cgi/cookie' | ||
# frozen_string_literal: false | ||
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 | ||
# 'httponly' => 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 | ||
# httponly = cookie1.httponly | ||
# | ||
# cookie1.name = 'name' | ||
# cookie1.value = ['value1', 'value2', ...] | ||
# cookie1.path = 'path' | ||
# cookie1.domain = 'domain' | ||
# cookie1.expires = Time.now + 30 | ||
# cookie1.secure = true | ||
# cookie1.httponly = 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. | ||
# <tt>*value</tt>:: | ||
# 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. | ||
# httponly:: whether this cookie is a HttpOnly cookie or not (default to | ||
# false). HttpOnly cookies are not available to javascript. | ||
# | ||
# 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 | ||
@httponly = 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 | ||
@httponly = options["httponly"] == true | ||
|
||
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 | ||
# True if this cookie is httponly; false otherwise | ||
attr_reader :httponly | ||
|
||
# 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 | ||
|
||
# Set whether the Cookie is a httponly cookie or not. | ||
# | ||
# +val+ must be a boolean. | ||
def httponly=(val) | ||
@httponly = !!val | ||
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 | ||
buf << "; HttpOnly" if @httponly | ||
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 | ||
"#<CGI::Cookie: #{self.to_s.inspect}>" | ||
end | ||
|
||
end # class Cookie | ||
end | ||
|
||
|
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,101 @@ | ||
require_relative '../../../../stdlib/cgi/session/pstore' | ||
# frozen_string_literal: false | ||
# | ||
# 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 | ||
# :enddoc: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,200 @@ | ||
require_relative '../../../stdlib/cgi/util' | ||
# frozen_string_literal: false | ||
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" <baz>') | ||
# # => "Usage: foo "bar" <baz>" | ||
def escapeHTML(string) | ||
string.gsub(/['&\"<>]/, TABLE_FOR_ESCAPE_HTML__) | ||
end | ||
|
||
begin | ||
require 'cgi/escape' | ||
rescue LoadError | ||
end | ||
|
||
# Unescape a string that has been HTML-escaped | ||
# CGI::unescapeHTML("Usage: foo "bar" <baz>") | ||
# # => "Usage: foo \"bar\" <baz>" | ||
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) | ||
alias escape_html escapeHTML | ||
|
||
# Synonym for CGI::unescapeHTML(str) | ||
alias unescape_html unescapeHTML | ||
|
||
# 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('<BR><A HREF="url"></A>', "A", "IMG") | ||
# # "<BR><A HREF="url"></A>" | ||
# | ||
# print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"]) | ||
# # "<BR><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('<BR><A HREF="url"></A>'), "A", "IMG") | ||
# # "<BR><A HREF="url"></A>" | ||
# | ||
# print CGI::unescapeElement( | ||
# CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"]) | ||
# # "<BR><A HREF="url"></A>" | ||
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) | ||
alias escape_element escapeElement | ||
|
||
# Synonym for CGI::unescapeElement(str) | ||
alias unescape_element unescapeElement | ||
|
||
# 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("<HTML><BODY></BODY></HTML>") | ||
# # <HTML> | ||
# # <BODY> | ||
# # </BODY> | ||
# # </HTML> | ||
# | ||
# print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t") | ||
# # <HTML> | ||
# # <BODY> | ||
# # </BODY> | ||
# # </HTML> | ||
# | ||
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 |
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
require_relative '../../stdlib/drb' | ||
# frozen_string_literal: false | ||
require 'drb/drb' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,233 @@ | ||
require_relative '../../../stdlib/drb/acl' | ||
# frozen_string_literal: false | ||
# 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}" | ||
end | ||
i += 2 | ||
end | ||
end | ||
|
||
end |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,15 @@ | ||
require_relative '../../../stdlib/drb/eq' | ||
# frozen_string_literal: false | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,44 @@ | ||
require_relative '../../../stdlib/drb/extserv' | ||
# frozen_string_literal: false | ||
=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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,94 @@ | ||
require_relative '../../../stdlib/drb/extservm' | ||
# frozen_string_literal: false | ||
=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&.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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,161 @@ | ||
require_relative '../../../stdlib/drb/gw' | ||
# frozen_string_literal: false | ||
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,35 @@ | ||
require_relative '../../../stdlib/drb/invokemethod' | ||
# frozen_string_literal: false | ||
# 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,26 @@ | ||
require_relative '../../../stdlib/drb/observer' | ||
# frozen_string_literal: false | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,346 @@ | ||
require_relative '../../../stdlib/drb/ssl' | ||
# frozen_string_literal: false | ||
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: | ||
# <code>drbssl://<host>:<port>?<option></code>. 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, | ||
# <code>'drbssl://localhost:0'</code> 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, | ||
# <code>'drbssl://localhost:0'</code> 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 = accept_or_shutdown | ||
return nil unless soc | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,102 @@ | ||
require_relative '../../../stdlib/drb/timeridconv' | ||
# frozen_string_literal: false | ||
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,118 @@ | ||
require_relative '../../../stdlib/drb/unix' | ||
# frozen_string_literal: false | ||
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 <code>drbunix:<path>?<option></code>. 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 | ||
close_shutdown_pipe | ||
end | ||
|
||
def accept | ||
s = accept_or_shutdown | ||
return nil unless s | ||
self.class.new(nil, s, @config) | ||
end | ||
|
||
def set_sockopt(soc) | ||
# no-op for now | ||
end | ||
end | ||
|
||
DRbProtocol.add_protocol(DRbUNIXSocket) | ||
# :startdoc: | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,171 @@ | ||
require_relative '../../stdlib/e2mmap' | ||
# frozen_string_literal: false | ||
# | ||
#-- | ||
# 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 | ||
|
||
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) : $@ | ||
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]] | ||
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 |
Oops, something went wrong.