got pry working with pry-doc, and modified show-doc and show-method

commands to use pry-doc if pry-doc exists, otherwise to use
source_location based techniques.
This commit is contained in:
John Mair 2011-03-14 14:46:50 +13:00
parent f8ccad729d
commit 43ff3593f9
3037 changed files with 73 additions and 2824 deletions

View File

@ -8,7 +8,7 @@ require "#{direc}/lib/pry/version"
CLOBBER.include("**/*.#{dlext}", "**/*~", "**/*#*", "**/*.log", "**/*.o") CLOBBER.include("**/*.#{dlext}", "**/*~", "**/*#*", "**/*.log", "**/*.o")
CLEAN.include("ext/**/*.#{dlext}", "ext/**/*.log", "ext/**/*.o", CLEAN.include("ext/**/*.#{dlext}", "ext/**/*.log", "ext/**/*.o",
"ext/**/*~", "ext/**/*#*", "ext/**/*.obj", "**/*#*", "**/*#*.*", "ext/**/*~", "ext/**/*#*", "ext/**/*.obj", "**/*#*", "**/*#*.*",
"ext/**/*.def", "ext/**/*.pdb", "**/*_flymake*.*", "**/*_flymake") "ext/**/*.def", "ext/**/*.pdb", "**/*_flymake*.*", "**/*_flymake", "**/*.rbc")
def apply_spec_defaults(s) def apply_spec_defaults(s)
s.name = "pry" s.name = "pry"
@ -25,8 +25,8 @@ def apply_spec_defaults(s)
s.homepage = "http://banisterfiend.wordpress.com" s.homepage = "http://banisterfiend.wordpress.com"
s.has_rdoc = 'yard' s.has_rdoc = 'yard'
s.executables = ["pry"] s.executables = ["pry"]
s.files = Dir["ext/**/extconf.rb", "ext/**/*.h", "ext/**/*.c", "lib/**/*.rb", "examples/**/*.rb", s.files = Dir["ext/**/extconf.rb", "ext/**/*.h", "ext/**/*.c", "lib/**/*", "examples/**/*.rb",
"test/*.rb", "test/testrc", "CHANGELOG", "LICENSE", "README.markdown", "Rakefile", ".gemtest"] "test/*.rb", "test/testrc", "CHANGELOG", "LICENSE", "README.markdown", "Rakefile", ".gemtest"]
end end
task :test do task :test do

View File

