summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--MANIFEST2
-rwxr-xr-xbin/ri385
-rw-r--r--eval.c13
-rw-r--r--lib/rdoc/code_objects.rb6
-rw-r--r--lib/rdoc/generators/ri_generator.rb4
-rw-r--r--lib/rdoc/parsers/parse_c.rb9
-rw-r--r--lib/rdoc/parsers/parse_f95.rb3
-rw-r--r--lib/rdoc/parsers/parse_rb.rb7
-rw-r--r--lib/rdoc/parsers/parse_simple.rb2
-rw-r--r--lib/rdoc/parsers/parserfactory.rb5
-rw-r--r--lib/rdoc/rdoc.rb73
-rw-r--r--lib/rdoc/ri/ri_descriptions.rb14
-rw-r--r--lib/rdoc/ri/ri_formatter.rb25
-rw-r--r--lib/rdoc/ri/ri_options.rb35
-rw-r--r--object.c55
-rw-r--r--process.c1007
-rw-r--r--signal.c24
18 files changed, 1427 insertions, 255 deletions
diff --git a/ChangeLog b/ChangeLog
index 541b6eb..034b31a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+Fri Jan 2 14:54:11 2004 Dave Thomas <dave@pragprog.com>
+
+ * bin/ri: Add new --classes option, and arrange for
+ help messages to be paged too.
+
+ * bin/rdoc: Add statistics.
+
+ * process.c: (MG) Added Process documentation
+
+ * lib/rdoc/ri/ri_formatter.rb (RI::AttributeFormatter::wrap):
+ Fix problem with labels not displaying in RI labeled
+ lists using BS and ANSI modes.
+
Wed Dec 31 11:20:34 2003 <dave@pragprog.com>
* lib/rdoc/parsers/parse_c.rb (RDoc::C_Parser::do_methods): Make
diff --git a/MANIFEST b/MANIFEST
index b422e9d..0414631 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,3 +1,4 @@
+.document
COPYING
COPYING.ja
ChangeLog
@@ -109,6 +110,7 @@ ext/Setup.emx
ext/Setup.nt
ext/Setup.x68
ext/extmk.rb
+lib/.document
lib/English.rb
lib/Env.rb
lib/README
diff --git a/bin/ri b/bin/ri
index 0fee8a8..4988500 100755
--- a/bin/ri
+++ b/bin/ri
@@ -20,17 +20,6 @@ require 'rdoc/ri/ri_reader'
require 'rdoc/ri/ri_formatter'
require 'rdoc/ri/ri_options'
-######################################################################
-
-def display_usage
- RI::Options::OptionList.usage(short_form=true)
-# File.open(__FILE__) do |f|
-# f.gets
-# puts $1 while (f.gets =~ /^# ?(.*)/)
-# end
-# exit
-end
-
######################################################################
@@ -54,30 +43,42 @@ class RiDisplay
######################################################################
+
+ def display_usage
+ setup_pager
+ RI::Options::OptionList.usage(short_form=true)
+ page_output
+ end
+
+ ######################################################################
def setup_pager
- require 'tempfile'
+ unless @options.use_stdout
+ require 'tempfile'
- @save_stdout = STDOUT.clone
- STDOUT.reopen(Tempfile.new("ri_"))
+ @save_stdout = STDOUT.clone
+ STDOUT.reopen(Tempfile.new("ri_"))
+ end
end
######################################################################
def page_output
- path = STDOUT.path
- STDOUT.reopen(@save_stdout)
- @save_stdout = nil
- paged = false
- for pager in [ ENV['PAGER'], "less", "more <", 'pager' ].compact.uniq
- if system("#{pager} #{path}")
- paged = true
- break
+ unless @options.use_stdout
+ path = STDOUT.path
+ STDOUT.reopen(@save_stdout)
+ @save_stdout = nil
+ paged = false
+ for pager in [ ENV['PAGER'], "less", "more <", 'pager' ].compact.uniq
+ if system("#{pager} #{path}")
+ paged = true
+ break
+ end
+ end
+ if !paged
+ @options.use_stdout = true
+ puts File.read(path)
end
- end
- if !paged
- @options.use_stdout = true
- puts File.read(path)
end
end
@@ -107,171 +108,209 @@ class RiDisplay
end
end
-######################################################################
-
-def display_method_info(method_entry)
- method = @ri_reader.get_method(method_entry)
- @formatter.draw_line(method.full_name)
- display_params(method)
- @formatter.draw_line
- display_flow(method.comment)
- if method.aliases && !method.aliases.empty?
- @formatter.blankline
- aka = "(also known as "
- aka << method.aliases.map {|a| a.name }.join(", ")
- aka << ")"
- @formatter.wrap(aka)
- end
-end
-
-######################################################################
-
-def display_class_info(class_entry)
- klass = @ri_reader.get_class(class_entry)
- @formatter.draw_line(klass.display_name + ": " + klass.full_name)
- display_flow(klass.comment)
- @formatter.draw_line
-
- unless klass.includes.empty?
- @formatter.blankline
- @formatter.display_heading("Includes:", 2, "")
- incs = []
- klass.includes.each do |inc|
- inc_desc = @ri_reader.find_class_by_name(inc.name)
- if inc_desc
- str = inc.name + "("
- str << inc_desc.instance_methods.map{|m| m.name}.join(", ")
- str << ")"
- incs << str
- else
- incs << inc.name
- end
+ ######################################################################
+
+ def display_method_info(method_entry)
+ method = @ri_reader.get_method(method_entry)
+ @formatter.draw_line(method.full_name)
+ display_params(method)
+ @formatter.draw_line
+ display_flow(method.comment)
+ if method.aliases && !method.aliases.empty?
+ @formatter.blankline
+ aka = "(also known as "
+ aka << method.aliases.map {|a| a.name }.join(", ")
+ aka << ")"
+ @formatter.wrap(aka)
end
- @formatter.wrap(incs.sort.join(', '))
- end
-
- unless klass.constants.empty?
- @formatter.blankline
- @formatter.display_heading("Constants:", 2, "")
- len = 0
- klass.constants.each { |c| len = c.name.length if c.name.length > len }
- len += 2
- klass.constants.each do |c|
- @formatter.wrap(c.value,
- @formatter.indent+((c.name+":").ljust(len)))
- end
- end
-
- unless klass.class_methods.empty?
- @formatter.blankline
- @formatter.display_heading("Class methods:", 2, "")
- @formatter.wrap(klass.class_methods.map{|m| m.name}.sort.join(', '))
end
-
- unless klass.instance_methods.empty?
- @formatter.blankline
- @formatter.display_heading("Instance methods:", 2, "")
- @formatter.wrap(klass.instance_methods.map{|m| m.name}.sort.join(', '))
- end
-
- unless klass.attributes.empty?
- @formatter.blankline
- @formatter.wrap("Attributes:", "")
- @formatter.wrap(klass.attributes.map{|a| a.name}.sort.join(', '))
- end
-end
-
-######################################################################
-
-# If the list of matching methods contains exactly one entry, or
-# if it contains an entry that exactly matches the requested method,
-# then display that entry, otherwise display the list of
-# matching method names
-
-def report_method_stuff(requested_method_name, methods)
- if methods.size == 1
- display_method_info(methods[0])
- else
- entries = methods.find_all {|m| m.name == requested_method_name}
- if entries.size == 1
- display_method_info(entries[0])
+
+ ######################################################################
+
+ def display_class_info(class_entry)
+ klass = @ri_reader.get_class(class_entry)
+ superclass = klass.superclass_string
+
+ if superclass
+ superclass = " < " + superclass
else
- puts "More than one method matched your request. You can refine"
- puts "your search by asking for information on one of:\n\n"
- @formatter.wrap(methods.map {|m| m.full_name} .join(", "))
+ superclass = ""
+ end
+
+ @formatter.draw_line(klass.display_name + ": " +
+ klass.full_name + superclass)
+
+ display_flow(klass.comment)
+ @formatter.draw_line
+
+ unless klass.includes.empty?
+ @formatter.blankline
+ @formatter.display_heading("Includes:", 2, "")
+ incs = []
+ klass.includes.each do |inc|
+ inc_desc = @ri_reader.find_class_by_name(inc.name)
+ if inc_desc
+ str = inc.name + "("
+ str << inc_desc.instance_methods.map{|m| m.name}.join(", ")
+ str << ")"
+ incs << str
+ else
+ incs << inc.name
+ end
+ end
+ @formatter.wrap(incs.sort.join(', '))
+ end
+
+ unless klass.constants.empty?
+ @formatter.blankline
+ @formatter.display_heading("Constants:", 2, "")
+ len = 0
+ klass.constants.each { |c| len = c.name.length if c.name.length > len }
+ len += 2
+ klass.constants.each do |c|
+ @formatter.wrap(c.value,
+ @formatter.indent+((c.name+":").ljust(len)))
+ end
+ end
+
+ unless klass.class_methods.empty?
+ @formatter.blankline
+ @formatter.display_heading("Class methods:", 2, "")
+ @formatter.wrap(klass.class_methods.map{|m| m.name}.sort.join(', '))
+ end
+
+ unless klass.instance_methods.empty?
+ @formatter.blankline
+ @formatter.display_heading("Instance methods:", 2, "")
+ @formatter.wrap(klass.instance_methods.map{|m| m.name}.sort.join(', '))
+ end
+
+ unless klass.attributes.empty?
+ @formatter.blankline
+ @formatter.wrap("Attributes:", "")
+ @formatter.wrap(klass.attributes.map{|a| a.name}.sort.join(', '))
end
end
-end
-
-######################################################################
-
-def report_class_stuff(requested_class_name, namespaces)
- if namespaces.size == 1
- display_class_info(namespaces[0])
- else
- entries = namespaces.find_all {|m| m.full_name == requested_class_name}
- if entries.size == 1
- display_class_info(entries[0])
+
+ ######################################################################
+
+ # If the list of matching methods contains exactly one entry, or
+ # if it contains an entry that exactly matches the requested method,
+ # then display that entry, otherwise display the list of
+ # matching method names
+
+ def report_method_stuff(requested_method_name, methods)
+ if methods.size == 1
+ display_method_info(methods[0])
else
- puts "More than one class or module matched your request. You can refine"
- puts "your search by asking for information on one of:\n\n"
- @formatter.wrap(namespaces.map {|m| m.full_name}.join(", "))
+ entries = methods.find_all {|m| m.name == requested_method_name}
+ if entries.size == 1
+ display_method_info(entries[0])
+ else
+ puts "More than one method matched your request. You can refine"
+ puts "your search by asking for information on one of:\n\n"
+ @formatter.wrap(methods.map {|m| m.full_name} .join(", "))
+ end
end
end
-end
-
-######################################################################
-
-
-def display_info_for(arg)
- desc = NameDescriptor.new(arg)
-
- namespaces = @ri_reader.top_level_namespace
-
- for class_name in desc.class_names
- namespaces = @ri_reader.lookup_namespace_in(class_name, namespaces)
- if namespaces.empty?
- raise RiError.new("Nothing known about #{arg}")
+
+ ######################################################################
+
+ def report_class_stuff(requested_class_name, namespaces)
+ if namespaces.size == 1
+ display_class_info(namespaces[0])
+ else
+ entries = namespaces.find_all {|m| m.full_name == requested_class_name}
+ if entries.size == 1
+ display_class_info(entries[0])
+ else
+ puts "More than one class or module matched your request. You can refine"
+ puts "your search by asking for information on one of:\n\n"
+ @formatter.wrap(namespaces.map {|m| m.full_name}.join(", "))
+ end
end
end
-
- setup_pager unless @options.use_stdout
- begin
- if desc.method_name.nil?
- report_class_stuff(desc.class_names.join('::'), namespaces)
- else
- methods = @ri_reader.find_methods(desc.method_name,
- desc.is_class_method,
- namespaces)
-
- if methods.empty?
+ ######################################################################
+
+
+ def display_info_for(arg)
+ desc = NameDescriptor.new(arg)
+
+ namespaces = @ri_reader.top_level_namespace
+
+ for class_name in desc.class_names
+ namespaces = @ri_reader.lookup_namespace_in(class_name, namespaces)
+ if namespaces.empty?
raise RiError.new("Nothing known about #{arg}")
+ end
+ end
+
+ setup_pager
+
+ begin
+ if desc.method_name.nil?
+ report_class_stuff(desc.class_names.join('::'), namespaces)
else
- report_method_stuff(desc.method_name, methods)
+ methods = @ri_reader.find_methods(desc.method_name,
+ desc.is_class_method,
+ namespaces)
+
+ if methods.empty?
+ raise RiError.new("Nothing known about #{arg}")
+ else
+ report_method_stuff(desc.method_name, methods)
+ end
end
+
+ page_output
+ ensure
+ STDOUT.reopen(@save_stdout) if @save_stdout
end
+
+ end
- page_output unless @options.use_stdout
- ensure
- STDOUT.reopen(@save_stdout) if @save_stdout
+ ######################################################################
+
+ def process_args
+ if @options.list_classes
+ display_class_list
+ else
+ if ARGV.size.zero?
+ display_usage
+ else
+ begin
+ ARGV.each do |arg|
+ display_info_for(arg)
+ end
+ rescue RiError => e
+ $stderr.puts(e.message)
+ exit(1)
+ end
+ end
+ end
end
-
-end
-end
-######################################################################
+ ######################################################################
-if ARGV.size.zero?
- display_usage
-else
- ri = RiDisplay.new
- begin
- ARGV.each do |arg|
- ri.display_info_for(arg)
+ def display_class_list
+ classes = @ri_reader.class_names
+ if classes.empty?
+ puts "Before using ri, you need to generate documentation"
+ puts "using 'rdoc' with the --ri option"
+ else
+ setup_pager
+ @formatter.draw_line("Known classes and modules")
+ @formatter.blankline
+ @formatter.wrap(@ri_reader.class_names.sort.join(", "))
+ page_output
end
- rescue RiError => e
- $stderr.puts(e.message)
- exit(1)
end
-end
+
+end # class RiDisplay
+
+######################################################################
+
+ri = RiDisplay.new
+ri.process_args
+
diff --git a/eval.c b/eval.c
index 1578dc7..6d88111 100644
--- a/eval.c
+++ b/eval.c
@@ -4181,9 +4181,12 @@ rb_exit(status)
exit(status);
}
+
/*
* call-seq:
* exit(integer=0)
+ * Kernel::exit(integer=0)
+ * Process::exit(integer=0)
*
* Initiates the termination of the Ruby script by raising the
* <code>SystemExit</code> exception. This exception may be caught. The
@@ -4203,9 +4206,9 @@ rb_exit(status)
* rescued a SystemExit exception
* after begin block
*
- * Just prior to termination, Ruby executes any <code>at_exit</code>
- * functions and runs any object finalizers (see
- * <code>ObjectSpace</code> beginning on page 434).
+ * Just prior to termination, Ruby executes any <code>at_exit</code> functions
+ * (see Kernel::at_exit) and runs any object finalizers (see
+ * ObjectSpace::define_finalizer).
*
* at_exit { puts "at_exit function" }
* ObjectSpace.define_finalizer("string", proc { puts "in finalizer" })
@@ -4246,10 +4249,12 @@ rb_f_exit(argc, argv)
return Qnil; /* not reached */
}
+
/*
* call-seq:
* abort
- * abort(msg)
+ * Kernel::abort
+ * Process::abort
*
* Terminate execution immediately, effectively by calling
* <code>Kernel.exit(1)</code>. If _msg_ is given, it is written
diff --git a/lib/rdoc/code_objects.rb b/lib/rdoc/code_objects.rb
index f5b4e81..a8c3752 100644
--- a/lib/rdoc/code_objects.rb
+++ b/lib/rdoc/code_objects.rb
@@ -263,6 +263,11 @@ module RDoc
return self if self.name == name
res = @modules[name] || @classes[name]
return res if res
+ find_enclosing_module_named(name)
+ end
+
+ # find a module at a higher scope
+ def find_enclosing_module_named(name)
parent && parent.find_module_named(name)
end
@@ -316,6 +321,7 @@ module RDoc
if result
modules.each do |module_name|
result = result.find_module_named(module_name)
+ break unless result
end
end
end
diff --git a/lib/rdoc/generators/ri_generator.rb b/lib/rdoc/generators/ri_generator.rb
index 705ad7d..8d94579 100644
--- a/lib/rdoc/generators/ri_generator.rb
+++ b/lib/rdoc/generators/ri_generator.rb
@@ -97,10 +97,10 @@ module Generators
def generate_class_info(cls)
if cls === RDoc::NormalModule
+ cls_desc = RI::ModuleDescription.new
+ else
cls_desc = RI::ClassDescription.new
cls_desc.superclass = cls.superclass
- else
- cls_desc = RI::ModuleDescription.new
end
cls_desc.name = cls.name
cls_desc.full_name = cls.full_name
diff --git a/lib/rdoc/parsers/parse_c.rb b/lib/rdoc/parsers/parse_c.rb
index 4aca5ee..904aeb9 100644
--- a/lib/rdoc/parsers/parse_c.rb
+++ b/lib/rdoc/parsers/parse_c.rb
@@ -127,13 +127,14 @@ module RDoc
@@known_bodies = {}
# prepare to parse a C file
- def initialize(top_level, file_name, body, options)
+ def initialize(top_level, file_name, body, options, stats)
@known_classes = KNOWN_CLASSES.dup
@body = body
@options = options
+ @stats = stats
@top_level = top_level
@classes = Hash.new
- @file_dir = File.dirname(file_name)
+ @file_dir = File.dirname(file_name)
end
# Extract the classes/modules and methods from a C file
@@ -173,8 +174,10 @@ module RDoc
if class_mod == "class"
cm = enclosure.add_class(NormalClass, class_name, parent_name)
+ @stats.num_classes += 1
else
cm = enclosure.add_module(NormalModule, class_name)
+ @stats.num_modules += 1
end
cm.record_location(enclosure.toplevel)
@@ -302,6 +305,8 @@ module RDoc
def handle_method(type, var_name, meth_name,
meth_body, param_count, source_file = nil)
+
+ @stats.num_methods += 1
class_name = @known_classes[var_name] || var_name
class_obj = find_class(var_name, class_name)
diff --git a/lib/rdoc/parsers/parse_f95.rb b/lib/rdoc/parsers/parse_f95.rb
index 3adf29d..518e421 100644
--- a/lib/rdoc/parsers/parse_f95.rb
+++ b/lib/rdoc/parsers/parse_f95.rb
@@ -32,8 +32,9 @@ module RDoc
parse_files_matching(/\.(f9(0|5)|F)$/)
# prepare to parse a Fortran 95 file
- def initialize(top_level, file_name, body, options)
+ def initialize(top_level, file_name, body, options, stats)
@body = body
+ @stats = stats
@options = options
@top_level = top_level
@progress = $stderr unless options.quiet
diff --git a/lib/rdoc/parsers/parse_rb.rb b/lib/rdoc/parsers/parse_rb.rb
index 380025e..b8044c1 100644
--- a/lib/rdoc/parsers/parse_rb.rb
+++ b/lib/rdoc/parsers/parse_rb.rb
@@ -1374,8 +1374,9 @@ module RDoc
parse_files_matching(/\.rbw?$/)
- def initialize(top_level, file_name, content, options)
+ def initialize(top_level, file_name, content, options, stats)
@options = options
+ @stats = stats
@size = 0
@token_listeners = nil
@input_file_name = file_name
@@ -1710,6 +1711,8 @@ module RDoc
def parse_class(container, single, tk, comment, &block)
progress("c")
+ @stats.num_classes += 1
+
container, name_t = get_class_or_module(container)
case name_t
@@ -1762,6 +1765,7 @@ module RDoc
def parse_module(container, single, tk, comment)
progress("m")
+ @stats.num_modules += 1
container, name_t = get_class_or_module(container)
# skip_tkspace
name = name_t.name
@@ -1853,6 +1857,7 @@ module RDoc
def parse_method(container, single, tk, comment)
progress(".")
+ @stats.num_methods += 1
line_no = tk.line_no
column = tk.char_no
diff --git a/lib/rdoc/parsers/parse_simple.rb b/lib/rdoc/parsers/parse_simple.rb
index 754f650..b011045 100644
--- a/lib/rdoc/parsers/parse_simple.rb
+++ b/lib/rdoc/parsers/parse_simple.rb
@@ -12,7 +12,7 @@ module RDoc
class SimpleParser
# prepare to parse a plain file
- def initialize(top_level, file_name, body, options)
+ def initialize(top_level, file_name, body, options, stats)
preprocess = SM::PreProcess.new(file_name, options.rdoc_include)
diff --git a/lib/rdoc/parsers/parserfactory.rb b/lib/rdoc/parsers/parserfactory.rb
index dc7d629..b19bd0c 100644
--- a/lib/rdoc/parsers/parserfactory.rb
+++ b/lib/rdoc/parsers/parserfactory.rb
@@ -73,14 +73,15 @@ module RDoc
# Find the correct parser for a particular file name. Return a
# SimpleParser for ones that we don't know
- def ParserFactory.parser_for(top_level, file_name, body, options)
+ def ParserFactory.parser_for(top_level, file_name, body, options, stats)
parser_description = can_parse(file_name)
if parser_description
parser = parser_description.parser
else
parser = SimpleParser
end
- parser.new(top_level, file_name, body, options)
+
+ parser.new(top_level, file_name, body, options, stats)
end
end
end
diff --git a/lib/rdoc/rdoc.rb b/lib/rdoc/rdoc.rb
index ca9c5da..ddcf217 100644
--- a/lib/rdoc/rdoc.rb
+++ b/lib/rdoc/rdoc.rb
@@ -31,6 +31,27 @@ require 'ftools'
module RDoc
+ # Name of the dotfile that contains the description of files to be
+ # processed in the current directory
+ DOT_DOC_FILENAME = ".document"
+
+ # Simple stats collector
+ class Stats
+ attr_accessor :num_files, :num_classes, :num_modules, :num_methods
+ def initialize
+ @num_files = @num_classes = @num_modules = @num_methods = 0
+ @start = Time.now
+ end
+ def print
+ puts "Files: #@num_files"
+ puts "Classes: #@num_classes"
+ puts "Modules: #@num_modules"
+ puts "Methods: #@num_methods"
+ puts "Elapsed: " + sprintf("%0.3fs", Time.now - @start)
+ end
+ end
+
+
# Exception thrown by any rdoc error. Only the #message part is
# of use externally.
@@ -110,25 +131,40 @@ module RDoc
end
+ # The .document file contains a list of file and directory name
+ # patterns, representing candidates for documentation. It may
+ # also contain comments (starting with '#')
+ def parse_dot_doc_file(in_dir, filename, options)
+ # read and strip comments
+ patterns = File.read(filename).gsub(/#.*/, '')
+
+ result = []
+
+ patterns.split.each do |patt|
+ candidates = Dir.glob(File.join(in_dir, patt))
+ result.concat(normalized_file_list(options, candidates))
+ end
+ result
+ end
+
+
# Given a list of files and directories, create a list
# of all the Ruby files they contain.
- def normalized_file_list(options, *relative_files)
+ def normalized_file_list(options, relative_files)
file_list = []
relative_files.each do |rel_file_name|
-
case type = File.stat(rel_file_name).ftype
when "file"
file_list << rel_file_name
when "directory"
next if options.exclude && options.exclude =~ rel_file_name
- Find.find(rel_file_name) do |fn|
- next if options.exclude && options.exclude =~ fn
- next unless ParserFactory.can_parse(fn)
- next unless File.file?(fn)
-
- file_list << fn.sub(%r{\./}, '')
+ dot_doc = File.join(rel_file_name, DOT_DOC_FILENAME)
+ if File.file?(dot_doc)
+ file_list.concat(parse_dot_doc_file(rel_file_name, dot_doc, options))
+ else
+ file_list.concat(list_files_in_directory(rel_file_name, options))
end
else
raise RDocError.new("I can't deal with a #{type} #{rel_file_name}")
@@ -137,6 +173,16 @@ module RDoc
file_list
end
+ # Return a list of the files to be processed in
+ # a directory. We know that this directory doesn't have
+ # a .document file, so we're looking for real files. However
+ # we may well contain subdirectories which must
+ # be tested for .document files
+ def list_files_in_directory(dir, options)
+ normalized_file_list(options, Dir.glob(File.join(dir, "*")))
+ end
+
+
# Parse each file on the command line, recursively entering
# directories
@@ -147,7 +193,7 @@ module RDoc
files = options.files
files = ["."] if files.empty?
- file_list = normalized_file_list(options, *files)
+ file_list = normalized_file_list(options, files)
file_list.each do |fn|
$stderr.printf("\n%35s: ", File.basename(fn)) unless options.quiet
@@ -155,8 +201,9 @@ module RDoc
content = File.open(fn, "r") {|f| f.read}
top_level = TopLevel.new(fn)
- parser = ParserFactory.parser_for(top_level, fn, content, options)
+ parser = ParserFactory.parser_for(top_level, fn, content, options, @stats)
file_info << parser.scan
+ @stats.num_files += 1
end
file_info
@@ -182,6 +229,8 @@ module RDoc
TopLevel::reset
+ @stats = Stats.new
+
options = Options.instance
options.parse(argv, GENERATORS)
@@ -211,7 +260,11 @@ module RDoc
ensure
Dir.chdir(pwd)
end
+ end
+ unless options.quiet
+ puts
+ @stats.print
end
end
end
diff --git a/lib/rdoc/ri/ri_descriptions.rb b/lib/rdoc/ri/ri_descriptions.rb
index 9bd5c2d..96041f1 100644
--- a/lib/rdoc/ri/ri_descriptions.rb
+++ b/lib/rdoc/ri/ri_descriptions.rb
@@ -100,6 +100,12 @@ module RI
"Module"
end
+ # the 'ClassDescription' subclass overrides this
+ # to format up the name of a parent
+ def superclass_string
+ nil
+ end
+
private
def merge(into, from)
@@ -116,6 +122,14 @@ module RI
def display_name
"Class"
end
+
+ def superclass_string
+ if @superclass && @superclass != "Object"
+ @superclass
+ else
+ nil
+ end
+ end
end
diff --git a/lib/rdoc/ri/ri_formatter.rb b/lib/rdoc/ri/ri_formatter.rb
index f41a815..8fd2144 100644
--- a/lib/rdoc/ri/ri_formatter.rb
+++ b/lib/rdoc/ri/ri_formatter.rb
@@ -197,7 +197,6 @@ module RI
end
end
- ######################################################################
def display_flow(flow)
flow.each do |f|
@@ -207,6 +206,7 @@ module RI
end
+ ######################################################################
# Handle text with attributes. We're a base class: there are
# different presentation classes (one, for example, uses overstrikes
# to handle bold and underlinig, while another using ANSI escape
@@ -278,27 +278,30 @@ module RI
return unless txt && !txt.empty?
txt = add_attributes_to(txt)
+ next_prefix = prefix.tr("^ ", " ")
+ linelen -= prefix.size
line = []
until txt.empty?
word = txt.next_word
- if word.size + line.size > linelen - @indent.size
- write_attribute_text(line)
+ if word.size + line.size > linelen
+ write_attribute_text(prefix, line)
+ prefix = next_prefix
line = []
end
line.concat(word)
end
- write_attribute_text(line) if line.length > 0
+ write_attribute_text(prefix, line) if line.length > 0
end
protected
# overridden in specific formatters
- def write_attribute_text(line)
- print @indent
+ def write_attribute_text(prefix, line)
+ print prefix
line.each do |achar|
print achar.char
end
@@ -340,8 +343,8 @@ module RI
BS = "\C-h"
- def write_attribute_text(line)
- print @indent
+ def write_attribute_text(prefix, line)
+ print prefix
line.each do |achar|
attr = achar.attr
if (attr & (ITALIC+CODE)) != 0
@@ -371,15 +374,13 @@ module RI
class AnsiFormatter < AttributeFormatter
- BS = "\C-h"
-
def initialize(*args)
print "\033[0m"
super
end
- def write_attribute_text(line)
- print @indent
+ def write_attribute_text(prefix, line)
+ print prefix
curr_attr = 0
line.each do |achar|
attr = achar.attr
diff --git a/lib/rdoc/ri/ri_options.rb b/lib/rdoc/ri/ri_options.rb
index 9b0704d..fe323ed 100644
--- a/lib/rdoc/ri/ri_options.rb
+++ b/lib/rdoc/ri/ri_options.rb
@@ -16,6 +16,9 @@ module RI
# can't find a pager
attr_accessor :use_stdout
+ # should we just display a class list and exit
+ attr_reader :list_classes
+
# The width of the output line
attr_reader :width
@@ -28,6 +31,10 @@ module RI
[ "--help", "-h", nil,
"you're looking at it" ],
+ [ "--classes", "-c", nil,
+ "Display the names of classes and modules we\n" +
+ "know about"],
+
[ "--format", "-f", "<name>",
"Format to use when displaying output:\n" +
" " + RI::TextFormatter.list + "\n" +
@@ -112,8 +119,8 @@ module RI
EOT
if short_form
- class_list
- puts "For help, type 'ri -h'"
+ puts "For help on options, type 'ri -h'"
+ puts "For a list of classes I know about, type 'ri -c'"
else
puts "Options:\n\n"
OPTION_LIST.each do |long, short, arg, desc|
@@ -136,30 +143,16 @@ module RI
end
end
- def OptionList.class_list
- paths = RI::Paths::PATH
- if paths.empty?
- puts "Before using ri, you need to generate documentation"
- puts "using 'rdoc' with the --ri option"
- else
- @ri_reader = RI::RiReader.new(RI::RiCache.new(paths))
- puts
- puts "Classes and modules I know about:"
- puts
- puts @ri_reader.class_names.sort.join(", ")
- puts
- end
- end
-
end
# Parse command line options.
def parse
- @use_stdout = !STDOUT.tty?
- @width = 72
- @formatter = RI::TextFormatter.for("plain")
+ @use_stdout = !STDOUT.tty?
+ @width = 72
+ @formatter = RI::TextFormatter.for("plain")
+ @list_classes = false
begin
@@ -170,6 +163,8 @@ module RI
case opt
when "--help" then OptionList.usage
when "--no-pager" then @use_stdout = true
+ when "--classes" then @list_classes = true
+
when "--format"
@formatter = RI::TextFormatter.for(arg)
unless @formatter
diff --git a/object.c b/object.c
index 90b7697..26d3ea3 100644
--- a/object.c
+++ b/object.c
@@ -1171,7 +1171,7 @@ sym_to_sym(sym)
/***********************************************************************
*
- * Document-class: Module
+ * Document-class: Module
*
* A <code>Module</code> is a collection of methods and constants. The
* methods in a module may be instance methods or module methods.
@@ -1197,6 +1197,15 @@ sym_to_sym(sym)
*
*/
+/*
+ * call-seq:
+ * mod.to_s => string
+ *
+ * Return a string representing this module or class. For basic
+ * classes and modules, this is the name. For singletons, we
+ * show information on the thing we're attached to as well.
+ */
+
static VALUE
rb_mod_to_s(klass)
VALUE klass;
@@ -2395,30 +2404,28 @@ VALUE ruby_top_self;
*
* Creating a new Name
*
- * Classes, modules, and objects are interrelated. In the diagram
- * that follows, the arrows represent inheritance, and the
- * parentheses meta-classes. All metaclasses are instances
- * of the class `Class'.
- *
- * +------------------+
- * | |
- * Object---->(Object) |
- * ^ ^ ^ ^ |
- * | | | | |
- * | | +-----+ +---------+ |
- * | | | | |
- * | +-----------+ | |
- * | | | | |
- * +------+ | Module--->(Module) |
- * | | ^ ^ |
- * OtherClass-->(OtherClass) | | |
- * | | |
- * Class---->(Class) |
- * ^ |
- * | |
- * +----------------+
- *
+ * Classes, modules, and objects are interrelated. In the diagram
+ * that follows, the arrows represent inheritance, and the
+ * parentheses meta-classes. All metaclasses are instances
+ * of the class `Class'.
*
+ * +------------------+
+ * | |
+ * Object---->(Object) |
+ * ^ ^ ^ ^ |
+ * | | | | |
+ * | | +-----+ +---------+ |
+ * | | | | |
+ * | +-----------+ | |
+ * | | | | |
+ * +------+ | Module--->(Module) |
+ * | | ^ ^ |
+ * OtherClass-->(OtherClass) | | |
+ * | | |
+ * Class---->(Class) |
+ * ^ |
+ * | |
+ * +----------------+
*/
diff --git a/process.c b/process.c
index 4311032..af9523f 100644
--- a/process.c
+++ b/process.c
@@ -105,12 +105,40 @@ static VALUE S_Tms;
#endif
#endif
+
+/*
+ * call-seq:
+ * Process.pid => fixnum
+ *
+ * Returns the process id of this process. Not available on all
+ * platforms.
+ *
+ * Process.pid #=> 27415
+ */
+
static VALUE
get_pid()
{
return INT2FIX(getpid());
}
+
+/*
+ * call-seq:
+ * Process.ppid => fixnum
+ *
+ * Returns the process id of the parent of this process. Always
+ * returns 0 on NT. Not available on all platforms.
+ *
+ * puts "I am #{Process.pid}"
+ * Process.fork { puts "Dad is #{Process.ppid}" }
+ *
+ * <em>produces:</em>
+ *
+ * I am 27417
+ * Dad is 27417
+ */
+
static VALUE
get_ppid()
{
@@ -121,6 +149,37 @@ get_ppid()
#endif
}
+
+/*********************************************************************
+ *
+ * Document-class: Process::Status
+ *
+ * <code>Process::Status</code> encapsulates the information on the
+ * status of a running or terminated system process. The built-in
+ * variable <code>$?</code> is either +nil+ or a
+ * <code>Process::Status</code> object.
+ *
+ * fork { exit 99 } #=> 26557
+ * Process.wait #=> 26557
+ * $?.class #=> Process::Status
+ * $?.to_i #=> 25344
+ * $? >> 8 #=> 99
+ * $?.stopped? #=> false
+ * $?.exited? #=> true
+ * $?.exitstatus #=> 99
+ *
+ * Posix systems record information on processes using a 16-bit
+ * integer. The lower bits record the process status (stopped,
+ * exited, signaled) and the upper bits possibly contain additional
+ * information (for example the program's return code in the case of
+ * exited processes). Pre Ruby 1.8, these bits were exposed directly
+ * to the Ruby program. Ruby now encapsulates these in a
+ * <code>Process::Status</code> object. To maximize compatibility,
+ * however, these objects retain a bit-oriented interface. In the
+ * descriptions that follow, when we talk about the integer value of
+ * _stat_, we're referring to this 16 bit value.
+ */
+
static VALUE rb_cProcStatus;
VALUE rb_last_status = Qnil;
@@ -133,6 +192,20 @@ last_status_set(status, pid)
rb_iv_set(rb_last_status, "pid", INT2FIX(pid));
}
+
+/*
+ * call-seq:
+ * stat.to_i => fixnum
+ * stat.to_int => fixnum
+ *
+ * Returns the bits in _stat_ as a <code>Fixnum</code>. Poking
+ * around in these bits is platform dependent.
+ *
+ * fork { exit 0xab } #=> 26566
+ * Process.wait #=> 26566
+ * sprintf('%04x', $?.to_i) #=> "ab00"
+ */
+
static VALUE
pst_to_i(st)
VALUE st;
@@ -140,6 +213,14 @@ pst_to_i(st)
return rb_iv_get(st, "status");
}
+
+/*
+ * call-seq:
+ * stat.to_s => string
+ *
+ * Equivalent to _stat_<code>.to_i.to_s</code>.
+ */
+
static VALUE
pst_to_s(st)
VALUE st;
@@ -147,6 +228,18 @@ pst_to_s(st)
return rb_fix2str(pst_to_i(st), 10);
}
+
+/*
+ * call-seq:
+ * stat.pid => fixnum
+ *
+ * Returns the process ID that this status object represents.
+ *
+ * fork { exit } #=> 26569
+ * Process.wait #=> 26569
+ * $?.pid #=> 26569
+ */
+
static VALUE
pst_pid(st)
VALUE st;
@@ -154,6 +247,14 @@ pst_pid(st)
return rb_iv_get(st, "pid");
}
+
+/*
+ * call-seq:
+ * stat.inspect => string
+ *
+ * Override the inspection method.
+ */
+
static VALUE
pst_inspect(st)
VALUE st;
@@ -203,6 +304,15 @@ pst_inspect(st)
return str;
}
+
+/*
+ * call-seq:
+ * stat == other => true or false
+ *
+ * Returns +true+ if the integer value of _stat_
+ * equals <em>other</em>.
+ */
+
static VALUE
pst_equal(st1, st2)
VALUE st1, st2;
@@ -211,6 +321,19 @@ pst_equal(st1, st2)
return rb_equal(pst_to_i(st1), st2);
}
+
+/*
+ * call-seq:
+ * stat & num => fixnum
+ *
+ * Logical AND of the bits in _stat_ with <em>num</em>.
+ *
+ * fork { exit 0x37 }
+ * Process.wait
+ * sprintf('%04x', $?.to_i) #=> "3700"
+ * sprintf('%04x', $? & 0x1e00) #=> "1600"
+ */
+
static VALUE
pst_bitand(st1, st2)
VALUE st1, st2;
@@ -220,6 +343,19 @@ pst_bitand(st1, st2)
return INT2NUM(status);
}
+
+/*
+ * call-seq:
+ * stat >> num => fixnum
+ *
+ * Shift the bits in _stat_ right <em>num</em> places.
+ *
+ * fork { exit 99 } #=> 26563
+ * Process.wait #=> 26563
+ * $?.to_i #=> 25344
+ * $? >> 8 #=> 99
+ */
+
static VALUE
pst_rshift(st1, st2)
VALUE st1, st2;
@@ -229,6 +365,16 @@ pst_rshift(st1, st2)
return INT2NUM(status);
}
+
+/*
+ * call-seq:
+ * stat.stopped? => true or false
+ *
+ * Returns +true+ if this process is stopped. This is only
+ * returned if the corresponding <code>wait</code> call had the
+ * <code>WUNTRACED</code> flag set.
+ */
+
static VALUE
pst_wifstopped(st)
VALUE st;
@@ -241,6 +387,15 @@ pst_wifstopped(st)
return Qfalse;
}
+
+/*
+ * call-seq:
+ * stat.stopsig => fixnum or nil
+ *
+ * Returns the number of the signal that caused _stat_ to stop
+ * (or +nil+ if self is not stopped).
+ */
+
static VALUE
pst_wstopsig(st)
VALUE st;
@@ -252,6 +407,15 @@ pst_wstopsig(st)
return Qnil;
}
+
+/*
+ * call-seq:
+ * stat.signaled? => true or false
+ *
+ * Returns +true+ if _stat_ terminated because of
+ * an uncaught signal.
+ */
+
static VALUE
pst_wifsignaled(st)
VALUE st;
@@ -264,6 +428,16 @@ pst_wifsignaled(st)
return Qfalse;
}
+
+/*
+ * call-seq:
+ * stat.termsig => fixnum or nil
+ *
+ * Returns the number of the signal that caused _stat_ to
+ * terminate (or +nil+ if self was not terminated by an
+ * uncaught signal).
+ */
+
static VALUE
pst_wtermsig(st)
VALUE st;
@@ -275,6 +449,16 @@ pst_wtermsig(st)
return Qnil;
}
+
+/*
+ * call-seq:
+ * stat.exited? => true or false
+ *
+ * Returns +true+ if _stat_ exited normally (for
+ * example using an <code>exit()</code> call or finishing the
+ * program).
+ */
+
static VALUE
pst_wifexited(st)
VALUE st;
@@ -287,6 +471,26 @@ pst_wifexited(st)
return Qfalse;
}
+
+/*
+ * call-seq:
+ * stat.exitstatus => fixnum or nil
+ *
+ * Returns the least significant eight bits of the return code of
+ * _stat_. Only available if <code>exited?</code> is
+ * +true+.
+ *
+ * fork { } #=> 26572
+ * Process.wait #=> 26572
+ * $?.exited? #=> true
+ * $?.exitstatus #=> 0
+ *
+ * fork { exit 99 } #=> 26573
+ * Process.wait #=> 26573
+ * $?.exited? #=> true
+ * $?.exitstatus #=> 99
+ */
+
static VALUE
pst_wexitstatus(st)
VALUE st;
@@ -298,6 +502,15 @@ pst_wexitstatus(st)
return Qnil;
}
+
+/*
+ * call-seq:
+ * stat.coredump => true or false
+ *
+ * Returns +true+ if _stat_ generated a coredump
+ * when it terminated. Not available on all platforms.
+ */
+
static VALUE
pst_wcoredump(st)
VALUE st;
@@ -419,6 +632,69 @@ waitall_each(pid, status, ary)
}
#endif
+
+/* [MG]:FIXME: I wasn't sure how this should be done, since ::wait()
+ has historically been documented as if it didn't take any arguments
+ despite the fact that it's just an alias for ::waitpid(). The way I
+ have it below is more truthful, but a little confusing.
+
+ I also took the liberty of putting in the pid values, as they're
+ pretty useful, and it looked as if the original 'ri' output was
+ supposed to contain them after "[...]depending on the value of
+ aPid:".
+
+ The 'ansi' and 'bs' formats of the ri output don't display the
+ definition list for some reason, but the plain text one does.
+ */
+
+/*
+ * call-seq:
+ * Process.wait() => fixnum
+ * Process.wait(pid=-1, flags=0) => fixnum
+ * Process.waitpid(pid=-1, flags=0) => fixnum
+ *
+ * Waits for a child process to exit, returns its process id, and
+ * sets <code>$?</code> to a <code>Process::Status</code> object
+ * containing information on that process. Which child it waits on
+ * depends on the value of _pid_:
+ *
+ * > 0:: Waits for the child whose process ID equals _pid_.
+ *
+ * 0:: Waits for any child whose process group ID equals that of the
+ * calling process.
+ * adsasdasd sads adada dsa a sad ad asd sad sa dsa dasdsad asd asd
+ * adsasdasd sads adada dsa a sad ad asd sad sa dsa dasdsad asd asd
+ * adsasdasd sads adada dsa a sad ad asd sad sa dsa dasdsad asd asd
+ * adsasdasd sads adada dsa a sad ad asd sad sa dsa dasdsad asd asd
+ *
+ * -1:: Waits for any child process (the default if no _pid_ is
+ * given).
+ *
+ * < -1:: Waits for any child whose process group ID equals the absolute
+ * value of _pid_.
+ *
+ * The _flags_ argument may be a logical or of the flag values
+ * <code>Process::WNOHANG</code> (do not block if no child available)
+ * or <code>Process::WUNTRACED</code> (return stopped children that
+ * haven't been reported). Not all flags are available on all
+ * platforms, but a flag value of zero will work on all platforms.
+ *
+ * Calling this method raises a <code>SystemError</code> if there are
+ * no child processes. Not available on all platforms.
+ *
+ * include Process
+ * fork { exit 99 } #=> 27429
+ * wait #=> 27429
+ * $?.exitstatus #=> 99
+ *
+ * pid = fork { sleep 3 } #=> 27440
+ * Time.now #=> Wed Apr 09 08:57:09 CDT 2003
+ * waitpid(pid, Process::WNOHANG) #=> nil
+ * Time.now #=> Wed Apr 09 08:57:09 CDT 2003
+ * waitpid(pid, 0) #=> 27440
+ * Time.now #=> Wed Apr 09 08:57:12 CDT 2003
+ */
+
static VALUE
proc_wait(argc, argv)
int argc;
@@ -446,6 +722,24 @@ proc_wait(argc, argv)
return INT2FIX(pid);
}
+
+/*
+ * call-seq:
+ * Process.wait2(pid=-1, flags=0) => [pid, status]
+ * Process.waitpid2(pid=-1, flags=0) => [pid, status]
+ *
+ * Waits for a child process to exit (see Process::waitpid for exact
+ * semantics) and returns an array containing the process id and the
+ * exit status (a <code>Process::Status</code> object) of that
+ * child. Raises a <code>SystemError</code> if there are no child
+ * processes.
+ *
+ * Process.fork { exit 99 } #=> 27437
+ * pid, status = Process.wait2
+ * pid #=> 27437
+ * status.exitstatus #=> 99
+ */
+
static VALUE
proc_wait2(argc, argv)
int argc;
@@ -456,6 +750,27 @@ proc_wait2(argc, argv)
return rb_assoc_new(pid, rb_last_status);
}
+
+/*
+ * call-seq:
+ * Process.waitall => [ [pid1,status1], ...]
+ *
+ * Waits for all children, returning an array of
+ * _pid_/_status_ pairs (where _status_ is a
+ * <code>Process::Status</code> object).
+ *
+ * fork { sleep 0.2; exit 2 } #=> 27432
+ * fork { sleep 0.1; exit 1 } #=> 27433
+ * fork { exit 0 } #=> 27434
+ * p Process.waitall
+ *
+ * <em>produces</em>:
+ *
+ * [[27434, #<Process::Status: pid=27434,exited(0)>],
+ * [27433, #<Process::Status: pid=27433,exited(1)>],
+ * [27432, #<Process::Status: pid=27432,exited(2)>]]
+ */
+
static VALUE
proc_waitall()
{
@@ -517,6 +832,48 @@ rb_detach_process(pid)
return rb_thread_create(detach_process_watcer, (void*)&pid);
}
+
+/*
+ * call-seq:
+ * Process.detach(pid) => thread
+ *
+ * Some operating systems retain the status of terminated child
+ * processes until the parent collects that status (normally using
+ * some variant of <code>wait()</code>. If the parent never collects
+ * this status, the child stays around as a <em>zombie</em> process.
+ * <code>Process::detach</code> prevents this by setting up a
+ * separate Ruby thread whose sole job is to reap the status of the
+ * process _pid_ when it terminates. Use <code>detach</code>
+ * only when you do not intent to explicitly wait for the child to
+ * terminate. <code>detach</code> only checks the status
+ * periodically (currently once each second).
+ *
+ * In this first example, we don't reap the first child process, so
+ * it appears as a zombie in the process status display.
+ *
+ * p1 = fork { sleep 0.1 }
+ * p2 = fork { sleep 0.2 }
+ * Process.waitpid(p2)
+ * sleep 2
+ * system("ps -ho pid,state -p #{p1}")
+ *
+ * <em>produces:</em>
+ *
+ * 27389 Z
+ *
+ * In the next example, <code>Process::detach</code> is used to reap
+ * the child automatically.
+ *
+ * p1 = fork { sleep 0.1 }
+ * p2 = fork { sleep 0.2 }
+ * Process.detach(p1)
+ * Process.waitpid(p2)
+ * sleep 2
+ * system("ps -ho pid,state -p #{p1}")
+ *
+ * <em>(produces no output)</em>
+ */
+
static VALUE
proc_detach(obj, pid)
VALUE pid;
@@ -798,6 +1155,31 @@ proc_spawn(sv)
#endif
#endif
+/*
+ * call-seq:
+ * exec(command [, arg, ...])
+ *
+ * Replaces the current process by running the given external _command_.
+ * If +exec+ is given a single argument, that argument is
+ * taken as a line that is subject to shell expansion before being
+ * executed. If multiple arguments are given, the second and subsequent
+ * arguments are passed as parameters to _command_ with no shell
+ * expansion. If the first argument is a two-element array, the first
+ * element is the command to be executed, and the second argument is
+ * used as the <code>argv[0]</code> value, which may show up in process
+ * listings. In MSDOS environments, the command is executed in a
+ * subshell; otherwise, one of the <code>exec(2)</code> system calls is
+ * used, so the running command may inherit some of the environment of
+ * the original program (including open file descriptors).
+ *
+ * exec "echo *" # echoes list of files in current directory
+ * # never get here
+ *
+ *
+ * exec "echo", "*" # echoes an asterisk
+ * # never get here
+ */
+
VALUE
rb_f_exec(argc, argv)
int argc;
@@ -832,6 +1214,14 @@ rb_f_exec(argc, argv)
return Qnil; /* dummy */
}
+
+/*
+ * call-seq:
+ * Process.fork [{ block }] => fixnum or nil
+ *
+ * See <code>Kernel::fork</code>.
+ */
+
static VALUE
rb_f_fork(obj)
VALUE obj;
@@ -866,6 +1256,18 @@ rb_f_fork(obj)
#endif
}
+
+/*
+ * call-seq:
+ * Process.exit!(fixnum=-1)
+ *
+ * Exits the process immediately. No exit handlers are
+ * run. <em>fixnum</em> is returned to the underlying system as the
+ * exit status.
+ *
+ * Process.exit!(0)
+ */
+
static VALUE
rb_f_exit_bang(argc, argv, obj)
int argc;
@@ -934,6 +1336,25 @@ rb_syswait(pid)
}
}
+/*
+ * call-seq:
+ * system(cmd [, arg, ...]) => true or false
+ *
+ * Executes _cmd_ in a subshell, returning +true+ if
+ * the command was found and ran successfully, +false+
+ * otherwise. An error status is available in <code>$?</code>. The
+ * arguments are processed in the same way as for
+ * <code>Kernel::exec</code>.
+ *
+ * system("echo *")
+ * system("echo", "*")
+ *
+ * <em>produces:</em>
+ *
+ * config.h main.rb
+ * *
+ */
+
static VALUE
rb_f_system(argc, argv)
int argc;
@@ -1071,6 +1492,24 @@ rb_f_system(argc, argv)
return Qfalse;
}
+/*
+ * call-seq:
+ * sleep(duration=0) => fixnum
+ *
+ * Suspends the current thread for _duraction_ seconds (which may be
+ * any number, including a +Float+ with fractional seconds). Returns the actual
+ * number of seconds slept (rounded), which may be less than that asked
+ * for if the thread was interrupted by a +SIGALRM+, or if
+ * another thread calls <code>Thread#run</code>. An argument of zero
+ * causes +sleep+ to sleep forever.
+ *
+ * Time.new #=> Wed Apr 09 08:56:32 CDT 2003
+ * sleep 1.2 #=> 1
+ * Time.new #=> Wed Apr 09 08:56:33 CDT 2003
+ * sleep 1.9 #=> 2
+ * Time.new #=> Wed Apr 09 08:56:35 CDT 2003
+ */
+
static VALUE
rb_f_sleep(argc, argv)
int argc;
@@ -1094,6 +1533,18 @@ rb_f_sleep(argc, argv)
return INT2FIX(end);
}
+
+/*
+ * call-seq:
+ * Process.getpgrp => integer
+ *
+ * Returns the process group ID for this process. Not available on
+ * all platforms.
+ *
+ * Process.getpgid(0) #=> 25527
+ * Process.getpgrp #=> 25527
+ */
+
static VALUE
proc_getpgrp()
{
@@ -1114,6 +1565,15 @@ proc_getpgrp()
#endif
}
+
+/*
+ * call-seq:
+ * Process.setpgrp => 0
+ *
+ * Equivalent to <code>setpgid(0,0)</code>. Not available on all
+ * platforms.
+ */
+
static VALUE
proc_setpgrp()
{
@@ -1131,6 +1591,17 @@ proc_setpgrp()
return INT2FIX(0);
}
+
+/*
+ * call-seq:
+ * Process.getpgid(pid) => integer
+ *
+ * Returns the process group ID for the given process id. Not
+ * available on all platforms.
+ *
+ * Process.getpgid(Process.ppid()) #=> 25527
+ */
+
static VALUE
proc_getpgid(obj, pid)
VALUE obj, pid;
@@ -1145,6 +1616,15 @@ proc_getpgid(obj, pid)
#endif
}
+
+/*
+ * call-seq:
+ * Process.setpgid(pid, integer) => 0
+ *
+ * Sets the process group ID of _pid_ (0 indicates this
+ * process) to <em>integer</em>. Not available on all platforms.
+ */
+
static VALUE
proc_setpgid(obj, pid, pgrp)
VALUE obj, pid, pgrp;
@@ -1163,6 +1643,18 @@ proc_setpgid(obj, pid, pgrp)
#endif
}
+
+/*
+ * call-seq:
+ * Process.setsid => fixnum
+ *
+ * Establishes this process as a new session and process group
+ * leader, with no controlling tty. Returns the session id. Not
+ * available on all platforms.
+ *
+ * Process.setsid #=> 27422
+ */
+
static VALUE
proc_setsid()
{
@@ -1199,6 +1691,24 @@ proc_setsid()
#endif
}
+
+/*
+ * call-seq:
+ * Process.getpriority(kind, integer) => fixnum
+ *
+ * Gets the scheduling priority for specified process, process group,
+ * or user. <em>kind</em> indicates the kind of entity to find: one
+ * of <code>Process::PRIO_PGRP</code>,
+ * <code>Process::PRIO_USER</code>, or
+ * <code>Process::PRIO_PROCESS</code>. _integer_ is an id
+ * indicating the particular process, process group, or user (an id
+ * of 0 means _current_). Lower priorities are more favorable
+ * for scheduling. Not available on all platforms.
+ *
+ * Process.getpriority(Process::PRIO_USER, 0) #=> 19
+ * Process.getpriority(Process::PRIO_PROCESS, 0) #=> 19
+ */
+
static VALUE
proc_getpriority(obj, which, who)
VALUE obj, which, who;
@@ -1218,6 +1728,19 @@ proc_getpriority(obj, which, who)
#endif
}
+
+/*
+ * call-seq:
+ * Process.setpriority(kind, integer, priority) => 0
+ *
+ * See <code>Process#getpriority</code>.
+ *
+ * Process.setpriority(Process::PRIO_USER, 0, 19) #=> 0
+ * Process.setpriority(Process::PRIO_PROCESS, 0, 19) #=> 0
+ * Process.getpriority(Process::PRIO_USER, 0) #=> 19
+ * Process.getpriority(Process::PRIO_PROCESS, 0) #=> 19
+ */
+
static VALUE
proc_setpriority(obj, which, who, prio)
VALUE obj, which, who, prio;
@@ -1258,6 +1781,27 @@ check_gid_switch()
}
}
+
+/*********************************************************************
+ * Document-class: Process::Sys
+ *
+ * The <code>Process::Sys</code> module contains UID and GID
+ * functions which provide direct bindings to the system calls of the
+ * same names instead of the more-portable versions of the same
+ * functionality found in the <code>Process</code>,
+ * <code>Process::UID</code>, and <code>Process::GID</code> modules.
+ */
+
+
+/*
+ * call-seq:
+ * Process::Sys.setuid(integer) => nil
+ *
+ * Set the user ID of the current process to _integer_. Not
+ * available on all platforms.
+ *
+ */
+
static VALUE
p_sys_setuid(obj, id)
VALUE obj, id;
@@ -1271,6 +1815,17 @@ p_sys_setuid(obj, id)
return Qnil;
}
+
+
+/*
+ * call-seq:
+ * Process::Sys.setruid(integer) => nil
+ *
+ * Set the real user ID of the calling process to _integer_.
+ * Not available on all platforms.
+ *
+ */
+
static VALUE
p_sys_setruid(obj, id)
VALUE obj, id;
@@ -1284,6 +1839,16 @@ p_sys_setruid(obj, id)
return Qnil;
}
+
+/*
+ * call-seq:
+ * Process::Sys.seteuid(integer) => nil
+ *
+ * Set the effective user ID of the calling process to
+ * _integer_. Not available on all platforms.
+ *
+ */
+
static VALUE
p_sys_seteuid(obj, id)
VALUE obj, id;
@@ -1297,6 +1862,18 @@ p_sys_seteuid(obj, id)
return Qnil;
}
+
+/*
+ * call-seq:
+ * Process::Sys.setreuid(rid, eid) => nil
+ *
+ * Sets the (integer) real and/or effective user IDs of the current
+ * process to _rid_ and _eid_, respectively. A value of
+ * <code>-1</code> for either means to leave that ID unchanged. Not
+ * available on all platforms.
+ *
+ */
+
static VALUE
p_sys_setreuid(obj, rid, eid)
VALUE obj, rid, eid;
@@ -1310,6 +1887,18 @@ p_sys_setreuid(obj, rid, eid)
return Qnil;
}
+
+/*
+ * call-seq:
+ * Process::Sys.setresuid(rid, eid, sid) => nil
+ *
+ * Sets the (integer) real, effective, and saved user IDs of the
+ * current process to _rid_, _eid_, and _sid_ respectively. A
+ * value of <code>-1</code> for any value means to
+ * leave that ID unchanged. Not available on all platforms.
+ *
+ */
+
static VALUE
p_sys_setresuid(obj, rid, eid, sid)
VALUE obj, rid, eid, sid;
@@ -1323,6 +1912,18 @@ p_sys_setresuid(obj, rid, eid, sid)
return Qnil;
}
+
+/*
+ * call-seq:
+ * Process.uid => fixnum
+ * Process::UID.rid => fixnum
+ * Process::Sys.getuid => fixnum
+ *
+ * Returns the (real) user ID of this process.
+ *
+ * Process.uid #=> 501
+ */
+
static VALUE
proc_getuid(obj)
VALUE obj;
@@ -1331,6 +1932,15 @@ proc_getuid(obj)
return INT2FIX(uid);
}
+
+/*
+ * call-seq:
+ * Process.uid= integer => numeric
+ *
+ * Sets the (integer) user ID for this process. Not available on all
+ * platforms.
+ */
+
static VALUE
proc_setuid(obj, id)
VALUE obj, id;
@@ -1359,8 +1969,33 @@ proc_setuid(obj, id)
return INT2FIX(uid);
}
+
+/********************************************************************
+ *
+ * Document-class: Process::UID
+ *
+ * The <code>Process::UID</code> module contains a collection of
+ * module functions which can be used to portably get, set, and
+ * switch the current process's real, effective, and saved user IDs.
+ *
+ */
+
static int SAVED_USER_ID;
+
+/*
+ * call-seq:
+ * Process::UID.change_privilege(integer) => fixnum
+ *
+ * Change the current process's real and effective user ID to that
+ * specified by _integer_. Returns the new user ID. Not
+ * available on all platforms.
+ *
+ * [Process.uid, Process.euid] #=> [0, 0]
+ * Process::UID.change_privilege(31) #=> 31
+ * [Process.uid, Process.euid] #=> [31, 31]
+ */
+
static VALUE
p_uid_change_privilege(obj, id)
VALUE obj, id;
@@ -1501,6 +2136,17 @@ p_uid_change_privilege(obj, id)
return INT2FIX(uid);
}
+
+
+/*
+ * call-seq:
+ * Process::Sys.setgid(integer) => nil
+ *
+ * Set the group ID of the current process to _integer_. Not
+ * available on all platforms.
+ *
+ */
+
static VALUE
p_sys_setgid(obj, id)
VALUE obj, id;
@@ -1514,6 +2160,16 @@ p_sys_setgid(obj, id)
return Qnil;
}
+
+/*
+ * call-seq:
+ * Process::Sys.setrgid(integer) => nil
+ *
+ * Set the real group ID of the calling process to _integer_.
+ * Not available on all platforms.
+ *
+ */
+
static VALUE
p_sys_setrgid(obj, id)
VALUE obj, id;
@@ -1527,6 +2183,17 @@ p_sys_setrgid(obj, id)
return Qnil;
}
+
+
+/*
+ * call-seq:
+ * Process::Sys.setegid(integer) => nil
+ *
+ * Set the effective group ID of the calling process to
+ * _integer_. Not available on all platforms.
+ *
+ */
+
static VALUE
p_sys_setegid(obj, id)
VALUE obj, id;
@@ -1540,6 +2207,18 @@ p_sys_setegid(obj, id)
return Qnil;
}
+
+/*
+ * call-seq:
+ * Process::Sys.setregid(rid, eid) => nil
+ *
+ * Sets the (integer) real and/or effective group IDs of the current
+ * process to <em>rid</em> and <em>eid</em>, respectively. A value of
+ * <code>-1</code> for either means to leave that ID unchanged. Not
+ * available on all platforms.
+ *
+ */
+
static VALUE
p_sys_setregid(obj, rid, eid)
VALUE obj, rid, eid;
@@ -1553,6 +2232,17 @@ p_sys_setregid(obj, rid, eid)
return Qnil;
}
+/*
+ * call-seq:
+ * Process::Sys.setresgid(rid, eid, sid) => nil
+ *
+ * Sets the (integer) real, effective, and saved user IDs of the
+ * current process to <em>rid</em>, <em>eid</em>, and <em>sid</em>
+ * respectively. A value of <code>-1</code> for any value means to
+ * leave that ID unchanged. Not available on all platforms.
+ *
+ */
+
static VALUE
p_sys_setresgid(obj, rid, eid, sid)
VALUE obj, rid, eid, sid;
@@ -1566,6 +2256,19 @@ p_sys_setresgid(obj, rid, eid, sid)
return Qnil;
}
+
+/*
+ * call-seq:
+ * Process::Sys.issetugid => true or false
+ *
+ * Returns +true+ if the process was created as a result
+ * of an execve(2) system call which had either of the setuid or
+ * setgid bits set (and extra privileges were given as a result) or
+ * if it has changed any of its real, effective or saved user or
+ * group IDs since it began execution.
+ *
+ */
+
static VALUE
p_sys_issetugid(obj)
VALUE obj;
@@ -1582,6 +2285,18 @@ p_sys_issetugid(obj)
#endif
}
+
+/*
+ * call-seq:
+ * Process.gid => fixnum
+ * Process::GID.rid => fixnum
+ * Process::Sys.getgid => fixnum
+ *
+ * Returns the (real) group ID for this process.
+ *
+ * Process.gid #=> 500
+ */
+
static VALUE
proc_getgid(obj)
VALUE obj;
@@ -1590,6 +2305,14 @@ proc_getgid(obj)
return INT2FIX(gid);
}
+
+/*
+ * call-seq:
+ * Process.gid= fixnum => fixnum
+ *
+ * Sets the group ID for this process.
+ */
+
static VALUE
proc_setgid(obj, id)
VALUE obj, id;
@@ -1621,6 +2344,18 @@ proc_setgid(obj, id)
static size_t maxgroups = 32;
+
+/*
+ * call-seq:
+ * Process.groups => array
+ *
+ * Get an <code>Array</code> of the gids of groups in the
+ * supplemental group access list for this process.
+ *
+ * Process.groups #=> [27, 6, 10, 11]
+ *
+ */
+
static VALUE
proc_getgroups(VALUE obj)
{
@@ -1647,6 +2382,20 @@ proc_getgroups(VALUE obj)
#endif
}
+
+/*
+ * call-seq:
+ * Process.groups= array => array
+ *
+ * Set the supplemental group access list to the given
+ * <code>Array</code> of group IDs.
+ *
+ * Process.groups #=> [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
+ * Process.groups = [27, 6, 10, 11] #=> [27, 6, 10, 11]
+ * Process.groups #=> [27, 6, 10, 11]
+ *
+ */
+
static VALUE
proc_setgroups(VALUE obj, VALUE ary)
{
@@ -1697,6 +2446,24 @@ proc_setgroups(VALUE obj, VALUE ary)
#endif
}
+
+/*
+ * call-seq:
+ * Process.initgroups(username, gid) => array
+ *
+ * Initializes the supplemental group access list by reading the
+ * system group database and using all groups of which the given user
+ * is a member. The group with the specified <em>gid</em> is also
+ * added to the list. Returns the resulting <code>Array</code> of the
+ * gids of all the groups in the supplementary group access list. Not
+ * available on all platforms.
+ *
+ * Process.groups #=> [0, 1, 2, 3, 4, 6, 10, 11, 20, 26, 27]
+ * Process.initgroups( "mgranger", 30 ) #=> [30, 6, 10, 11]
+ * Process.groups #=> [30, 6, 10, 11]
+ *
+ */
+
static VALUE
proc_initgroups(obj, uname, base_grp)
VALUE obj, uname, base_grp;
@@ -1712,6 +2479,17 @@ proc_initgroups(obj, uname, base_grp)
#endif
}
+
+/*
+ * call-seq:
+ * Process.maxgroups => fixnum
+ *
+ * Returns the maximum number of gids allowed in the supplemental
+ * group access list.
+ *
+ * Process.maxgroups #=> 32
+ */
+
static VALUE
proc_getmaxgroups(obj)
VALUE obj;
@@ -1719,6 +2497,15 @@ proc_getmaxgroups(obj)
return INT2FIX(maxgroups);
}
+
+/*
+ * call-seq:
+ * Process.maxgroups= fixnum => fixnum
+ *
+ * Sets the maximum number of gids allowed in the supplemental group
+ * access list.
+ */
+
static VALUE
proc_setmaxgroups(obj, val)
VALUE obj;
@@ -1733,8 +2520,33 @@ proc_setmaxgroups(obj, val)
return INT2FIX(maxgroups);
}
+
+/********************************************************************
+ *
+ * Document-class: Process::GID
+ *
+ * The <code>Process::GID</code> module contains a collection of
+ * module functions which can be used to portably get, set, and
+ * switch the current process's real, effective, and saved group IDs.
+ *
+ */
+
static int SAVED_GROUP_ID;
+
+/*
+ * call-seq:
+ * Process::GID.change_privilege(integer) => fixnum
+ *
+ * Change the current process's real and effective group ID to that
+ * specified by _integer_. Returns the new group ID. Not
+ * available on all platforms.
+ *
+ * [Process.gid, Process.egid] #=> [0, 0]
+ * Process::GID.change_privilege(33) #=> 33
+ * [Process.gid, Process.egid] #=> [33, 33]
+ */
+
static VALUE
p_gid_change_privilege(obj, id)
VALUE obj, id;
@@ -1876,6 +2688,18 @@ p_gid_change_privilege(obj, id)
return INT2FIX(gid);
}
+
+/*
+ * call-seq:
+ * Process.euid => fixnum
+ * Process::UID.eid => fixnum
+ * Process::Sys.geteuid => fixnum
+ *
+ * Returns the effective user ID for this process.
+ *
+ * Process.euid #=> 501
+ */
+
static VALUE
proc_geteuid(obj)
VALUE obj;
@@ -1884,6 +2708,15 @@ proc_geteuid(obj)
return INT2FIX(euid);
}
+
+/*
+ * call-seq:
+ * Process.euid= integer
+ *
+ * Sets the effective user ID for this process. Not available on all
+ * platforms.
+ */
+
static VALUE
proc_seteuid(obj, euid)
VALUE obj, euid;
@@ -1944,6 +2777,21 @@ rb_seteuid_core(euid)
return INT2FIX(euid);
}
+
+/*
+ * call-seq:
+ * Process::UID.grant_privilege(integer) => fixnum
+ * Process::UID.eid= integer => fixnum
+ *
+ * Set the effective user ID, and if possible, the saved user ID of
+ * the process to the given _integer_. Returns the new
+ * effective user ID. Not available on all platforms.
+ *
+ * [Process.uid, Process.euid] #=> [0, 0]
+ * Process::UID.grant_privilege(31) #=> 31
+ * [Process.uid, Process.euid] #=> [0, 31]
+ */
+
static VALUE
p_uid_grant_privilege(obj, id)
VALUE obj, id;
@@ -1951,6 +2799,19 @@ p_uid_grant_privilege(obj, id)
return rb_seteuid_core(NUM2INT(id));
}
+
+/*
+ * call-seq:
+ * Process.egid => fixnum
+ * Process::GID.eid => fixnum
+ * Process::Sys.geteid => fixnum
+ *
+ * Returns the effective group ID for this process. Not available on
+ * all platforms.
+ *
+ * Process.egid #=> 500
+ */
+
static VALUE
proc_getegid(obj)
VALUE obj;
@@ -1960,6 +2821,15 @@ proc_getegid(obj)
return INT2FIX(egid);
}
+
+/*
+ * call-seq:
+ * Process.egid = fixnum => fixnum
+ *
+ * Sets the effective group ID for this process. Not available on all
+ * platforms.
+ */
+
static VALUE
proc_setegid(obj, egid)
VALUE obj, egid;
@@ -2021,6 +2891,21 @@ rb_setegid_core(egid)
return INT2FIX(egid);
}
+
+/*
+ * call-seq:
+ * Process::GID.grant_privilege(integer) => fixnum
+ * Process::GID.eid = integer => fixnum
+ *
+ * Set the effective group ID, and if possible, the saved group ID of
+ * the process to the given _integer_. Returns the new
+ * effective group ID. Not available on all platforms.
+ *
+ * [Process.gid, Process.egid] #=> [0, 0]
+ * Process::GID.grant_privilege(31) #=> 33
+ * [Process.gid, Process.egid] #=> [0, 33]
+ */
+
static VALUE
p_gid_grant_privilege(obj, id)
VALUE obj, id;
@@ -2028,6 +2913,16 @@ p_gid_grant_privilege(obj, id)
return rb_setegid_core(NUM2INT(id));
}
+
+/*
+ * call-seq:
+ * Process::UID.re_exchangeable? => true or false
+ *
+ * Returns +true+ if the real and effective user IDs of a
+ * process may be exchanged on the current platform.
+ *
+ */
+
static VALUE
p_uid_exchangeable()
{
@@ -2040,6 +2935,19 @@ p_uid_exchangeable()
#endif
}
+
+/*
+ * call-seq:
+ * Process::UID.re_exchange => fixnum
+ *
+ * Exchange real and effective user IDs and return the new effective
+ * user ID. Not available on all platforms.
+ *
+ * [Process.uid, Process.euid] #=> [0, 31]
+ * Process::UID.re_exchange #=> 0
+ * [Process.uid, Process.euid] #=> [31, 0]
+ */
+
static VALUE
p_uid_exchange(obj)
VALUE obj;
@@ -2063,6 +2971,16 @@ p_uid_exchange(obj)
return INT2FIX(uid);
}
+
+/*
+ * call-seq:
+ * Process::GID.re_exchangeable? => true or false
+ *
+ * Returns +true+ if the real and effective group IDs of a
+ * process may be exchanged on the current platform.
+ *
+ */
+
static VALUE
p_gid_exchangeable()
{
@@ -2075,6 +2993,19 @@ p_gid_exchangeable()
#endif
}
+
+/*
+ * call-seq:
+ * Process::GID.re_exchange => fixnum
+ *
+ * Exchange real and effective group IDs and return the new effective
+ * group ID. Not available on all platforms.
+ *
+ * [Process.gid, Process.egid] #=> [0, 33]
+ * Process::GID.re_exchange #=> 0
+ * [Process.gid, Process.egid] #=> [33, 0]
+ */
+
static VALUE
p_gid_exchange(obj)
VALUE obj;
@@ -2098,6 +3029,17 @@ p_gid_exchange(obj)
return INT2FIX(gid);
}
+/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
+
+/*
+ * call-seq:
+ * Process::UID.sid_available? => true or false
+ *
+ * Returns +true+ if the current platform has saved user
+ * ID functionality.
+ *
+ */
+
static VALUE
p_uid_have_saved_id()
{
@@ -2118,6 +3060,20 @@ p_uid_sw_ensure(id)
return rb_seteuid_core(id);
}
+
+/*
+ * call-seq:
+ * Process::UID.switch => fixnum
+ * Process::UID.switch {|| block} => object
+ *
+ * Switch the effective and real user IDs of the current process. If
+ * a <em>block</em> is given, the user IDs will be switched back
+ * after the block is executed. Returns the new effective user ID if
+ * called without a block, and the return value of the block if one
+ * is given.
+ *
+ */
+
static VALUE
p_uid_switch(obj)
VALUE obj;
@@ -2186,6 +3142,18 @@ p_uid_switch(obj)
#endif
}
+
+/* [MG] :FIXME: Is this correct? I'm not sure how to phrase this. */
+
+/*
+ * call-seq:
+ * Process::GID.sid_available? => true or false
+ *
+ * Returns +true+ if the current platform has saved group
+ * ID functionality.
+ *
+ */
+
static VALUE
p_gid_have_saved_id()
{
@@ -2205,6 +3173,20 @@ p_gid_sw_ensure(id)
return rb_setegid_core(id);
}
+
+/*
+ * call-seq:
+ * Process::GID.switch => fixnum
+ * Process::GID.switch {|| block} => object
+ *
+ * Switch the effective and real group IDs of the current process. If
+ * a <em>block</em> is given, the group IDs will be switched back
+ * after the block is executed. Returns the new effective group ID if
+ * called without a block, and the return value of the block if one
+ * is given.
+ *
+ */
+
static VALUE
p_gid_switch(obj)
VALUE obj;
@@ -2272,6 +3254,19 @@ p_gid_switch(obj)
#endif
}
+
+/*
+ * call-seq:
+ * Process.times => aStructTms
+ *
+ * Returns a <code>Tms</code> structure (see <code>Struct::Tms</code>
+ * on page 388) that contains user and system CPU times for this
+ * process.
+ *
+ * t = Process.times
+ * [ t.utime, t.stime ] #=> [0.0, 0.02]
+ */
+
VALUE
rb_proc_times(obj)
VALUE obj;
@@ -2303,6 +3298,12 @@ VALUE rb_mProcUID;
VALUE rb_mProcGID;
VALUE rb_mProcID_Syscall;
+
+/*
+ * The <code>Process</code> module is a collection of methods used to
+ * manipulate processes.
+ */
+
void
Init_process()
{
@@ -2331,10 +3332,10 @@ Init_process()
rb_define_singleton_method(rb_mProcess, "fork", rb_f_fork, 0);
rb_define_singleton_method(rb_mProcess, "exit!", rb_f_exit_bang, -1);
- rb_define_singleton_method(rb_mProcess, "exit", rb_f_exit, -1);
- rb_define_singleton_method(rb_mProcess, "abort", rb_f_abort, -1);
+ rb_define_singleton_method(rb_mProcess, "exit", rb_f_exit, -1); // in eval.c
+ rb_define_singleton_method(rb_mProcess, "abort", rb_f_abort, -1); // in eval.c
- rb_define_module_function(rb_mProcess, "kill", rb_f_kill, -1);
+ rb_define_module_function(rb_mProcess, "kill", rb_f_kill, -1); // in signal.c
rb_define_module_function(rb_mProcess, "wait", proc_wait, -1);
rb_define_module_function(rb_mProcess, "wait2", proc_wait2, -1);
rb_define_module_function(rb_mProcess, "waitpid", proc_wait, -1);
diff --git a/signal.c b/signal.c
index 3bc2c7f..91e0478 100644
--- a/signal.c
+++ b/signal.c
@@ -198,6 +198,30 @@ ruby_signal_name(no)
return signo2signm(no);
}
+/*
+ * call-seq:
+ * Process.kill(signal, pid, ...) => fixnum
+ *
+ * Sends the given signal to the specified process id(s), or to the
+ * current process if _pid_ is zero. _signal_ may be an
+ * integer signal number or a POSIX signal name (either with or without
+ * a +SIG+ prefix). If _signal_ is negative (or starts
+ * with a minus sign), kills process groups instead of
+ * processes. Not all signals are available on all platforms.
+ *
+ * pid = fork do
+ * Signal.trap("HUP") { puts "Ouch!"; exit }
+ * # ... do some work ...
+ * end
+ * # ...
+ * Process.kill("HUP", pid)
+ * Process.wait
+ *
+ * <em>produces:</em>
+ *
+ * Ouch!
+ */
+
VALUE
rb_f_kill(argc, argv)
int argc;