summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Env.rb14
-rw-r--r--lib/cgi-lib.rb10
-rw-r--r--lib/delegate.rb46
-rw-r--r--lib/eregex.rb11
-rw-r--r--lib/importenv.rb15
-rw-r--r--lib/mkmf.rb2
-rw-r--r--lib/thwait.rb71
-rw-r--r--lib/tk.rb63
-rw-r--r--lib/tkafter.rb265
-rw-r--r--lib/tkdialog.rb2
-rw-r--r--lib/tkpalette.rb4
-rw-r--r--lib/tktext.rb33
12 files changed, 461 insertions, 75 deletions
diff --git a/lib/Env.rb b/lib/Env.rb
index e52501f801..df14023f9e 100644
--- a/lib/Env.rb
+++ b/lib/Env.rb
@@ -20,9 +20,11 @@ for k,v in ENV
EOS
end
-p $TERM
-$TERM = nil
-p $TERM
-p ENV["TERM"]
-$TERM = "foo"
-p ENV["TERM"]
+if __FILE__ == $0
+ p $TERM
+ $TERM = nil
+ p $TERM
+ p ENV["TERM"]
+ $TERM = "foo"
+ p ENV["TERM"]
+end
diff --git a/lib/cgi-lib.rb b/lib/cgi-lib.rb
index 6c20237ff5..6a846b2017 100644
--- a/lib/cgi-lib.rb
+++ b/lib/cgi-lib.rb
@@ -25,7 +25,7 @@ class CGI < SimpleDelegator
words = Shellwords.shellwords(if not ARGV.empty? then
ARGV.join(' ')
else
- print "(offline mode: enter name=value pairs on standard input)\n" if STDOUT.tty?
+ STDERR.print "(offline mode: enter name=value pairs on standard input)\n" if STDOUT.tty?
readlines.join(' ').gsub(/\n/, '')
end.gsub(/\\=/, '%3D').gsub(/\\&/, '%26'))
@@ -47,14 +47,18 @@ class CGI < SimpleDelegator
module_function :escape, :unescape
def initialize(input = $stdin)
- # exception messages should be printed to stdout.
- STDERR.reopen(STDOUT)
@inputs = {}
case ENV['REQUEST_METHOD']
when "GET"
+ # exception messages should be printed to stdout.
+ STDERR.reopen(STDOUT)
+
ENV['QUERY_STRING'] or ""
when "POST"
+ # exception messages should be printed to stdout.
+ STDERR.reopen(STDOUT)
+
input.read ENV['CONTENT_LENGTH'].to_i
else
read_from_cmdline
diff --git a/lib/delegate.rb b/lib/delegate.rb
index 7889c8284f..925b4ec867 100644
--- a/lib/delegate.rb
+++ b/lib/delegate.rb
@@ -9,6 +9,12 @@
# foo = Object.new
# foo2 = SimpleDelegator.new(foo)
# foo.hash == foo2.hash # => true
+#
+# Foo = DelegateClass(Array)
+#
+# class ExtArray<DelegateClass(Array)
+# ...
+# end
class Delegator
@@ -18,7 +24,7 @@ class Delegator
preserved |= t.instance_methods
break if t == Delegator
end
- preserved -= ["__getobj__","to_s","nil?","to_a","hash","dup","==","=~"]
+ preserved -= ["__getobj__","to_s","to_a","inspect","hash","eql?","==","=~","==="]
for method in obj.methods
next if preserved.include? method
eval <<EOS
@@ -61,7 +67,44 @@ end
Delegater = Delegator
SimpleDelegater = SimpleDelegator
+#
+def DelegateClass(superclass)
+ klass = Class.new
+ methods = superclass.instance_methods
+ methods -= ::Kernel.instance_methods
+ methods |= ["to_s","to_a","inspect","hash","eql?","==","=~","==="]
+ klass.module_eval <<EOS
+ def initialize(obj)
+ @obj = obj
+ end
+EOS
+ for method in methods
+ klass.module_eval <<EOS
+ def #{method}(*args, &block)
+ begin
+ @obj.__send__(:#{method}, *args, &block)
+ rescue
+ $@[0,2] = nil
+ raise
+ end
+ end
+EOS
+ end
+ return klass;
+end
+
if __FILE__ == $0
+ class ExtArray<DelegateClass(Array)
+ def initialize()
+ super([])
+ end
+ end
+
+ ary = ExtArray.new
+ p ary.type
+ ary.push 25
+ p ary
+
foo = Object.new
def foo.test
raise 'this is OK'
@@ -69,4 +112,5 @@ if __FILE__ == $0
foo2 = SimpleDelegator.new(foo)
p foo.hash == foo2.hash # => true
foo.test # raise error!
+
end
diff --git a/lib/eregex.rb b/lib/eregex.rb
index f214f6a2d4..384d531e0f 100644
--- a/lib/eregex.rb
+++ b/lib/eregex.rb
@@ -30,10 +30,7 @@ class Regexp
end
end
-p "abc" =~ /b/|/c/
-p "abc" =~ /b/&/c/
-
-
-
-
-
+if __FILE__ == $0
+ p "abc" =~ /b/|/c/
+ p "abc" =~ /b/&/c/
+end
diff --git a/lib/importenv.rb b/lib/importenv.rb
index 41253765ea..10b289199c 100644
--- a/lib/importenv.rb
+++ b/lib/importenv.rb
@@ -21,9 +21,12 @@ for k,v in ENV
EOS
end
-p $TERM
-$TERM = nil
-p $TERM
-p ENV["TERM"]
-$TERM = "foo"
-p ENV["TERM"]
+if __FILE__ == $0
+ p $TERM
+ $TERM = nil
+ p $TERM
+ p ENV["TERM"]
+ $TERM = "foo"
+ p ENV["TERM"]
+end
+
diff --git a/lib/mkmf.rb b/lib/mkmf.rb
index 8ef5bfd79b..306f23905b 100644
--- a/lib/mkmf.rb
+++ b/lib/mkmf.rb
@@ -260,7 +260,7 @@ SHELL = /bin/sh
srcdir = #{$srcdir}
hdrdir = #{$hdrdir}
-CC = gcc
+CC = #{CONFIG["CC"]}
prefix = #{CONFIG["prefix"]}
CFLAGS = #{CONFIG["CCDLFLAGS"]} -I#{$hdrdir} -I#{CONFIG["includedir"]} #{CFLAGS} #{$CFLAGS} #{$defs.join(" ")}
diff --git a/lib/thwait.rb b/lib/thwait.rb
index c638335f5d..84ba2c8749 100644
--- a/lib/thwait.rb
+++ b/lib/thwait.rb
@@ -1,34 +1,60 @@
#
-# thwait.rb -
-# $Release Version: $
-# $Revision: 1.1 $
-# $Date: 1997/08/18 03:13:14 $
-# by Keiju ISHITSUKA(Nippon Rational Inc.)
+# thwait.rb - スレッド同期クラス
+# $Release Version: 0.9 $
+# $Revision: 1.3 $
+# $Date: 1998/06/26 03:19:34 $
+# by Keiju ISHITSUKA(Nihpon Rational Software Co.,Ltd.)
#
# --
+# 機能:
+# 複数のスレッドを関しそれらのスレッドが終了するまでwaitする機能を提
+# 供する.
#
-#
+# クラスメソッド:
+# * ThreadsWait.all_waits(thread1,...)
+# 全てのスレッドが終了するまで待つ. イテレータとして呼ばれた時には,
+# スレッドが終了する度にイテレータを実行する.
+# * th = ThreadsWait.new(thread1,...)
+# 同期するスレッドを指定し同期オブジェクトを生成.
+#
+# メソッド:
+# * th.threads
+# 同期すべきスレッドの一覧
+# * th.empty?
+# 同期すべきスレッドがあるかどうか
+# * th.finished?
+# すでに終了したスレッドがあるかどうか
+# * th.join(thread1,...)
+# 同期するスレッドを指定し, いずれかのスレッドが終了するまで待ちにはいる.
+# * th.join_nowait(threa1,...)
+# 同期するスレッドを指定する. 待ちには入らない.
+# * th.next_wait
+# いずれかのスレッドが終了するまで待ちにはいる.
+# * th.all_waits
+# 全てのスレッドが終了するまで待つ. イテレータとして呼ばれた時には,
+# スレッドが終了する度にイテレータを実行する.
#
require "thread.rb"
require "e2mmap.rb"
class ThreadsWait
- RCS_ID='-$Header: /home/keiju/var/src/var.lib/ruby/RCS/thwait.rb,v 1.1 1997/08/18 03:13:14 keiju Exp keiju $-'
+ RCS_ID='-$Id: thwait.rb,v 1.3 1998/06/26 03:19:34 keiju Exp keiju $-'
Exception2MessageMapper.extend_to(binding)
- def_exception("ErrWaitThreadsNothing", "Wait threads nothing.")
- def_exception("FinshedThreadsNothing", "finished thread nothing.")
+ def_exception("ErrNoWaitingThread", "No threads for waiting.")
+ def_exception("ErrNoFinshedThread", "No finished threads.")
# class mthods
# all_waits
#
# 指定したスレッドが全て終了するまで待つ. イテレータとして呼ばれると
- # 指定したスレッドが終了するとイテレータを呼び出す.
+ # 指定したスレッドが終了するとその終了したスレッドを引数としてイテレー
+ # タを呼び出す.
#
def ThreadsWait.all_waits(*threads)
- tw = ThreadsWait.new(th1, th2, th3, th4, th5)
+ tw = ThreadsWait.new(*threads)
if iterator?
tw.all_waits do
|th|
@@ -81,7 +107,8 @@ class ThreadsWait
# all_wait
#
- # 待っているスレッドを追加し待ちにはいる.
+ # 待っているスレッドを追加し. いずれかのスレッドが1つ終了するまで待
+ # ちにはいる.
#
def join(*threads)
join_nowait(*threads)
@@ -102,17 +129,19 @@ class ThreadsWait
end
#
- # 次の待ちにはいる.
- # 待つべきスレッドがなければ, 例外ErrWaitThreadsNothing を返す.
+ # いずれかのスレッドが終了するまで待ちにはいる.
+ # 待つべきスレッドがなければ, 例外ErrNoWaitingThreadを返す.
# nonnlockが真の時には, nonblockingで調べる. 存在しなければ, 例外
- # FinishedThreadNothingを返す.
+ # ErrNoFinishedThreadを返す.
#
def next_wait(nonblock = nil)
- Threads.Wait.fail ErrWaitThreadsNothing if @threads.empty?
-
- th = @wait_queue.pop(nonblock)
- @threads.delete th
- th
+ ThreadsWait.fail ErrNoWaitingThread if @threads.empty?
+ begin
+ @threads.delete(th = @wait_queue.pop(nonblock))
+ th
+ rescue ThreadError
+ ThreadsWait.fail ErrNoFinshedThread
+ end
end
#
@@ -126,3 +155,5 @@ class ThreadsWait
end
end
end
+
+ThWait = ThreadsWait
diff --git a/lib/tk.rb b/lib/tk.rb
index 5d1d68d902..204e22ac37 100644
--- a/lib/tk.rb
+++ b/lib/tk.rb
@@ -165,7 +165,7 @@ module TkComm
end
private :_get_eval_string
- Tk_IDs = [0] # [0]-cmdid, [1]-winid
+ Tk_IDs = [0, 0] # [0]-cmdid, [1]-winid
def _curr_cmd_id
id = format("c%.4d", Tk_IDs[0])
end
@@ -188,8 +188,8 @@ module TkComm
private :install_cmd, :uninstall_cmd
def install_win(ppath)
- id = format("w%.4d", Tk_IDs[0])
- Tk_IDs[0] += 1
+ id = format("w%.4d", Tk_IDs[1])
+ Tk_IDs[1] += 1
if !ppath or ppath == "."
@path = format(".%s", id);
else
@@ -413,10 +413,10 @@ module Tk
list(w) if args.size == 0
end
def group(*args)
- tk_call 'wm', 'path', path, *args
+ tk_call 'wm', 'group', path, *args
end
def iconbitmap(*args)
- tk_call 'wm', 'bitmap', path, *args
+ tk_call 'wm', 'iconbitmap', path, *args
end
def iconify
tk_call 'wm', 'iconify', path
@@ -504,6 +504,10 @@ class TkVariable
end
end
+ def wait
+ INTERP._eval("tkwait variable #{@id}")
+ end
+
def id
@id
end
@@ -867,6 +871,12 @@ module TkWinfo
def winfo_y
TkWinfo.y self
end
+ def TkWinfo.viewable(window)
+ bool(tk_call 'winfo', 'viewable', window.path)
+ end
+ def winfo_viewable
+ TkWinfo.viewable self
+ end
end
module TkPack
@@ -912,11 +922,11 @@ module TkGrid
tk_call "grid", 'configure', *(wins+hash_kv(keys))
end
- def columnconfigure(master, index, *args)
+ def columnconfigure(master, index, args)
tk_call "grid", 'columnconfigure', master, index, *hash_kv(args)
end
- def rowconfigure(master, index, *args)
+ def rowconfigure(master, index, args)
tk_call "grid", 'rowconfigure', master, index, *hash_kv(args)
end
@@ -948,7 +958,7 @@ module TkGrid
tk_call 'grid', 'size', master
end
- def slaves(*args)
+ def slaves(args)
list(tk_call('grid', 'slaves', *hash_kv(args)))
end
@@ -1190,6 +1200,15 @@ class TkWindow<TkObject
end
uninstall_win
end
+
+ def wait_visibility
+ tk_call 'tkwait', 'visibility', path
+ end
+ alias wait wait_visibility
+
+ def wait_destroy
+ tk_call 'tkwait', 'window', path
+ end
end
class TkRoot<TkWindow
@@ -1442,6 +1461,9 @@ class TkMenu<TkWindow
def yposition(index)
number(tk_send('yposition', index))
end
+ def entryconfigure(index, keys=nil)
+ tk_send 'entryconfigure', index, *hash_kv(keys)
+ end
end
class TkMenubutton<TkLabel
@@ -1495,6 +1517,31 @@ module TkComposite
end
end
+module TkClipboard
+ include Tk
+ extend Tk
+
+ def clear
+ tk_call 'clipboard', 'clear'
+ end
+ def get
+ begin
+ tk_call 'selection', 'get', '-selection', 'CLIPBOARD'
+ rescue
+ ''
+ end
+ end
+ def set(data)
+ clear
+ append(data)
+ end
+ def append(data)
+ tk_call 'clipboard', 'append', data
+ end
+
+ module_function :clear, :set, :get, :append
+end
+
autoload :TkCanvas, 'tkcanvas'
autoload :TkImage, 'tkcanvas'
autoload :TkBitmapImage, 'tkcanvas'
diff --git a/lib/tkafter.rb b/lib/tkafter.rb
new file mode 100644
index 0000000000..708d051002
--- /dev/null
+++ b/lib/tkafter.rb
@@ -0,0 +1,265 @@
+#
+# tkafter.rb : methods for Tcl/Tk after command
+# 1998/06/23 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp>
+#
+require 'tk'
+
+class TkAfter
+ include TkCore
+ extend TkCore
+
+ Tk_CBID = [0]
+ Tk_CBTBL = {}
+
+ INTERP._invoke("proc", "rb_after", "args", "ruby [format \"TkAfter.callback %%Q!%s!\" $args]")
+
+ ###############################
+ # class methods
+ ###############################
+ def TkAfter.callback(arg)
+ @after_id = nil
+ arg = Array(tk_split_list(arg))
+ obj_id = arg.shift
+ return nil if Tk_CBTBL[obj_id] == nil; # canceled
+ ret = _get_eval_string(Tk_CBTBL[obj_id].do_callback(*arg))
+ Tk_CBTBL[obj_id].set_next_callback(*arg)
+ ret
+ end
+
+ def TkAfter.info
+ tk_call('after', 'info').split(' ').filter{|id|
+ ret = Tk_CBTBL.find{|key,val| val.after_id == id}
+ (ret == nil)? id: ret[1]
+ }
+ end
+
+ ###############################
+ # instance methods
+ ###############################
+ def do_callback(*args)
+ @current_proc.call(*args)
+ end
+
+ def set_callback(sleep, args=nil)
+ @after_script = "rb_after #{@id} #{_get_eval_string(args)}"
+ @after_id = tk_call('after', sleep, @after_script)
+ @current_script = [sleep, @after_script]
+ end
+
+ def set_next_callback(*args)
+ if @running == false || @proc_max == 0 || @do_loop == 0
+ Tk_CBTBL[@id] = nil ;# for GC
+ return
+ end
+ if @current_pos >= @proc_max
+ if @do_loop < 0 || (@do_loop -= 1) > 0
+ @current_pos = 0
+ else
+ Tk_CBTBL[@id] = nil ;# for GC
+ return
+ end
+ end
+
+ @current_args = args
+
+ if @sleep_time.kind_of? Proc
+ sleep = @sleep_time.call(*args)
+ else
+ sleep = @sleep_time
+ end
+ @current_sleep = sleep
+
+ cmd, *cmd_args = @loop_proc[@current_pos]
+ @current_pos += 1
+ @current_proc = cmd
+
+ if cmd_args[0].kind_of? Proc
+ #c = cmd_args.shift
+ #cb_args = c.call(*(cmd_args + args))
+ cb_args = cmd_args[0].call(*args)
+ else
+ cb_args = cmd_args
+ end
+
+ set_callback(sleep, cb_args)
+ end
+
+ def initialize(*args)
+ @id = format("a%.4d", Tk_CBID[0])
+ Tk_CBID[0] += 1
+
+ @init_sleep=0
+ @init_proc=nil
+ @init_args=[]
+
+ @current_script = []
+ @current_proc = nil
+ @current_args = nil
+
+ @sleep_time = 0
+ @current_sleep = 0
+ @loop_exec = 0
+ @do_loop = 0
+ @loop_proc = []
+ @proc_max = 0
+ @current_pos = 0
+
+ @after_id = nil
+ @after_script = nil
+
+ set_procs(*args) if args != []
+
+ @running = false
+ end
+
+ attr :after_id
+ attr :after_script
+ attr :current_proc
+ attr :current_sleep
+
+ attr_accessor :loop_exec
+
+ def get_procs
+ [@init_sleep, @init_proc, @init_args, @sleep_time, @loop_exec, @loop_proc]
+ end
+
+ def current_status
+ [@running, @current_sleep, @current_proc, @current_args, @do_loop]
+ end
+
+ def running?
+ @running
+ end
+
+ def loop_rest
+ @do_loop
+ end
+
+ def loop_rest=(rest)
+ @do_loop = rest
+ end
+
+ def set_procs(interval, loop_exec, *procs)
+ if !interval == 'idle' \
+ && !interval.kind_of?(Integer) && !interval.kind_of?(Proc)
+ fail format("%s need to be Integer or Proc", interval.inspect)
+ end
+ @sleep_time = interval
+
+ @loop_proc = []
+ procs.each{|e|
+ if e.kind_of? Proc
+ @loop_proc.push([e])
+ else
+ @loop_proc.push(e)
+ end
+ }
+ @proc_max = @loop_proc.size
+ @current_pos = 0
+
+ @do_loop = 0
+ if loop_exec
+ if loop_exec.kind_of?(Integer) && loop_exec < 0
+ @loop_exec = -1
+ elsif loop_exec == nil || loop_exec == false || loop_exec == 0
+ @loop_exec = 1
+ else
+ if not loop_exec.kind_of?(Integer)
+ fail format("%s need to be Integer", loop_exec.inspect)
+ end
+ @loop_exec = loop_exec
+ end
+ @do_loop = @loop_exec
+ end
+
+ self
+ end
+
+ def add_procs(*procs)
+ procs.each{|e|
+ if e.kind_of? Proc
+ @loop_proc.push([e])
+ else
+ @loop_proc.push(e)
+ end
+ }
+ @proc_max = @loop_proc.size
+
+ self
+ end
+
+ def start(sleep=0, init_proc=nil, *init_args)
+ return nil if @running
+
+ Tk_CBTBL[@id] = self
+ @do_loop = @loop_exec
+ @current_pos = 0
+
+ if !sleep == 'idle' && !sleep.kind_of?(Integer)
+ fail format("%s need to be Integer", sleep.inspect)
+ end
+
+ @init_proc = init_proc
+ @init_args = init_args
+ @current_sleep = @init_sleep = sleep
+ @running = true
+ if init_proc
+ if not init_proc.kind_of? Proc
+ fail format("%s need to be Proc", init_proc.inspect)
+ end
+ @current_proc = init_proc
+ set_callback(sleep, init_args)
+ else
+ set_next_callback(*init_args)
+ end
+
+ self
+ end
+
+ def restart
+ cancel if @running
+ start(@init_sleep, @init_proc, @init_args)
+ end
+
+ def cancel
+ @running = false
+ tk_call 'after', 'cancel', @after_id if @after_id
+ @after_id = nil
+ Tk_CBTBL[@id] = nil ;# for GC
+ self
+ end
+ alias stop cancel
+
+ def continue(wait=nil)
+ sleep, cmd = @current_script
+ return nil if cmd == nil || @running == true
+ if wait
+ if not wait.kind_of? Integer
+ fail format("%s need to be Integer", wait.inspect)
+ end
+ sleep = wait
+ end
+ Tk_CBTBL[@id] = self
+ @running = true
+ @after_id = tk_call('after', sleep, cmd)
+ self
+ end
+
+ def skip
+ return nil if @running == false
+ cancel
+ Tk_CBTBL[@id] = self
+ @running = true
+ set_next_callback(@current_args)
+ self
+ end
+
+ def info
+ if @after_id
+ inf = tk_split_list(tk_call('after', 'info', @after_id))
+ [Tk_CBTBL[inf[0][1]], inf[1]]
+ else
+ nil
+ end
+ end
+end
diff --git a/lib/tkdialog.rb b/lib/tkdialog.rb
index e8f2142e07..011d431951 100644
--- a/lib/tkdialog.rb
+++ b/lib/tkdialog.rb
@@ -9,7 +9,7 @@ class TkDialog < TkWindow
INTERP._eval('eval {global '+id+';'+
'set '+id+' [tk_dialog '+
@path+" "+title+" \"#{message}\" "+bitmap+" "+
- default_button+" "+buttons+']}')
+ String(default_button)+" "+buttons+']}')
end
def value
return @var.value.to_i
diff --git a/lib/tkpalette.rb b/lib/tkpalette.rb
index b317330937..a2dc7c87cb 100644
--- a/lib/tkpalette.rb
+++ b/lib/tkpalette.rb
@@ -1,7 +1,9 @@
#
# tkpalette.rb : methods for Tcl/Tk standard library 'palette.tcl'
-# 1998/06/18 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp>
+# 1998/06/21 by Hidetoshi Nagai <nagai@ai.kyutech.ac.jp>
#
+require 'tk'
+
module TkPalette
include Tk
extend Tk
diff --git a/lib/tktext.rb b/lib/tktext.rb
index 8e6772c470..146944dde7 100644
--- a/lib/tktext.rb
+++ b/lib/tktext.rb
@@ -146,21 +146,12 @@ class TkTextTag<TkObject
end
def nextrange(first, last=nil)
- l = tk_split_list(tk_call(@t.path, 'tag', 'nextrange', @id, first, last))
- r = []
- while key=l.shift
- r.push [key, l.shift]
- end
- r
+ tk_split_list(tk_call(@t.path, 'tag', 'nextrange', @id, first, last))
end
def prevrange(first, last=nil)
+ tk_split_list(tk_call(@t.path, 'tag', 'prevrange', @id, first, last))
l = tk_split_list(tk_call(@t.path, 'tag', 'prevrange', @id, first, last))
- r = []
- while key=l.shift
- r.push [key, l.shift]
- end
- r
end
def [](key)
@@ -175,17 +166,17 @@ class TkTextTag<TkObject
tk_call @t.path, 'tag', 'cget', @id, "-#{key}"
end
- def configure(keys)
- tk_call @t.path, 'tag', 'configure', @id, *hash_kv(keys)
+ def configure(key, val=nil)
+ if key.kind_of? Hash
+ tk_call @t.path, 'tag', 'configure', @id, *hash_kv(key)
+ else
+ tk_call @t.path, 'tag', 'configure', @id, "-#{key}", val
+ end
+ end
+
+ def configinfo
+ tk_split_list(tk_call(@t.path, 'tag', 'configure', @id))
end
-# def configure(key, value)
-# if value == FALSE
-# value = "0"
-# elsif value.kind_of? Proc
-# value = install_cmd(value)
-# end
-# tk_call @t.path, 'tag', 'configure', @id, "-#{key}", value
-# end
def bind(seq, cmd=Proc.new, args=nil)
id = install_bind(cmd, args)