@ -3,6 +3,11 @@ require "method_source"
require "pry/command_base" require "pry/command_base"
require "pry/pry_instance" require "pry/pry_instance"
begin
require "pry-doc"
rescue LoadError
end
class Pry class Pry
# Default commands used by Pry. # Default commands used by Pry.
@ -90,55 +95,6 @@ class Pry
output.puts "Last result: #{Pry.view(Pry.last_result)}" output.puts "Last result: #{Pry.view(Pry.last_result)}"
end end
# FIXME!!! some methods, e.g Kernel#tap actually require
# Object#tap in order to retrieve their docs!!!
begin
require "yard"
if RUBY_VERSION =~ /1.9/
YARD::Registry.load_yardoc("#{File.dirname(__FILE__)}/core_docs_19")
else
YARD::Registry.load_yardoc("#{File.dirname(__FILE__)}/core_docs_18")
end
yard_cache = {}
command "doc" do |meth_name|
begin
meth = target.eval("method(:#{meth_name})")
rescue
begin
meth = target.eval("instance_method(:#{meth_name})")
rescue
output.puts "Error could not find method: #{meth_name}"
next
end
end
if meth.source_location
file = meth.source_location.first
if !yard_cache[file]
log.enter_level(Logger::FATAL) do
YARD.parse(file)
end
yard_cache[file] = true
end
end
if meth.owner.name # instance method
klassinst, klass = '#', meth.owner.name
else # class method
klassinst, klass = '.', meth.owner.to_s[/#<.+?:(.+?)>/, 1]
end
if obj = YARD::Registry.at([klass, meth.name].join(klassinst))
output.puts obj.docstring
else
output.puts "Couldn't find docs for: #{meth_name}"
end
end
rescue LoadError
end
command "whereami", "Show the code context for the session." do command "whereami", "Show the code context for the session." do
file = target.eval('__FILE__') file = target.eval('__FILE__')
line_num = target.eval('__LINE__') line_num = target.eval('__LINE__')
@ -177,7 +133,6 @@ class Pry
end end
end end
command "version", "Show Pry version." do command "version", "Show Pry version." do
output.puts "Pry version: #{Pry::VERSION} on Ruby #{RUBY_VERSION}." output.puts "Pry version: #{Pry::VERSION} on Ruby #{RUBY_VERSION}."
end end
@ -242,6 +197,10 @@ Shows local and instance variables by default.
options[:p] = true options[:p] = true
end end
opts.on("-j", "--just-singletons", "Display just the singleton methods (with -m).") do
options[:j] = true
end
opts.on("-s", "--super", "Include superclass entries (relevant to constant and methods options).") do opts.on("-s", "--super", "Include superclass entries (relevant to constant and methods options).") do
options[:s] = true options[:s] = true
end end
@ -274,7 +233,7 @@ Shows local and instance variables by default.
}) if options.empty? || (options.size == 1 && options[:v]) }) if options.empty? || (options.size == 1 && options[:v])
# Display public methods by default if -m or -M switch is used. # Display public methods by default if -m or -M switch is used.
options[:P] = true if (options[:m] || options[:M]) && !(options[:p] || options[:r]) options[:P] = true if (options[:m] || options[:M]) && !(options[:p] || options[:r] || options[:j])
info = {} info = {}
target_self = target.eval('self') target_self = target.eval('self')
@ -304,6 +263,8 @@ Shows local and instance variables by default.
info["private methods"] = [Array(target.eval("private_methods(#{options[:s]})")).sort, i += 1] if (options[:m] && options[:p]) || options[:a] info["private methods"] = [Array(target.eval("private_methods(#{options[:s]})")).sort, i += 1] if (options[:m] && options[:p]) || options[:a]
info["just singleton methods"] = [Array(target.eval("methods(#{options[:s]})")).sort, i += 1] if (options[:m] && options[:j]) || options[:a]
info["public instance methods"] = [Array(target.eval("public_instance_methods(#{options[:s]})")).uniq.sort, i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:P]) || options[:a]) info["public instance methods"] = [Array(target.eval("public_instance_methods(#{options[:s]})")).uniq.sort, i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:P]) || options[:a])
info["protected instance methods"] = [Array(target.eval("protected_instance_methods(#{options[:s]})")).uniq.sort, i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:r]) || options[:a]) info["protected instance methods"] = [Array(target.eval("protected_instance_methods(#{options[:s]})")).uniq.sort, i += 1] if target_self.is_a?(Module) && ((options[:M] && options[:r]) || options[:a])
@ -338,6 +299,7 @@ Shows local and instance variables by default.
# plain # plain
else else
list = info.values.sort_by { |v| v.last }.map { |v| v.first }.inject(&:+) list = info.values.sort_by { |v| v.last }.map { |v| v.first }.inject(&:+)
list.uniq! if list
if Pry.color if Pry.color
output.puts CodeRay.scan(Pry.view(list), :ruby).term output.puts CodeRay.scan(Pry.view(list), :ruby).term
else else
@ -440,10 +402,14 @@ Show the comments above method METH. Shows _method_ comments (rather than instan
e.g show-doc hello_method e.g show-doc hello_method
-- --
} }
opts.on("-M", "--instance-methods", "Operate on instance methods instead.") do opts.on("-M", "--instance-methods", "Operate on instance methods.") do
options[:M] = true options[:M] = true
end end
opts.on("-m", "--methods", "Operate on methods.") do
options[:m] = true
end
opts.on("-c", "--context CONTEXT", "Select object context to run under.") do |context| opts.on("-c", "--context CONTEXT", "Select object context to run under.") do |context|
target = Pry.binding_for(target.eval(context)) target = Pry.binding_for(target.eval(context))
end end
@ -466,24 +432,35 @@ e.g show-doc hello_method
begin begin
if options[:M] if options[:M]
meth = target.eval("instance_method(:#{meth_name})") meth = target.eval("instance_method(:#{meth_name})")
else elsif options[:m]
meth = target.eval("method(:#{meth_name})") meth = target.eval("method(:#{meth_name})")
else
begin
meth = target.eval("method(:#{meth_name})")
rescue
meth = target.eval("instance_method(:#{meth_name})")
end
end end
rescue rescue
output.puts "Invalid method name: #{meth_name}. Type `show-doc --help` for help" output.puts "Invalid method name: #{meth_name}. Type `show-doc --help` for help"
next next
end end
doc = meth.comment if Pry.has_pry_doc
info = Pry::MethodInfo.info_for(meth)
if !info
doc = meth.comment
else
doc = info.docstring
end
else
doc = meth.comment
end
file, line = meth.source_location file, line = meth.source_location
check_for_dynamically_defined_method.call(file) check_for_dynamically_defined_method.call(file)
output.puts "--\nFrom #{file} @ line ~#{line}:\n--" output.puts "--\nFrom #{file} @ line ~#{line}:\n--"
if Pry.color
doc = CodeRay.scan(doc, :ruby).term
end
output.puts doc output.puts doc
doc doc
end end
@ -499,10 +476,14 @@ Show the source for method METH. Shows _method_ source (rather than instance met
e.g: show-method hello_method e.g: show-method hello_method
-- --
} }
opts.on("-M", "--instance-methods", "Operate on instance methods instead.") do opts.on("-M", "--instance-methods", "Operate on instance methods.") do
options[:M] = true options[:M] = true
end end
opts.on("-m", "--methods", "Operate on methods..") do
options[:m] = true
end
opts.on("-c", "--context CONTEXT", "Select object context to run under.") do |context| opts.on("-c", "--context CONTEXT", "Select object context to run under.") do |context|
target = Pry.binding_for(target.eval(context)) target = Pry.binding_for(target.eval(context))
end end
@ -528,27 +509,45 @@ e.g: show-method hello_method
begin begin
if options[:M] if options[:M]
meth = target.eval("instance_method(:#{meth_name})") meth = target.eval("instance_method(:#{meth_name})")
else elsif options[:m]
meth = target.eval("method(:#{meth_name})") meth = target.eval("method(:#{meth_name})")
else
begin
meth = target.eval("method(:#{meth_name})")
rescue
meth = target.eval("instance_method(:#{meth_name})")
end
end end
rescue rescue
target_self = target.eval('self') target_self = target.eval('self')
if !options[:M]&& target_self.is_a?(Module) &&
target_self.method_defined?(meth_name)
output.puts "Did you mean: show-method -M #{meth_name} ?"
end
output.puts "Invalid method name: #{meth_name}. Type `show-method --help` for help" output.puts "Invalid method name: #{meth_name}. Type `show-method --help` for help"
next next
end end
code = meth.source meth_type = :ruby
# Try to find source for C methods using MethodInfo (if possible)
if Pry.has_pry_doc && meth.source_location.nil?
info = Pry::MethodInfo.info_for(meth)
if !info
output.puts "Cannot find source for C method: #{meth_name}"
next
end
code = info.source
meth_type = :c
else
code = meth.source
end
file, line = meth.source_location file, line = meth.source_location
check_for_dynamically_defined_method.call(file) check_for_dynamically_defined_method.call(file)
output.puts "--\nFrom #{file} @ line #{line}:\n--" output.puts "--\nFrom #{file} @ line #{line}:\n--"
if Pry.color if Pry.color
code = CodeRay.scan(code, :ruby).term code = CodeRay.scan(code, meth_type).term
end end
output.puts code output.puts code

