From 88c127c19be3d12bc5edcb6ceb416985258396c6 Mon Sep 17 00:00:00 2001 From: dave Date: Fri, 2 Jan 2004 06:01:12 +0000 Subject: Finish documenting internal stuff. See Changelog for other details git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5364 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- .document | 11 + ChangeLog | 13 + MANIFEST | 2 + bin/ri | 385 +++++++------ eval.c | 13 +- lib/.document | 4 + lib/rdoc/code_objects.rb | 6 + lib/rdoc/generators/ri_generator.rb | 4 +- lib/rdoc/parsers/parse_c.rb | 9 +- lib/rdoc/parsers/parse_f95.rb | 3 +- lib/rdoc/parsers/parse_rb.rb | 7 +- lib/rdoc/parsers/parse_simple.rb | 2 +- lib/rdoc/parsers/parserfactory.rb | 5 +- lib/rdoc/rdoc.rb | 73 ++- lib/rdoc/ri/ri_descriptions.rb | 14 + lib/rdoc/ri/ri_formatter.rb | 25 +- lib/rdoc/ri/ri_options.rb | 35 +- object.c | 55 +- process.c | 1007 ++++++++++++++++++++++++++++++++++- signal.c | 24 + 20 files changed, 1442 insertions(+), 255 deletions(-) create mode 100644 .document create mode 100644 lib/.document diff --git a/.document b/.document new file mode 100644 index 0000000000..2471a9ce85 --- /dev/null +++ b/.document @@ -0,0 +1,11 @@ +# This file determines which files in the +# Ruby hierarchy will be processed by the RDoc +# tool when it is given the top-level directory +# as an argument + +# Process all the C source files +*.c + +# the lib/ directory (which has its own .document file) + +lib diff --git a/ChangeLog b/ChangeLog index 58adab1edd..92f2acc0be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Fri Jan 2 14:54:11 2004 Dave Thomas + + * 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. + Thu Jan 1 09:03:20 2004 Dave Thomas * bin/ri (report_class_stuff): Fix problem with ambiguous nested diff --git a/MANIFEST b/MANIFEST index 831072bba5..2247390266 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 0fee8a84d4..498850027d 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 - ###################################################################### @@ -53,31 +42,43 @@ class RiDisplay end + ###################################################################### + + 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 1578dc7720..6d88111a85 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 * SystemExit 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 at_exit - * functions and runs any object finalizers (see - * ObjectSpace beginning on page 434). + * Just prior to termination, Ruby executes any at_exit 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 * Kernel.exit(1). If _msg_ is given, it is written diff --git a/lib/.document b/lib/.document new file mode 100644 index 0000000000..dc9eab9a8d --- /dev/null +++ b/lib/.document @@ -0,0 +1,4 @@ +# We only run RDoc on the top-level files in here: we skip +# all the helper stuff in sub-directories + +*.rb \ No newline at end of file diff --git a/lib/rdoc/code_objects.rb b/lib/rdoc/code_objects.rb index f5b4e81853..a8c3752cb8 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 705ad7da06..8d94579347 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 4aca5eeb67..904aeb9417 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 3adf29d933..518e421c60 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 380025ef8d..b8044c119b 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 754f650793..b01104574e 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 dc7d629903..b19bd0ca9b 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 ca9c5da2bf..ddcf217c4b 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 9bd5c2d13b..96041f1c7e 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 f41a815435..8fd214437d 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 9b0704d421..fe323ed45f 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", "", "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 90b76979a4..26d3ea39b9 100644 --- a/object.c +++ b/object.c @@ -1171,7 +1171,7 @@ sym_to_sym(sym) /*********************************************************************** * - * Document-class: Module + * Document-class: Module * * A Module 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 4311032367..af9523f0c4 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}" } + * + * produces: + * + * I am 27417 + * Dad is 27417 + */ + static VALUE get_ppid() { @@ -121,6 +149,37 @@ get_ppid() #endif } + +/********************************************************************* + * + * Document-class: Process::Status + * + * Process::Status encapsulates the information on the + * status of a running or terminated system process. The built-in + * variable $? is either +nil+ or a + * Process::Status 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 + * Process::Status 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 Fixnum. 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_.to_i.to_s. + */ + 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 other. + */ + 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 num. + * + * 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 num 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 wait call had the + * WUNTRACED 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 exit() 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 exited? 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 $? to a Process::Status 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 + * Process::WNOHANG (do not block if no child available) + * or Process::WUNTRACED (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 SystemError 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 Process::Status object) of that + * child. Raises a SystemError 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 + * Process::Status object). + * + * fork { sleep 0.2; exit 2 } #=> 27432 + * fork { sleep 0.1; exit 1 } #=> 27433 + * fork { exit 0 } #=> 27434 + * p Process.waitall + * + * produces: + * + * [[27434, #], + * [27433, #], + * [27432, #]] + */ + 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 wait(). If the parent never collects + * this status, the child stays around as a zombie process. + * Process::detach 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 detach + * only when you do not intent to explicitly wait for the child to + * terminate. detach 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}") + * + * produces: + * + * 27389 Z + * + * In the next example, Process::detach 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}") + * + * (produces no output) + */ + 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 argv[0] value, which may show up in process + * listings. In MSDOS environments, the command is executed in a + * subshell; otherwise, one of the exec(2) 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 Kernel::fork. + */ + 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. fixnum 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 $?. The + * arguments are processed in the same way as for + * Kernel::exec. + * + * system("echo *") + * system("echo", "*") + * + * produces: + * + * 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 Thread#run. 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 setpgid(0,0). 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 integer. 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. kind indicates the kind of entity to find: one + * of Process::PRIO_PGRP, + * Process::PRIO_USER, or + * Process::PRIO_PROCESS. _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 Process#getpriority. + * + * 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 Process::Sys 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 Process, + * Process::UID, and Process::GID 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 + * -1 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 -1 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 Process::UID 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 rid and eid, respectively. A value of + * -1 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 rid, eid, and sid + * respectively. A value of -1 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 Array 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 + * Array 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 gid is also + * added to the list. Returns the resulting Array 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 Process::GID 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 block 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 block 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 Tms structure (see Struct::Tms + * 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 Process 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 3bc2c7f7a2..91e0478c32 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 + * + * produces: + * + * Ouch! + */ + VALUE rb_f_kill(argc, argv) int argc; -- cgit v1.2.3