* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / lib / shell.rb
blob6a64cb263fc1da946e1ca4cd2dc0ba3f1e387bf4
2 #   shell.rb - 
3 #       $Release Version: 0.7 $
4 #       $Revision: 1.9 $
5 #       by Keiju ISHITSUKA(keiju@ruby-lang.org)
7 # --
9 #   
12 require "e2mmap"
14 require "thread" unless defined?(Mutex)
16 require "forwardable"
18 require "shell/error"
19 require "shell/command-processor"
20 require "shell/process-controller"
22 class Shell
23   @RCS_ID='-$Id: shell.rb,v 1.9 2002/03/04 12:01:10 keiju Exp keiju $-'
25   include Error
26   extend Exception2MessageMapper
28 #  @cascade = true
29   # debug: true -> normal debug
30   # debug: 1    -> eval definition debug
31   # debug: 2    -> detail inspect debug
32   @debug = false
33   @verbose = true
35   @debug_display_process_id = false
36   @debug_display_thread_id = true
37   @debug_output_mutex = Mutex.new
39   class << Shell
40     extend Forwardable
42     attr_accessor :cascade, :debug, :verbose
44 #    alias cascade? cascade
45     alias debug? debug
46     alias verbose? verbose
47     @verbose = true
49     def debug=(val)
50       @debug = val
51       @verbose = val if val
52     end
54     def cd(path)
55       new(path)
56     end
58     def default_system_path
59       if @default_system_path
60         @default_system_path
61       else
62         ENV["PATH"].split(":")
63       end
64     end
66     def default_system_path=(path)
67       @default_system_path = path
68     end
70     def default_record_separator
71       if @default_record_separator
72         @default_record_separator
73       else
74         $/
75       end
76     end
78     def default_record_separator=(rs)
79       @default_record_separator = rs
80     end
82     # os resource mutex
83     mutex_methods = ["unlock", "lock", "locked?", "synchronize", "try_lock", "exclusive_unlock"]
84     for m in mutex_methods
85       def_delegator("@debug_output_mutex", m, "debug_output_"+m.to_s)
86     end
88   end
90   def initialize(pwd = Dir.pwd, umask = nil)
91     @cwd = File.expand_path(pwd)
92     @dir_stack = []
93     @umask = umask
95     @system_path = Shell.default_system_path
96     @record_separator = Shell.default_record_separator
98     @command_processor = CommandProcessor.new(self)
99     @process_controller = ProcessController.new(self)
101     @verbose = Shell.verbose
102     @debug = Shell.debug
103   end
105   attr_reader :system_path
107   def system_path=(path)
108     @system_path = path
109     rehash
110   end
112   attr_accessor :umask, :record_separator
113   attr_accessor :verbose, :debug
115   def debug=(val)
116     @debug = val
117     @verbose = val if val
118   end
120   alias verbose? verbose
121   alias debug? debug
123   attr_reader :command_processor
124   attr_reader :process_controller
126   def expand_path(path)
127     File.expand_path(path, @cwd)
128   end
130   # Most Shell commands are defined via CommandProcessor
132   #
133   # Dir related methods
134   #
135   # Shell#cwd/dir/getwd/pwd
136   # Shell#chdir/cd
137   # Shell#pushdir/pushd
138   # Shell#popdir/popd
139   # Shell#mkdir
140   # Shell#rmdir
142   attr_reader :cwd
143   alias dir cwd
144   alias getwd cwd
145   alias pwd cwd
147   attr_reader :dir_stack
148   alias dirs dir_stack
150   # If called as iterator, it restores the current directory when the
151   # block ends.
152   def chdir(path = nil, verbose = @verbose)
153     check_point
155     if iterator?
156       notify("chdir(with block) #{path}") if verbose
157       cwd_old = @cwd
158       begin
159         chdir(path, nil)
160         yield
161       ensure
162         chdir(cwd_old, nil)
163       end
164     else
165       notify("chdir #{path}") if verbose
166       path = "~" unless path
167       @cwd = expand_path(path)
168       notify "current dir: #{@cwd}"
169       rehash
170       Void.new(self)
171     end
172   end
173   alias cd chdir
175   def pushdir(path = nil, verbose = @verbose)
176     check_point
178     if iterator?
179       notify("pushdir(with block) #{path}") if verbose
180       pushdir(path, nil)
181       begin
182         yield
183       ensure
184         popdir
185       end
186     elsif path
187       notify("pushdir #{path}") if verbose
188       @dir_stack.push @cwd
189       chdir(path, nil)
190       notify "dir stack: [#{@dir_stack.join ', '}]"
191       self
192     else
193       notify("pushdir") if verbose
194       if pop = @dir_stack.pop
195         @dir_stack.push @cwd
196         chdir pop
197         notify "dir stack: [#{@dir_stack.join ', '}]"
198         self
199       else
200         Shell.Fail DirStackEmpty
201       end
202     end
203     Void.new(self)
204   end
205   alias pushd pushdir
207   def popdir
208     check_point
210     notify("popdir")
211     if pop = @dir_stack.pop
212       chdir pop
213       notify "dir stack: [#{@dir_stack.join ', '}]"
214       self
215     else
216       Shell.Fail DirStackEmpty
217     end
218     Void.new(self)
219   end
220   alias popd popdir
222   #
223   # process management
224   #
225   def jobs
226     @process_controller.jobs
227   end
229   def kill(sig, command)
230     @process_controller.kill_job(sig, command)
231   end
233   #
234   # command definitions
235   #
236   def Shell.def_system_command(command, path = command)
237     CommandProcessor.def_system_command(command, path)
238   end
240   def Shell.undef_system_command(command)
241     CommandProcessor.undef_system_command(command)
242   end
244   def Shell.alias_command(ali, command, *opts, &block)
245     CommandProcessor.alias_command(ali, command, *opts, &block)
246   end
248   def Shell.unalias_command(ali)
249     CommandProcessor.unalias_command(ali)
250   end
252   def Shell.install_system_commands(pre = "sys_")
253     CommandProcessor.install_system_commands(pre)
254   end
256   #
257   def inspect
258     if debug.kind_of?(Integer) && debug > 2
259       super
260     else
261       to_s
262     end
263   end
265   def self.notify(*opts, &block)
266     Shell::debug_output_synchronize do
267       if opts[-1].kind_of?(String)
268         yorn = verbose?
269       else
270         yorn = opts.pop
271       end
272       return unless yorn
274       if @debug_display_thread_id
275         if @debug_display_process_id
276           prefix = "shell(##{Process.pid}:#{Thread.current.to_s.sub("Thread", "Th")}): "
277         else
278           prefix = "shell(#{Thread.current.to_s.sub("Thread", "Th")}): "
279         end
280       else
281         prefix = "shell: "
282       end
283       _head = true
284       STDERR.print opts.collect{|mes|
285         mes = mes.dup
286         yield mes if iterator?
287         if _head
288           _head = false
289 #         "shell" " + mes
290           prefix + mes
291         else
292           " "* prefix.size + mes
293         end
294       }.join("\n")+"\n"
295     end
296   end
298   CommandProcessor.initialize
299   CommandProcessor.run_config