View File

@ -1,40 +0,0 @@
range.c f56591088e2ec8b25ee20ba71e5d4f7bd6cebd80
lex.c 2fb09da4fd0e597e908a758b2cb8861791d77fe0
st.c de730738519f9d314642d643605daf0032cde5e8
sprintf.c 38e24ae2792f3f0c788c12930f24df8967386584
struct.c aeeb1c59b8feced0cb0ab5c8f2bb0f7db949294c
util.c bf0f10904a55eb132e1dbff76abbfaa5c0b9645a
re.c 0c7fb7c27754745b224b5d68e6f9360a2c27c392
version.c 09c81d814237aa4c9d91b315336bf2998506b84b
dmydln.c deae68321fe218cbece2c778eeee2cb1ed07f9dc
object.c 63320c4caf35debfcea26701f38e552f0783a591
random.c 49191bb7edda1923b7964c87356c65d7ce539929
class.c 254e814ebb75cf1104927821200b014557755c27
enum.c b25337156929ead15069399fb3f877d77dd84d0f
compar.c 3cc33a5da41a461486f6c94d1a13c3d6f6af2491
array.c 306a01939123a5e84b4967a70a857a64dfe55dd9
numeric.c c3ab075b4360c127ae1d6f0a4c77195ca63ffaf5
inits.c 9ca3e88702cfbaff13952b422a357c5b5d0a2c9d
enumerator.c 8ebf946bb338cb5337ea05c2a0abff241243ad58
main.c ca4ccfe720cc9b09e6bdc4adda8822cd28e97ef4
pack.c 46f347e026df35603757f1ee88f9e5600089107a
process.c 1f405125e81953e5f5ccfcadc0fbf23653849563
string.c 7e436a583b230a560609f136eaa9a90eb8393323
dmyext.c 0a5f623323d12032746f4b954fd362cce8139d4e
regex.c 5ce25e880652ba773da3dcf4fd3773ad93536c5d
time.c 52012e930b3683b70745c9ac46e765449fc59e02
dir.c e81a961fc1ee266aa151270f54024e1e7a2bb5d4
signal.c d82b5d5225baaa152d513090715303cf3a471efd
error.c 027ab87587753aac879a87d14aed68ac469609ae
hash.c 90a48add85816a628c4b7e84f34f181d3772e781
dln.c 488c3f8d5989c8c0640dd0fb4dc14dbe29dfd292
io.c 2fc691c76efad88f77da987e8720f54477fba70e
variable.c feba3d4dbfb850e55e9c85d548be0eef01732b05
file.c 5f8f03c9d63600dba17d07d5a7ed8ddf464e2049
ruby.c d7be35d486d8b283761260ddc340d831fd6fb302
prec.c 405d264141d7ee55ff51dbc17d4838d65066ce83
bignum.c 34dbe3ba01a899937f92a441cced0e22a6036aa8
math.c 958af177feac771f4f58efd124e5ea7b18da11c7
marshal.c 0be9f8ddc3f3e8f7d6369c0ee6b557a9c354a701
eval.c 58f7f1e768c09122e491cc122468a3cf5aa95669
gc.c 7e688ca3d4dd056559de10ba5d38f18759889f2d

View File

@ -1,899 +0,0 @@
o:$YARD::CodeObjects::MethodObject:
@name: pack:@docstringIC:YARD::Docstring"Packs the contents of <i>arr</i> into a binary sequence according to
the directives in <i>aTemplateString</i> (see the table below)
Directives ``A,'' ``a,'' and ``Z'' may be followed by a count,
which gives the width of the resulting field. The remaining
directives also may take a count, indicating the number of array
elements to convert. If the count is an asterisk
(``<code>*</code>''), all remaining array elements will be
converted. Any of the directives ``<code>sSiIlL</code>'' may be
followed by an underscore (``<code>_</code>'') to use the underlying
platform's native size for the specified type; otherwise, they use a
platform-independent size. Spaces are ignored in the template
string. See also <code>String#unpack</code>.
a = [ "a", "b", "c" ]
n = [ 65, 66, 67 ]
a.pack("A3A3A3") #=> "a b c "
a.pack("a3a3a3") #=> "a\000\000b\000\000c\000\000"
n.pack("ccc") #=> "ABC"
Directives for +pack+.
Integer | Array |
Directive | Element | Meaning
------------------------------------------------------------------------
C | Integer | 8-bit unsigned integer (unsigned char)
S | Integer | 16-bit unsigned integer, native endian (uint16_t)
L | Integer | 32-bit unsigned integer, native endian (uint32_t)
Q | Integer | 64-bit unsigned integer, native endian (uint64_t)
| |
c | Integer | 8-bit signed integer (char)
s | Integer | 16-bit signed integer, native endian (int16_t)
l | Integer | 32-bit signed integer, native endian (int32_t)
q | Integer | 64-bit signed integer, native endian (int64_t)
| |
S_ | Integer | unsigned short, native endian
I, I_ | Integer | unsigned int, native endian
L_ | Integer | unsigned long, native endian
| |
s_ | Integer | signed short, native endian
i, i_ | Integer | signed int, native endian
l_ | Integer | signed long, native endian
| |
n | Integer | 16-bit unsigned integer, network (big-endian) byte order
N | Integer | 32-bit unsigned integer, network (big-endian) byte order
v | Integer | 16-bit unsigned integer, VAX (little-endian) byte order
V | Integer | 32-bit unsigned integer, VAX (little-endian) byte order
| |
U | Integer | UTF-8 character
w | Integer | BER-compressed integer
Float | |
Directive | | Meaning
------------------------------------------------------------------------
D, d | Float | double-precision float, native format
F, f | Float | single-precision float, native format
E | Float | double-precision float, little-endian byte order
e | Float | single-precision float, little-endian byte order
G | Float | double-precision float, network (big-endian) byte order
g | Float | single-precision float, network (big-endian) byte order
String | |
Directive | | Meaning
------------------------------------------------------------------------
A | String | arbitrary binary string (space padded, count is width)
a | String | arbitrary binary string (null padded, count is width)
Z | String | same as ``a'', except that null is added with *
B | String | bit string (MSB first)
b | String | bit string (LSB first)
H | String | hex string (high nibble first)
h | String | hex string (low nibble first)
u | String | UU-encoded string
M | String | quoted printable, MIME encoding (see RFC2045)
m | String | base64 encoded string (see RFC 2045, count is width)
P | String | pointer to a structure (fixed-length string)
p | String | pointer to a null-terminated string
Misc. | |
Directive | | Meaning
------------------------------------------------------------------------
@ | --- | moves to absolute position
X | --- | back up a byte
x | --- | null byte
: @objectu:YARD::StubProxyArray#pack: @summary0: @all"
Packs the contents of <i>arr</i> into a binary sequence according to
the directives in <i>aTemplateString</i> (see the table below)
Directives ``A,'' ``a,'' and ``Z'' may be followed by a count,
which gives the width of the resulting field. The remaining
directives also may take a count, indicating the number of array
elements to convert. If the count is an asterisk
(``<code>*</code>''), all remaining array elements will be
converted. Any of the directives ``<code>sSiIlL</code>'' may be
followed by an underscore (``<code>_</code>'') to use the underlying
platform's native size for the specified type; otherwise, they use a
platform-independent size. Spaces are ignored in the template
string. See also <code>String#unpack</code>.
a = [ "a", "b", "c" ]
n = [ 65, 66, 67 ]
a.pack("A3A3A3") #=> "a b c "
a.pack("a3a3a3") #=> "a\000\000b\000\000c\000\000"
n.pack("ccc") #=> "ABC"
Directives for +pack+.
Integer | Array |
Directive | Element | Meaning
------------------------------------------------------------------------
C | Integer | 8-bit unsigned integer (unsigned char)
S | Integer | 16-bit unsigned integer, native endian (uint16_t)
L | Integer | 32-bit unsigned integer, native endian (uint32_t)
Q | Integer | 64-bit unsigned integer, native endian (uint64_t)
| |
c | Integer | 8-bit signed integer (char)
s | Integer | 16-bit signed integer, native endian (int16_t)
l | Integer | 32-bit signed integer, native endian (int32_t)
q | Integer | 64-bit signed integer, native endian (int64_t)
| |
S_ | Integer | unsigned short, native endian
I, I_ | Integer | unsigned int, native endian
L_ | Integer | unsigned long, native endian
| |
s_ | Integer | signed short, native endian
i, i_ | Integer | signed int, native endian
l_ | Integer | signed long, native endian
| |
n | Integer | 16-bit unsigned integer, network (big-endian) byte order
N | Integer | 32-bit unsigned integer, network (big-endian) byte order
v | Integer | 16-bit unsigned integer, VAX (little-endian) byte order
V | Integer | 32-bit unsigned integer, VAX (little-endian) byte order
| |
U | Integer | UTF-8 character
w | Integer | BER-compressed integer
Float | |
Directive | | Meaning
------------------------------------------------------------------------
D, d | Float | double-precision float, native format
F, f | Float | single-precision float, native format
E | Float | double-precision float, little-endian byte order
e | Float | single-precision float, little-endian byte order
G | Float | double-precision float, network (big-endian) byte order
g | Float | single-precision float, network (big-endian) byte order
String | |
Directive | | Meaning
------------------------------------------------------------------------
A | String | arbitrary binary string (space padded, count is width)
a | String | arbitrary binary string (null padded, count is width)
Z | String | same as ``a'', except that null is added with *
B | String | bit string (MSB first)
b | String | bit string (LSB first)
H | String | hex string (high nibble first)
h | String | hex string (low nibble first)
u | String | UU-encoded string
M | String | quoted printable, MIME encoding (see RFC2045)
m | String | base64 encoded string (see RFC 2045, count is width)
P | String | pointer to a structure (fixed-length string)
p | String | pointer to a null-terminated string
Misc. | |
Directive | | Meaning
------------------------------------------------------------------------
@ | --- | moves to absolute position
X | --- | back up a byte
x | --- | null byte
:@ref_tags[:
@tags[:@current_file_has_commentsF: @scope: instance:@parameters[:@docstring_extra0: @files[[" pack.c0:@namespaceu;
Array:
@path"Array#pack;[:@visibility: public: @source"OX/*
* call-seq:
* arr.pack ( aTemplateString ) -> aBinaryString
*
* Packs the contents of <i>arr</i> into a binary sequence according to
* the directives in <i>aTemplateString</i> (see the table below)
* Directives ``A,'' ``a,'' and ``Z'' may be followed by a count,
* which gives the width of the resulting field. The remaining
* directives also may take a count, indicating the number of array
* elements to convert. If the count is an asterisk
* (``<code>*</code>''), all remaining array elements will be
* converted. Any of the directives ``<code>sSiIlL</code>'' may be
* followed by an underscore (``<code>_</code>'') to use the underlying
* platform's native size for the specified type; otherwise, they use a
* platform-independent size. Spaces are ignored in the template
* string. See also <code>String#unpack</code>.
*
* a = [ "a", "b", "c" ]
* n = [ 65, 66, 67 ]
* a.pack("A3A3A3") #=> "a b c "
* a.pack("a3a3a3") #=> "a\000\000b\000\000c\000\000"
* n.pack("ccc") #=> "ABC"
*
* Directives for +pack+.
*
* Integer | Array |
* Directive | Element | Meaning
* ------------------------------------------------------------------------
* C | Integer | 8-bit unsigned integer (unsigned char)
* S | Integer | 16-bit unsigned integer, native endian (uint16_t)
* L | Integer | 32-bit unsigned integer, native endian (uint32_t)
* Q | Integer | 64-bit unsigned integer, native endian (uint64_t)
* | |
* c | Integer | 8-bit signed integer (char)
* s | Integer | 16-bit signed integer, native endian (int16_t)
* l | Integer | 32-bit signed integer, native endian (int32_t)
* q | Integer | 64-bit signed integer, native endian (int64_t)
* | |
* S_ | Integer | unsigned short, native endian
* I, I_ | Integer | unsigned int, native endian
* L_ | Integer | unsigned long, native endian
* | |
* s_ | Integer | signed short, native endian
* i, i_ | Integer | signed int, native endian
* l_ | Integer | signed long, native endian
* | |
* n | Integer | 16-bit unsigned integer, network (big-endian) byte order
* N | Integer | 32-bit unsigned integer, network (big-endian) byte order
* v | Integer | 16-bit unsigned integer, VAX (little-endian) byte order
* V | Integer | 32-bit unsigned integer, VAX (little-endian) byte order
* | |
* U | Integer | UTF-8 character
* w | Integer | BER-compressed integer
*
* Float | |
* Directive | | Meaning
* ------------------------------------------------------------------------
* D, d | Float | double-precision float, native format
* F, f | Float | single-precision float, native format
* E | Float | double-precision float, little-endian byte order
* e | Float | single-precision float, little-endian byte order
* G | Float | double-precision float, network (big-endian) byte order
* g | Float | single-precision float, network (big-endian) byte order
*
* String | |
* Directive | | Meaning
* ------------------------------------------------------------------------
* A | String | arbitrary binary string (space padded, count is width)
* a | String | arbitrary binary string (null padded, count is width)
* Z | String | same as ``a'', except that null is added with *
* B | String | bit string (MSB first)
* b | String | bit string (LSB first)
* H | String | hex string (high nibble first)
* h | String | hex string (low nibble first)
* u | String | UU-encoded string
* M | String | quoted printable, MIME encoding (see RFC2045)
* m | String | base64 encoded string (see RFC 2045, count is width)
* P | String | pointer to a structure (fixed-length string)
* p | String | pointer to a null-terminated string
*
* Misc. | |
* Directive | | Meaning
* ------------------------------------------------------------------------
* @ | --- | moves to absolute position
* X | --- | back up a byte
* x | --- | null byte
*/
static VALUE
pack_pack(ary, fmt)
VALUE ary, fmt;
{
static const char nul10[] = "\0\0\0\0\0\0\0\0\0\0";
static const char spc10[] = " ";
char *p, *pend;
VALUE res, from, associates = 0;
char type;
long items, len, idx, plen;
const char *ptr;
#ifdef NATINT_PACK
int natint; /* native integer */
#endif
int signed_p, integer_size, bigendian_p;
StringValue(fmt);
p = RSTRING(fmt)->ptr;
pend = p + RSTRING(fmt)->len;
res = rb_str_buf_new(0);
items = RARRAY(ary)->len;
idx = 0;
#define TOO_FEW (rb_raise(rb_eArgError, toofew), 0)
#define THISFROM (items > 0 ? RARRAY(ary)->ptr[idx] : TOO_FEW)
#define NEXTFROM (items-- > 0 ? RARRAY(ary)->ptr[idx++] : TOO_FEW)
while (p < pend) {
if (RSTRING(fmt)->ptr + RSTRING(fmt)->len != pend) {
rb_raise(rb_eRuntimeError, "format string modified");
}
type = *p++; /* get data type */
#ifdef NATINT_PACK
natint = 0;
#endif
if (ISSPACE(type)) continue;
if (type == '#') {
while ((p < pend) && (*p != '\n')) {
p++;
}
continue;
}
if (*p == '_' || *p == '!') {
const char *natstr = "sSiIlL";
if (strchr(natstr, type)) {
#ifdef NATINT_PACK
natint = 1;
#endif
p++;
}
else {
rb_raise(rb_eArgError, "'%c' allowed only after types %s", *p, natstr);
}
}
if (*p == '*') { /* set data length */
len = strchr("@Xxu", type) ? 0
: strchr("PMm", type) ? 1
: items;
p++;
}
else if (ISDIGIT(*p)) {
len = strtoul(p, (char**)&p, 10);
}
else {
len = 1;
}
switch (type) {
case 'A': case 'a': case 'Z':
case 'B': case 'b':
case 'H': case 'h':
from = NEXTFROM;
if (NIL_P(from)) {
ptr = "";
plen = 0;
}
else {
StringValue(from);
ptr = RSTRING(from)->ptr;
plen = RSTRING(from)->len;
OBJ_INFECT(res, from);
}
if (p[-1] == '*')
len = plen;
switch (type) {
case 'a': /* arbitrary binary string (null padded) */
case 'A': /* ASCII string (space padded) */
case 'Z': /* null terminated ASCII string */
if (plen >= len) {
rb_str_buf_cat(res, ptr, len);
if (p[-1] == '*' && type == 'Z')
rb_str_buf_cat(res, nul10, 1);
}
else {
rb_str_buf_cat(res, ptr, plen);
len -= plen;
while (len >= 10) {
rb_str_buf_cat(res, (type == 'A')?spc10:nul10, 10);
len -= 10;
}
rb_str_buf_cat(res, (type == 'A')?spc10:nul10, len);
}
break;
case 'b': /* bit string (ascending) */
{
int byte = 0;
long i, j = 0;
if (len > plen) {
j = (len - plen + 1)/2;
len = plen;
}
for (i=0; i++ < len; ptr++) {
if (*ptr & 1)
byte |= 128;
if (i & 7)
byte >>= 1;
else {
char c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
byte = 0;
}
}
if (len & 7) {
char c;
byte >>= 7 - (len & 7);
c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
}
len = j;
goto grow;
}
break;
case 'B': /* bit string (descending) */
{
int byte = 0;
long i, j = 0;
if (len > plen) {
j = (len - plen + 1)/2;
len = plen;
}
for (i=0; i++ < len; ptr++) {
byte |= *ptr & 1;
if (i & 7)
byte <<= 1;
else {
char c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
byte = 0;
}
}
if (len & 7) {
char c;
byte <<= 7 - (len & 7);
c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
}
len = j;
goto grow;
}
break;
case 'h': /* hex string (low nibble first) */
{
int byte = 0;
long i, j = 0;
if (len > plen) {
j = (len + 1) / 2 - (plen + 1) / 2;
len = plen;
}
for (i=0; i++ < len; ptr++) {
if (ISALPHA(*ptr))
byte |= (((*ptr & 15) + 9) & 15) << 4;
else
byte |= (*ptr & 15) << 4;
if (i & 1)
byte >>= 4;
else {
char c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
byte = 0;
}
}
if (len & 1) {
char c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
}
len = j;
goto grow;
}
break;
case 'H': /* hex string (high nibble first) */
{
int byte = 0;
long i, j = 0;
if (len > plen) {
j = (len + 1) / 2 - (plen + 1) / 2;
len = plen;
}
for (i=0; i++ < len; ptr++) {
if (ISALPHA(*ptr))
byte |= ((*ptr & 15) + 9) & 15;
else
byte |= *ptr & 15;
if (i & 1)
byte <<= 4;
else {
char c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
byte = 0;
}
}
if (len & 1) {
char c = byte & 0xff;
rb_str_buf_cat(res, &c, 1);
}
len = j;
goto grow;
}
break;
}
break;
case 'c': /* signed char */
case 'C': /* unsigned char */
while (len-- > 0) {
char c;
from = NEXTFROM;
c = num2i32(from);
rb_str_buf_cat(res, &c, sizeof(char));
}
break;
case 's': /* signed short */
signed_p = 1;
integer_size = NATINT_LEN(short, 2);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'S': /* unsigned short */
signed_p = 0;
integer_size = NATINT_LEN(short, 2);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'i': /* signed int */
signed_p = 1;
integer_size = (int)sizeof(int);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'I': /* unsigned int */
signed_p = 0;
integer_size = (int)sizeof(int);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'l': /* signed long */
signed_p = 1;
integer_size = NATINT_LEN(long, 4);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'L': /* unsigned long */
signed_p = 0;
integer_size = NATINT_LEN(long, 4);
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'q': /* signed quad (64bit) int */
signed_p = 1;
integer_size = 8;
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'Q': /* unsigned quad (64bit) int */
signed_p = 0;
integer_size = 8;
bigendian_p = BIGENDIAN_P();
goto pack_integer;
case 'n': /* unsigned short (network byte-order) */
signed_p = 0;
integer_size = 2;
bigendian_p = 1;
goto pack_integer;
case 'N': /* unsigned long (network byte-order) */
signed_p = 0;
integer_size = 4;
bigendian_p = 1;
goto pack_integer;
case 'v': /* unsigned short (VAX byte-order) */
signed_p = 0;
integer_size = 2;
bigendian_p = 0;
goto pack_integer;
case 'V': /* unsigned long (VAX byte-order) */
signed_p = 0;
integer_size = 4;
bigendian_p = 0;
goto pack_integer;
pack_integer:
switch (integer_size) {
#if defined(HAVE_INT16_T) && !defined(FORCE_BIG_PACK)
case SIZEOF_INT16_T:
while (len-- > 0) {
union {
int16_t i;
char a[sizeof(int16_t)];
} v;
from = NEXTFROM;
v.i = (int16_t)num2i32(from);
if (bigendian_p != BIGENDIAN_P()) v.i = swap16(v.i);
rb_str_buf_cat(res, v.a, sizeof(int16_t));
}
break;
#endif
#if defined(HAVE_INT32_T) && !defined(FORCE_BIG_PACK)
case SIZEOF_INT32_T:
while (len-- > 0) {
union {
int32_t i;
char a[sizeof(int32_t)];
} v;
from = NEXTFROM;
v.i = (int32_t)num2i32(from);
if (bigendian_p != BIGENDIAN_P()) v.i = swap32(v.i);
rb_str_buf_cat(res, v.a, sizeof(int32_t));
}
break;
#endif
#if defined(HAVE_INT64_T) && SIZEOF_LONG == SIZEOF_INT64_T && !defined(FORCE_BIG_PACK)
case SIZEOF_INT64_T:
while (len-- > 0) {
union {
int64_t i;
char a[sizeof(int64_t)];
} v;
from = NEXTFROM;
v.i = num2i32(from); /* can return 64bit value if SIZEOF_LONG == SIZEOF_INT64_T */
if (bigendian_p != BIGENDIAN_P()) v.i = swap64(v.i);
rb_str_buf_cat(res, v.a, sizeof(int64_t));
}
break;
#endif
default:
if (integer_size > MAX_INTEGER_PACK_SIZE)
rb_bug("unexpected intger size for pack: %d", integer_size);
while (len-- > 0) {
union {
unsigned long i[(MAX_INTEGER_PACK_SIZE+SIZEOF_LONG-1)/SIZEOF_LONG];
char a[(MAX_INTEGER_PACK_SIZE+SIZEOF_LONG-1)/SIZEOF_LONG*SIZEOF_LONG];
} v;
int num_longs = (integer_size+SIZEOF_LONG-1)/SIZEOF_LONG;
int i;
from = NEXTFROM;
from = rb_to_int(from);
if (integer_size == QUAD_SIZE)
rb_quad_pack(v.a, from); /* RangeError compatibility for Ruby 1.8. */
rb_big_pack(from, v.i, num_longs);
if (bigendian_p) {
for (i = 0; i < num_longs/2; i++) {
unsigned long t = v.i[i];
v.i[i] = v.i[num_longs-1-i];
v.i[num_longs-1-i] = t;
}
}
if (bigendian_p != BIGENDIAN_P()) {
for (i = 0; i < num_longs; i++)
v.i[i] = swapl(v.i[i]);
}
rb_str_buf_cat(res,
bigendian_p ?
v.a + sizeof(long)*num_longs - integer_size :
v.a,
integer_size);
}
break;
}
break;
case 'f': /* single precision float in native format */
case 'F': /* ditto */
while (len-- > 0) {
float f;
from = NEXTFROM;
f = RFLOAT(rb_Float(from))->value;
rb_str_buf_cat(res, (char*)&f, sizeof(float));
}
break;
case 'e': /* single precision float in VAX byte-order */
while (len-- > 0) {
float f;
FLOAT_CONVWITH(ftmp);
from = NEXTFROM;
f = RFLOAT(rb_Float(from))->value;
f = HTOVF(f,ftmp);
rb_str_buf_cat(res, (char*)&f, sizeof(float));
}
break;
case 'E': /* double precision float in VAX byte-order */
while (len-- > 0) {
double d;
DOUBLE_CONVWITH(dtmp);
from = NEXTFROM;
d = RFLOAT(rb_Float(from))->value;
d = HTOVD(d,dtmp);
rb_str_buf_cat(res, (char*)&d, sizeof(double));
}
break;
case 'd': /* double precision float in native format */
case 'D': /* ditto */
while (len-- > 0) {
double d;
from = NEXTFROM;
d = RFLOAT(rb_Float(from))->value;
rb_str_buf_cat(res, (char*)&d, sizeof(double));
}
break;
case 'g': /* single precision float in network byte-order */
while (len-- > 0) {
float f;
FLOAT_CONVWITH(ftmp);
from = NEXTFROM;
f = RFLOAT(rb_Float(from))->value;
f = HTONF(f,ftmp);
rb_str_buf_cat(res, (char*)&f, sizeof(float));
}
break;
case 'G': /* double precision float in network byte-order */
while (len-- > 0) {
double d;
DOUBLE_CONVWITH(dtmp);
from = NEXTFROM;
d = RFLOAT(rb_Float(from))->value;
d = HTOND(d,dtmp);
rb_str_buf_cat(res, (char*)&d, sizeof(double));
}
break;
case 'x': /* null byte */
grow:
while (len >= 10) {
rb_str_buf_cat(res, nul10, 10);
len -= 10;
}
rb_str_buf_cat(res, nul10, len);
break;
case 'X': /* back up byte */
shrink:
plen = RSTRING(res)->len;
if (plen < len)
rb_raise(rb_eArgError, "X outside of string");
RSTRING(res)->len = plen - len;
RSTRING(res)->ptr[plen - len] = '\0';
break;
case '@': /* null fill to absolute position */
len -= RSTRING(res)->len;
if (len > 0) goto grow;
len = -len;
if (len > 0) goto shrink;
break;
case '%':
rb_raise(rb_eArgError, "%% is not supported");
break;
case 'U': /* Unicode character */
while (len-- > 0) {
long l;
char buf[8];
int le;
from = NEXTFROM;
from = rb_to_int(from);
l = NUM2INT(from);
if (l < 0) {
rb_raise(rb_eRangeError, "pack(U): value out of range");
}
le = uv_to_utf8(buf, l);
rb_str_buf_cat(res, (char*)buf, le);
}
break;
case 'u': /* uuencoded string */
case 'm': /* base64 encoded string */
from = NEXTFROM;
StringValue(from);
ptr = RSTRING(from)->ptr;
plen = RSTRING(from)->len;
if (len <= 2)
len = 45;
else
len = len / 3 * 3;
while (plen > 0) {
long todo;
if (plen > len)
todo = len;
else
todo = plen;
encodes(res, ptr, todo, type);
plen -= todo;
ptr += todo;
}
break;
case 'M': /* quoted-printable encoded string */
from = rb_obj_as_string(NEXTFROM);
if (len <= 1)
len = 72;
qpencode(res, from, len);
break;
case 'P': /* pointer to packed byte string */
from = THISFROM;
if (!NIL_P(from)) {
StringValue(from);
if (RSTRING(from)->len < len) {
rb_raise(rb_eArgError, "too short buffer for P(%ld for %ld)",
RSTRING(from)->len, len);
}
}
len = 1;
/* FALL THROUGH */
case 'p': /* pointer to string */
while (len-- > 0) {
char *t;
from = NEXTFROM;
if (NIL_P(from)) {
t = 0;
}
else {
t = StringValuePtr(from);
}
if (!associates) {
associates = rb_ary_new();
}
rb_ary_push(associates, from);
rb_obj_taint(from);
rb_str_buf_cat(res, (char*)&t, sizeof(char*));
}
break;
case 'w': /* BER compressed integer */
while (len-- > 0) {
unsigned long ul;
VALUE buf = rb_str_new(0, 0);
char c, *bufs, *bufe;
from = NEXTFROM;
if (TYPE(from) == T_BIGNUM) {
VALUE big128 = rb_uint2big(128);
while (TYPE(from) == T_BIGNUM) {
from = rb_big_divmod(from, big128);
c = NUM2INT(RARRAY(from)->ptr[1]) | 0x80; /* mod */
rb_str_buf_cat(buf, &c, sizeof(char));
from = RARRAY(from)->ptr[0]; /* div */
}
}
{
long l = NUM2LONG(from);
if (l < 0) {
rb_raise(rb_eArgError, "can't compress negative numbers");
}
ul = l;
}
while (ul) {
c = ((ul & 0x7f) | 0x80);
rb_str_buf_cat(buf, &c, sizeof(char));
ul >>= 7;
}
if (RSTRING(buf)->len) {
bufs = RSTRING(buf)->ptr;
bufe = bufs + RSTRING(buf)->len - 1;
*bufs &= 0x7f; /* clear continue bit */
while (bufs < bufe) { /* reverse */
c = *bufs;
*bufs++ = *bufe;
*bufe-- = c;
}
rb_str_buf_cat(res, RSTRING(buf)->ptr, RSTRING(buf)->len);
}
else {
c = 0;
rb_str_buf_cat(res, &c, sizeof(char));
}
}
break;
default:
break;
}
}
if (associates) {
rb_str_associate(res, associates);
}
OBJ_INFECT(res, fmt);
return res;
}:@source_type:c

Some files were not shown because too many files have changed in this diff Show More