summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorhut <hut@lavabit.com>2009-05-09 00:00:00 +0200
committerhut <hut@lavabit.com>2009-05-09 00:00:00 +0200
commita0de7f95bc7525b99b2c2e16f566e0ee367e9c3c (patch)
tree62d1cdf9523f4e8fad28ca7df0e1d696c991d2db
parent34bfb32ecf2cea5e5de95980beedb681139d9c01 (diff)
downloadranger-0.2.1.tar.gz
lots of changes. version 0.2.1 v0.2.1
-rw-r--r--ChangeLog46
-rw-r--r--Readme0
-rw-r--r--code/action.rb69
-rw-r--r--code/bars.rb134
-rw-r--r--code/debug.rb24
-rw-r--r--code/directory.rb (renamed from code/extensions.rb)381
-rw-r--r--code/draw.rb194
-rw-r--r--code/extensions/basic.rb149
-rw-r--r--code/extensions/fileutils.rb1726
-rw-r--r--code/fm.rb57
-rw-r--r--code/keys.rb238
-rw-r--r--code/screensaver/clock.rb10
-rw-r--r--code/types.rb106
-rwxr-xr-xdata/generate.rb21
-rw-r--r--data/mime.datbin0 -> 10452 bytes
-rw-r--r--data/mime.types769
-rwxr-xr-xfm35
-rw-r--r--interface/ncurses.rb110
18 files changed, 3562 insertions, 507 deletions
diff --git a/ChangeLog b/ChangeLog
index 375565f6..6c2edddd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,14 +1,54 @@
+Version 0.2.1
+
+2009-05-09  hut
+	Oh no. I neglegted to write change logs... in a nutshell, i changed:
+	New Hotkey: [r]...flags...[r] which runs the selected file with flags
+	like [d] to detach the process or [t] to run it in a seperate terminal
+	For that I also changed Action.run()
+
+2009-04-27  hut
+	Minor changes to the way directories and file infos are read
+		entries are not instantly scheduled upon encounter
+	Rewritten "ascend" method, which uses Action::run() now (still theres a lot
+	to be done)
+	Directory::entry#mimetype() instead of Fm.MIMETYPES[Directory::entry#ext()]
+
+2009-04-18  hut
+	Found reproducable bug which leaves it (after closing) running and using up
+		99% CPU. It happens when the window managers way of closing windows by
+		pressing the X button or pressing Alt+F4 is used to quit.
+	New Hotkeys: [S-TAB] is a shortcut for [`][`] and [TAB] for [`][9]
+	Better distinguishable columns
+	MIME support, allowing sorting by MIME-filetype
+		TODO: fall back to sorting by name for two files of identical filetype
+	Sorting by extension
+	Minor bugs fixed
+
+2009-04-16  hut
+	New Combination [c][u][t] to mark files for moving
+	Replaced system() calls with cp or mv with the use of FileUtils
+	Modified FileUtils for showing its progress the progressbar
+	Fixed threading problem which made it impossible to stop copying/moving
+	Fixed priority problems, fluent UI while copying.
+
+	TODO: enqueue file operations for better performance
+
+2009-04-15  hut
+
+	Added possibility to run fm with a filename in argument which runs the
+	file, like an enter-press inside fm would run it.
+
 Version 0.2
 
 2009-04-10  hut
 	Generating Previews in the background
-	Help screen
-	Tooltips for (S) and (t)
+	Help screen with [?]
+	Tooltips for [S] and [t]
 	Added more filetype associations
 	Split up fm.rb into several files
 
 2009-04-09  hut
-	New Hotkey (S) to change sorting order
+	New Hotkey [S] to change sorting order
 	Generalized file-modifying tasks in the Action module
 
 2009-04-08  hut
diff --git a/Readme b/Readme
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/Readme
diff --git a/code/action.rb b/code/action.rb
new file mode 100644
index 00000000..e463e852
--- /dev/null
+++ b/code/action.rb
@@ -0,0 +1,69 @@
+module Action
+	def self.copy(files, path)
+		files = [files] unless files.is_a? Array
+		unless files.empty?
+			CopyBar2.new(files, path)
+		end
+	end
+
+	def self.move(files, path)
+		files = [files] unless files.is_a? Array
+		unless files.empty?
+			MoveBar2.new(files, path)
+		end
+	end
+
+	def self.run(hash = {})
+		unless OpenStruct === hash
+			hash = OpenStruct.new(hash)
+		end
+
+		cf       = Fm.currentfile
+		
+		all      = hash.all.or true
+		files    = hash.files.or(all ? Fm.selection : [cf])
+		mode     = hash.mode.or 0
+		newway   = hash.newway.or false
+
+		return false if files.nil?
+
+		if newway
+			hash = Fm.filehandler(files, hash)
+			handler = hash.exec
+		else
+			handler, wait = Fm.getfilehandler(*files)
+		end
+
+		return false unless handler
+
+		wait     = hash.wait.or wait
+		new_term = hash.new_term.or false
+		detach   = hash.detach.or false
+
+		log handler
+		if detach
+			run_detached(handler, new_term)
+		else
+			run_inside(handler, wait)
+		end
+		return true
+	end
+
+	def self.run_detached(what, new_term)
+		if new_term
+			p = fork { exec('x-terminal-emulator', '-e', 'bash', '-c', what) }
+#			Process.detach(p)
+		else
+			p = fork { exec "#{what} 2>> /dev/null >> /dev/null" }
+			Process.detach(p)
+		end
+	end
+
+	def self.run_inside(what, wait)
+		closei
+		system(*what)
+		gets if wait
+		starti
+	end
+end
+
diff --git a/code/bars.rb b/code/bars.rb
new file mode 100644
index 00000000..61d14572
--- /dev/null
+++ b/code/bars.rb
@@ -0,0 +1,134 @@
+class Bar
+	def initialize( text = '' )
+		@text = text
+		@text_prefix = nil
+		@max = 0
+		@done = 0
+		@counter = 0
+		@thread = nil
+		@update_proc = nil
+		Fm.bar_add(self)
+	end
+
+	def kill(evil = true)
+		Fm.bar_del(self)
+		Fm.force_update
+
+		@thread.kill
+	end
+
+	def update(&block)
+		if block
+			@update_proc = block
+		elsif @update_proc
+			@update_proc.call(self)
+		end
+	end
+
+	def set_text_prefix(text)
+		@text_prefix = text
+	end
+	def set_text(text)
+		@text_prefix = nil
+		@text = text
+	end
+	alias text= set_text
+
+	attr_accessor :thread, :counter, :max
+end
+
+class CopyBar < Bar
+	def initialize( text = '' )
+		super
+
+		@update_proc = proc do |b|
+			begin
+				b.done = File.size(fname).to_f / finished
+			rescue
+				b.done = 0
+			end
+		end
+	end
+end
+
+class Bar2
+	def kill(evil = true)
+		Fm.bar_del(self)
+		Fm.force_update
+
+		@thread.kill
+	end
+
+	def set_text_prefix(text)
+		@text_prefix = text
+	end
+	def set_text(text)
+		@text_prefix = nil
+		@text = text
+	end
+	alias text= set_text
+
+	attr_accessor :thread, :counter, :max
+	def done
+		if @counter.is_a? MutableNumber
+			@counter.value.to_f / @max
+		else
+			0
+		end
+	end
+
+	def update() end
+
+	def text
+		if @text_prefix
+			"#{@text_prefix} #{(@counter.value.to_f * 10000/ @max).round.to_f/100}%"
+		elsif @text
+			@text
+		else
+			""
+		end
+	end
+end
+
+class CopyOrMoveBar < Bar2
+	def initialize(files, path)
+		path = path.path unless path.is_a? String
+		Fm.bar_add(self)
+		log([files, path])
+
+		
+		@thread = Thread.new do
+			begin
+				for file in files
+					file = file.path unless file.is_a? String
+					if File.exists?(file)
+						run_operation(file, path)
+					end
+				end
+			rescue
+				log $!
+				log $!.backtrace
+			ensure
+				kill(false)
+			end
+		end
+		@thread.priority = Fm::COPY_PRIORITY
+	end
+end
+
+class CopyBar2 < CopyOrMoveBar
+	def run_operation(file, path)
+		if File.directory?(file)
+			FileUtils.cp_r_in_bar(self, file, path)
+		else
+			FileUtils.cp_in_bar(self, file, path)
+		end
+	end
+end
+
+class MoveBar2 < CopyOrMoveBar
+	def run_operation(file, path)
+		FileUtils.mv_in_bar(self, file, path)
+	end
+end
+
diff --git a/code/debug.rb b/code/debug.rb
index 6261ed0e..d3a97190 100644
--- a/code/debug.rb
+++ b/code/debug.rb
@@ -1,4 +1,3 @@
-
 require 'pp'
 
 module Debug
@@ -25,6 +24,28 @@ module Debug
 	end
 
 	if LOG_LEVEL > 0
+		def bm(descr="benchmark", &block)
+			# Benchmark
+			t1 = Time.now
+			yield
+			dur = Time.now-t1
+
+			# substract the durtation of a "bm(..) do end"
+			dur -= bm_dummy(descr) do end
+
+			# Format the duration
+			dur *= 1000
+			dur = dur > 0 ? dur : 0
+			dur = '%0.3f' % dur
+			logerr("#{descr}: #{dur}ms")
+		end
+
+		def bm_dummy(descr="benchmark", &block)
+			t1 = Time.now
+			yield
+			return (Time.now-t1)
+		end
+
 		def __log__(obj, level)
 			if level <= LOG_LEVEL
 				obj = obj.nil? ? "checkpoint at #{Time.now}" : obj
@@ -63,5 +84,6 @@ module Debug
 		def log(a=nil) end
 		def logpp(a=nil) end
 		def trace() end
+		def bm(*args, &block) yield end
 	end
 end
diff --git a/code/extensions.rb b/code/directory.rb
index a15fc952..c42392aa 100644
--- a/code/extensions.rb
+++ b/code/directory.rb
@@ -1,184 +1,6 @@
-class Bar
-	def initialize( text = '' )
-		@text = text
-		@done = 0
-		@thread = nil
-		@update_proc = nil
-		Fm.bar_add(self)
-	end
-
-	def kill(evil = true)
-		Fm.bar_del(self)
-		Fm.force_update
-
-		@thread.kill
-	end
-
-	def update(&block)
-		if block
-			@update_proc = block
-		elsif @update_proc
-			@update_proc.call(self)
-		end
-	end
-
-	attr_accessor :text, :done, :thread
-end
-
-class CopyBar < Bar
-	def initialize( text = '' )
-		super
-
-		@update_proc = proc do |b|
-			begin
-				b.done = File.size(fname).to_f / finished
-			rescue
-				b.done = 0
-			end
-		end
-	end
-end
-
-module Action
-#	def self.get_all_files(path)
-#		glob = Dir.new(path).to_a
-#	end
-	
-	def self.make_a_bar_for_one(text, command)
-		bar = CopyBar.new(test)
-
-		finished = File.size(from[0]).to_f
-		fname = File.join(to, File.basename(from[0]))
-
-		bar.thread = Thread.new do
-			begin
-				system('ionice', '-c3', command, *(from + [to]))
-			ensure
-				bar.kill(false)
-			end
-		end
-	end
-
-	def self.copy(from, to)
-#		log [from, to]
-
-#		if String === from[0]
-#			from[0] = Directory::Entry.new(from[0])
-#		end
-
-		if from.size == 1 and from[0].file?
-			from = from[0]
-			bar = Bar.new("Copying...")
-			finished = from.size.to_f
-			fname = File.join(to, from.basename)
-
-			bar.update do |b|
-				begin
-					b.done = File.size(fname).to_f / finished
-				rescue
-					b.done = 0
-				end
-			end
-
-			bar.thread = Thread.new do
-				begin
-					system('cp', from.to_s, to)
-				ensure
-					bar.kill(false)
-				end
-			end
-
-		else
-			bar = Bar.new("Copying...")
-			from = from.dup
-			from = [from] unless Array === from
-			finished = Dir.number_of_files(*from.map{|x| x.to_s})
-			count = 0
-
-			bar.update do |b|
-				begin
-					b.done = count / finished
-				rescue
-					b.done = 0
-				end
-			end
-			
-			from.map!{|x| x.to_s}
-			bar.thread = Thread.new do
-				begin
-					system('cp', '-r', *(from + [to.to_s]))
-#					IO.popen("cp -vr #{from.join(' ')} #{to.sh}") do |f|
-#					IO.popen(['cp', '-vr', *(from + [to])]) do |f|
-#						count += 1 while f.gets =~ /' -> `/
-#					end
-				ensure
-					bar.kill(false)
-				end
-			end
-		end
-	end
-
-	def self.move(from, to)
-#		log [from, to]
-		
-#		if String === from[0]
-#			from[0] = Directory::Entry.new(from[0])
-#		end
-
-		if from.size == 1 and from[0].file?
-			from = from[0]
-			bar = Bar.new("Moving...")
-			finished = from.size.to_f
-			fname = File.join(to, from.basename)
-
-			bar.update do |b|
-				begin
-					b.done = File.size(fname).to_f / finished
-				rescue
-					b.done = 0
-				end
-			end
-
-			bar.thread = Thread.new do
-				begin
-					system('mv', from.to_s, to)
-				ensure
-					bar.kill(false)
-				end
-			end
-
-		else
-			bar = Bar.new("Moving...")
-			from = from.dup
-			from = [from] unless Array === from
-			finished = Dir.number_of_files(*from.map{|x| x.to_s})
-			count = 0
-
-			bar.update do |b|
-				begin
-					b.done = count / finished
-				rescue
-					b.done = 0
-				end
-			end
-			
-			from.map!{|x| x.to_s}
-			bar.thread = Thread.new do
-				begin
-					system('mv', *(from + [to.to_s]))
-#					IO.popen("mv -v #{from.join(' ')} #{to.sh}") do |f|
-#						count += 1 while f.gets =~ /' -> `/
-#					end
-				ensure
-					bar.kill(false)
-				end
-			end
-		end
-	end
-end
-
 class Directory
 	BAD_TIME = Time.at(0)
+	MOVIE_EXTENSIONS = %w(avi mpg mpeg mp4 mp5 ogv ogm wmv mkv flv fid vob div divx)
 	class Entry #{{{
 		# Let's just cache every shit, because i don't want
 		# to call File methods all the time
@@ -194,6 +16,9 @@ class Directory
 				@dirname = File.dirname(dirname)
 				@basename = File.basename(dirname)
 			end
+			@name, @ext = @basename.split_at_last_dot
+#			@ext = @basename.from_last('.') || ''
+			@movie = MOVIE_EXTENSIONS.include?(@ext)
 			@size = 0
 			@exists = false
 			@rights = '----------'
@@ -208,8 +33,10 @@ class Directory
 			@marked = false
 		end
 
-		attr_reader(:basename, :mtime, :rights, :type, :path,
-					  :infostring, :readlink, :basename, :size, :ctime)
+		attr_reader *%w(
+			basename mtime rights type path ext
+			infostring readlink basename size ctime name
+		)
 
 		attr_accessor(:marked)
 		
@@ -217,11 +44,19 @@ class Directory
 		def exists?() @exists end
 		def marked?() @marked end
 		def symlink?() @symlink end
+		def movie?() @movie end
 		def broken_symlink?() @symlink and !@exists end
 		def dir?() @type == :dir end
 		def file?() @type == :file end
 		def writable?() @writable end
 		def executable?() @executable end
+		def mimetype()
+			if @type == :dir
+				nil
+			else
+				Fm::MIMETYPES[@ext]
+			end
+		end
 
 		def delete!
 			if @type == :dir
@@ -242,17 +77,7 @@ class Directory
 		end
 
 		def sh
-			res = @path.dup
-			res.gsub!('\\\\', "\000")
-			res.gsub!(' ', '\\ ')
-			res.gsub!('(', '\\(')
-			res.gsub!('&', '\\&')
-			res.gsub!(')', '\\)')
-			res.gsub!('*', '\\*')
-			res.gsub!('\'', '\\\'')
-			res.gsub!('"', '\\"')
-			res.gsub!("\000", '\\\\')
-			return res
+			@path.sh
 		end
 
 		def in? path
@@ -322,6 +147,9 @@ class Directory
 		@file_size = 0
 		@pointed_file = nil
 		@width = 1000
+		@read = false
+		@empty = true
+		@scheduled = false
 
 		refresh
 	end
@@ -334,6 +162,7 @@ class Directory
 		else
 			@files.reject!{|x| x[0] == ?. or x == 'lost+found'}
 		end
+
 		if @files.empty?
 			@files = ['.']
 		end
@@ -342,14 +171,60 @@ class Directory
 		@files.map!{|basename| Entry.new(@path, basename)}
 	end
 
-	attr_reader(:path, :files, :pos, :width, :files_raw, :file_size)
+	attr_reader(:path, :files, :pos, :width, :files_raw,
+					:file_size, :read)
+	attr_accessor(:scheduled)
+
+	def scheduled?() @scheduled end
+	def read?() @read end
 
 	def pos=(x)
+#		if @files.size <= 1 or x < 0
+#			x = 0
+#		elsif x > @files.size
+#			x = @files.size - 1
+#		end
 		@pos = x
+		make_sure_cursor_is_in_range()
 		@pointed_file = @files[x]
 		resize
 	end
 
+	def recheck_stuff()
+#		log "pointed file: #@pointed_file"
+#		log @files_raw
+#		log ""
+		if test = @files_raw.index(@pointed_file)
+#			log("if")
+			@pos = test
+		else
+#			log("else")
+			make_sure_cursor_is_in_range()
+		end
+	end
+
+	def make_sure_cursor_is_in_range()
+		if @files.size <= 1 or @pos < 0
+			@pos = 0
+		elsif @pos > @files.size
+			@pos = @files.size - 1
+		end
+	end
+
+	def find_file(x)
+		x = File.basename(x)
+
+		files.each_with_index do |file, i|
+			if file.basename == x
+				self.pos = i
+			end
+		end
+	end
+
+	def empty?()
+		Dir.entries(@path).size <= 2
+	end
+
 	def restore()
 		for f in @files
 			f.marked = false
@@ -389,6 +264,7 @@ class Directory
 			f.refresh
 			@file_size += f.size if f.file?
 		end
+		@read = true
 	end
 
 #	def refresh()
@@ -421,19 +297,29 @@ class Directory
 	end
 
 	def schedule()
+		@scheduled = true
 		Fm.schedule(self)
 	end
 
 	def refresh!()
+		oldfile = @pointed_file
 		read_dir
 		get_file_info
 		sort
+
+		if @files.include? oldfile
+			self.pointed_file = oldfile
+		end
 	end
 
 	def sort_sub(x, y)
 		case OPTIONS['sort']
 		when :name
 			x.basename <=> y.basename
+		when :ext
+			x.ext <=> y.ext
+		when :type
+			x.ext.filetype <=> y.ext.filetype
 		when :size
 			x.size <=> y.size
 		when :ctime
@@ -462,112 +348,3 @@ class Directory
 	end
 end
 
-
-class File
-	MODES_HASH = {
-		'0' => '---',
-		'1' => '--x',
-		'2' => '-w-',
-		'3' => '-wx',
-		'4' => 'r--',
-		'5' => 'r-x',
-		'6' => 'rw-',
-		'7' => 'rwx'
-	}
-	def self.modestr(f)
-		unless exists?(f)
-			return '----------'
-		end
-
-		if symlink?(f)
-			result = 'l'
-		elsif directory?(f)
-			result = 'd'
-		else
-			result = '-'
-		end
-
-		s = ("%o" % File.stat(f).mode)[-3..-1]
-		for m in s.each_char
-			result << MODES_HASH[m]
-		end
-
-		result
-	end
-end
-
-class Dir
-	def self.number_of_files(*dirs)
-		n = 0
-		dirs.each do |entry|
-			if File.directory?(entry)
-				n += 1 + number_of_files(*(Dir.new(entry).to_a - ['.', '..']).map\
-												 {|x| File.join entry, x } )
-			else
-				n += 1
-			end
-		end
-		return n
-	end
-end
-
-class Numeric
-	def limit(max, min = 0)
-		self < min ? min : (self > max ? max : self)
-	end
-
-	def bytes space = true, n_round = 2
-		n = 1024
-		a = %w(B K M G T Y)
-
-		i = 0
-		flt = self.to_f
-
-		while flt > n and i < a.length - 1
-			flt /= n
-			i += 1
-		end
-
-#		flt = flt.round(n_round)
-		r = 10 ** n_round
-		flt *= r
-		flt = flt.round.to_f / r
-		int = flt.to_i
-		flt = int if int == flt
-
-		return flt.to_s + (space ? ' ' + a[i] : a[i])
-	end
-end
-
-class Array
-	def wrap(n)
-		n.times { push shift }
-	end
-end
-
-class String
-	def clear
-		replace String.new
-	end
-	if RUBY_VERSION < '1.9'
-		def ord
-			self[0]
-		end
-	end
-
-	def sh
-		res = self.dup
-		res.gsub!('\\\\', "\000")
-		res.gsub!(' ', '\\ ')
-		res.gsub!('(', '\\(')
-		res.gsub!('&', '\\&')
-		res.gsub!(')', '\\)')
-		res.gsub!('*', '\\*')
-		res.gsub!('\'', '\\\'')
-		res.gsub!('"', '\\"')
-		res.gsub!("\000", '\\\\')
-		return res
-	end
-
-end
-
diff --git a/code/draw.rb b/code/draw.rb
index 1a2236cc..dac6c25c 100644
--- a/code/draw.rb
+++ b/code/draw.rb
@@ -1,5 +1,5 @@
 module Fm
-	DONT_PREVIEW_THESE_FILES = /\.(avi|[mj]pe?g|iso|mp\d|og[gmv]|wm[av]|mkv|torrent|so|class|flv|png|bmp|zip|rar|7z|tar|gz|vob|divx?)$/i
+	DONT_PREVIEW_THESE_FILES = /\.(avi|[mj]pe?g|iso|mp\d|og[gmv]|wm[av]|mkv|torrent|so|class|flv|png|bmp|vob|divx?)$/i
 
 	def self.column_put_file(n, file)
 		i = 0
@@ -7,85 +7,140 @@ module Fm
 			m = lines - 2
 			color 7
 			bold false
-			File.open(file.path, 'r') do |f|
-				check = true
-				left, wid = get_boundaries(n)
-				f.lines.each do |l|
-					if check
-						check = false
-						break unless l.each_char.all? {|x| x[0] > 0 and x[0] < 128}
-					end
-					puti i+1, left, l.gsub("\t","   ")[0, wid-1].ljust(wid)
+			left, wid = get_boundaries(n)
+			if file.ext =~ /(?:rar|zip|7z|tar|gz)$/ and file.size < 10485760
+				text = `aunpack -l #{file.sh} 2>> /dev/null`
+				text.each_line do |l|
+					puti i+1, left, l[0, wid-1].ljust(wid)
 					i += 1
 					break if i == m
 				end
+			else
+				File.open(file.path, 'r') do |f|
+					check = true
+					left, wid = get_boundaries(n)
+					f.lines.each do |l|
+						if check
+							check = false
+							break unless l.each_char.all? {|x| x[0] > 0 and x[0] < 128}
+						end
+						puti i+1, left, l.gsub("\t","   ")[0, wid-1].ljust(wid)
+						i += 1
+						break if i == m
+					end
+				end
 			end
 		end
 		column_clear(n, i)
 	end
 
 	def self.put_directory(c, d)
-		l = 0
+		l = 1
 		if d
 			infos = (c == COLUMNS - 2)
 			left, wid = get_boundaries(c)
 
-			offset = get_offset(d, lines)
-			(lines - 1).times do |l|
-				lpo = l + offset
-				bg = -1
-				break if (f = d.files[lpo]) == nil
-
-				dir = false
-				if f.symlink?
-					bld = true
-					if f.broken_symlink?
-						clr = [1, bg]
+			if d.read? and not d.empty?
+
+				offset = get_offset(d, lines)
+				(lines - 1).times do |l|
+					lpo = l + offset
+					bg = -1
+					break if (f = d.files[lpo]) == nil
+#					log f
+
+					dir = false
+					if f.symlink?
+						bld = true
+						if f.broken_symlink?
+							clr = [1, bg]
+						else
+							clr = [6, bg]
+						end
+						dir = f.dir?
+					elsif f.dir?
+						bld = true
+						dir = true
+						clr = [4, bg]
+					elsif f.movie?
+						bld = true
+						clr = [5, bg]
+					elsif f.executable?
+						bld = true
+						clr = [2, bg]
 					else
-						clr = [6, bg]
+						bld = false
+						clr = [7, bg]
 					end
-					dir = f.dir?
-				elsif f.dir?
-					bld = true
-					dir = true
-					clr = [4, bg]
-				elsif f.executable?
-					bld = true
-					clr = [2, bg]
-				else
-					bld = false
-					clr = [7, bg]
-				end
 
-				fn = f.basename
-				if f.marked?
-					fn = "*#{fn}"
-				end
-				if infos
-					myinfo = " #{f.infostring}  "
-					str = fn[0, wid-1].ljust(wid+1)
-					if str.size > myinfo.size
-						str[-myinfo.size..-1] = myinfo
-						yes = true
+					fn = f.basename
+					if f.marked?
+						fn = "* #{fn}"
+					end
+					if infos
+						myinfo = " #{f.infostring}  "
+						str = fn[0, wid-1].ljust(wid+1)
+						if str.size > myinfo.size
+							str[-myinfo.size..-1] = myinfo
+							yes = true
+						else
+							yes = false
+						end
+						puti l+1, left, str
+						if dir and yes
+							args = l+1, left+wid-myinfo.size, myinfo.size, *clr
+							color_bold_at(*args)
+						end
 					else
-						yes = false
+						puti l+1, left, fn[0, wid-1].ljust(wid+1)
 					end
-					puti l+1, left, str
-					if dir and yes
-						args = l+1, left+wid-myinfo.size, myinfo.size, *clr
-						color_bold_at(*args)
+
+					args = l+1, left, fn.size.limit(wid-1), *clr
+
+					if d.pos == lpo
+						if c == COLUMNS - 2
+#							puti l+1, left-1, '^'
+#							puti l+1, left+args[2], '$'
+
+							args[4] = 0
+#							args[1] -= 1
+#							if args[2] < 5
+#								args[2] = 7
+#							else
+#								args[2] += 1
+#							end
+#							color_bold_at(l+1, left-1, 1, 0, 0)
+#							color_bold_at(l+1, left+args[2], 1, 0, 0)
+							color_reverse_bold_at(*args)
+
+							# ...
+							args[1] -= 1; args[2] += 2
+							color_bold_at(*args)
+							args[1] += 1; args[2] -= 2
+							color_reverse_bold_at(*args)
+						else
+							color_reverse_at(*args)
+						end
+#						if f.marked?
+#							args[1] += 1
+#							args[2] = 1
+#							args[3] = 1
+#							color_reverse_at(*args)
+#						end
+					else
+						if bld then color_at(*args) else color_at(*args) end
+#						if bld then color_bold_at(*args) else color_at(*args) end
 					end
-				else
-					puti l+1, left, fn[0, wid-1].ljust(wid+1)
 				end
+			elsif d.read? and d.empty?
+				puti l, left, 'empty'.ljust(wid+1)
 
-				args = l+1, left, fn.size.limit(wid), *clr
+			elsif not d.read?
+				puti l, left, 'reading...'.ljust(wid+1)
+				d.schedule unless d.scheduled?
+			else
+				puti l, left, 'ERROR'.ljust(wid+1)
 
-				if d.pos == lpo
-					color_reverse_at(*args)
-				else
-					if bld then color_bold_at(*args) else color_at(*args) end
-				end
 			end
 		end
 
@@ -140,7 +195,9 @@ module Fm
 		bold false
 		@cur_y = get_boundaries(COLUMNS-2)[0]
 
-		if @buffer == '?'
+		if @buffer =~ /^block/
+			screensaver
+		elsif @buffer == '?'
 			cleari
 			puti 0, "      - - - Help - - -"
 			puti 2, "   h/j/k/l: Movement    J/K: fast Movement"
@@ -177,12 +234,22 @@ module Fm
 			puti 8, "   gh: go to ~/"
 			puti 9, "   gt: go to ~/.trash/"
 		else
+			@pwd.recheck_stuff()
+			cf = currentfile
+
+			if cf and s0 = cf.mimetype
+				puti 0, cols-s0.size, s0
+			end
+
 			s1 = "  "
 			s2 = "#{@path.last.path}#{"/" unless @path.size == 1}"
-			cf = currentfile
 			s3 = "#{cf ? cf.basename : ''}"
 			
-			puti 0, (s1 + s2 + s3).ljust(cols)
+			if s0
+				puti 0, (s1 + s2 + s3).ljust(cols-s0.size)
+			else
+				puti 0, (s1 + s2 + s3).ljust(cols)
+			end
 
 			bg = -1
 			color_at 0, 0, -1, 7, bg
@@ -227,6 +294,7 @@ module Fm
 				puti btm, "Toggle (h)idden_files (d)irs_first (c)olor (f)ilepreview"
 			else
 #				log(@pwd)
+#				log "Buffer: #{@buffer}"
 				puti btm, "#@buffer    #{@pwd.file_size.bytes(false)},#{@pwd.size},#{@pwd.pos+1}    ".rjust(cols)
 				more = ''
 				if cf.symlink?
@@ -255,10 +323,10 @@ module Fm
 			done = bar.done
 			c = (done * cols).to_i
 			unless done == 0
-				color_at l, 0, c, 0, 2
+				color_at l, 0, c, 0, 4
 			end
 			unless done == cols
-				color_at l, c, -1, 0, 3
+				color_at l, c, -1, 0, 6
 			end
 		end
 	end
diff --git a/code/extensions/basic.rb b/code/extensions/basic.rb
new file mode 100644
index 00000000..c822e9f6
--- /dev/null
+++ b/code/extensions/basic.rb
@@ -0,0 +1,149 @@
+# Generic extensions of the language
+
+class MutableNumber
+	attr_accessor :value
+
+	def initialize(n=0)
+		@value = n
+	end
+	def add(n=1) @value += n end
+	def sub(n=1) @value -= n end
+end
+
+class Array
+	def wrap(n)
+		# TODO: this can be done better...
+		n.times { push shift }
+	end
+end
+
+class String
+	def clear
+		self.replace("")
+	end
+
+	if RUBY_VERSION < '1.9'
+		def ord
+			self[0]
+		end
+	end
+
+	def from_first(str)
+		self.include?(str) ? self [ self.index(str) + str.size .. -1 ] : nil
+	end
+
+	def from_last(str)
+		self.include?(str) ? self [ self.rindex(str) + str.size .. -1 ] : nil
+	end
+
+	def split_at_last_dot()
+		if ix = self.rindex('.')
+			return self[0...ix], self[ix+1..-1]
+		else
+			return self, ''
+		end
+	end
+
+	def before_last(str)
+		self.include?(str) ? self [ 0 .. rindex(str) - str.size ] : self
+	end
+
+	def filetype()
+		Fm::MIMETYPES[self] || 'unknown'
+	end
+
+	def sh
+		res = self.dup
+		res.gsub!('\\\\', "\000")
+		res.gsub!(' ', '\\ ')
+		res.gsub!('(', '\\(')
+		res.gsub!('&', '\\\&')
+		res.gsub!(')', '\\)')
+		res.gsub!('*', '\\*')
+		res.gsub!('\'', "\\\\'")
+		res.gsub!('"', '\\"')
+		res.gsub!("\000", '\\\\')
+		return res
+	end
+end
+
+class Numeric
+	def limit(max, min = 0)
+		self < min ? min : (self > max ? max : self)
+	end
+
+	def bytes space = true, n_round = 2
+		n = 1024
+		a = %w(B K M G T Y)
+
+		i = 0
+		flt = self.to_f
+
+		while flt > n and i < a.length - 1
+			flt /= n
+			i += 1
+		end
+
+#		flt = flt.round(n_round)
+		r = 10 ** n_round
+		flt *= r
+		flt = flt.round.to_f / r
+		int = flt.to_i
+		flt = int if int == flt
+
+		return flt.to_s + (space ? ' ' + a[i] : a[i])
+	end
+end
+
+class Dir
+	def self.number_of_files(*dirs)
+		n = 0
+		dirs.each do |entry|
+			if File.directory?(entry)
+				n += 1 + number_of_files(*(Dir.new(entry).to_a - ['.', '..']).map\
+												 {|x| File.join entry, x } )
+			else
+				n += 1
+			end
+		end
+		return n
+	end
+end
+
+class File
+	MODES_HASH = {
+		'0' => '---',
+		'1' => '--x',
+		'2' => '-w-',
+		'3' => '-wx',
+		'4' => 'r--',
+		'5' => 'r-x',
+		'6' => 'rw-',
+		'7' => 'rwx'
+	}
+	def self.modestr(f)
+		unless exists?(f)
+			return '----------'
+		end
+
+		if symlink?(f)
+			result = 'l'
+		elsif directory?(f)
+			result = 'd'
+		else
+			result = '-'
+		end
+
+		s = ("%o" % File.stat(f).mode)[-3..-1]
+		for m in s.each_char
+			result << MODES_HASH[m]
+		end
+
+		result
+	end
+end
+
+
+class Object;   def or(value) self  end end
+class NilClass; def or(value) value end end
+
diff --git a/code/extensions/fileutils.rb b/code/extensions/fileutils.rb
new file mode 100644
index 00000000..701fcde7
--- /dev/null
+++ b/code/extensions/fileutils.rb
@@ -0,0 +1,1726 @@
+# 
+# = fileutils.rb
+# 
+# Copyright (c) 2000-2006 Minero Aoki
+# 
+# This program is free software.
+# You can distribute/modify this program under the same terms of ruby.
+# 
+# == module FileUtils
+# 
+# Namespace for several file utility methods for copying, moving, removing, etc.
+# 
+# === Module Functions
+# 
+#   cd(dir, options)
+#   cd(dir, options) {|dir| .... }
+#   pwd()
+#   mkdir(dir, options)
+#   mkdir(list, options)
+#   mkdir_p(dir, options)
+#   mkdir_p(list, options)
+#   rmdir(dir, options)
+#   rmdir(list, options)
+#   ln(old, new, options)
+#   ln(list, destdir, options)
+#   ln_s(old, new, options)
+#   ln_s(list, destdir, options)
+#   ln_sf(src, dest, options)
+#   cp(src, dest, options)
+#   cp(list, dir, options)
+#   cp_r(src, dest, options)
+#   cp_r(list, dir, options)
+#   mv(src, dest, options)
+#   mv(list, dir, options)
+#   rm(list, options)
+#   rm_r(list, options)
+#   rm_rf(list, options)
+#   install(src, dest, mode = <src's>, options)
+#   chmod(mode, list, options)
+#   chmod_R(mode, list, options)
+#   chown(user, group, list, options)
+#   chown_R(user, group, list, options)
+#   touch(list, options)
+#
+# The <tt>options</tt> parameter is a hash of options, taken from the list
+# <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
+# <tt>:noop</tt> means that no changes are made.  The other two are obvious.
+# Each method documents the options that it honours.
+#
+# All methods that have the concept of a "source" file or directory can take
+# either one file or a list of files in that argument.  See the method
+# documentation for examples.
+#
+# There are some `low level' methods, which do not accept any option:
+#
+#   copy_entry(src, dest, preserve = false, dereference = false)
+#   copy_file(src, dest, preserve = false, dereference = true)
+#   copy_stream(srcstream, deststream)
+#   remove_entry(path, force = false)
+#   remove_entry_secure(path, force = false)
+#   remove_file(path, force = false)
+#   compare_file(path_a, path_b)
+#   compare_stream(stream_a, stream_b)
+#   uptodate?(file, cmp_list)
+#
+# == module FileUtils::Verbose
+# 
+# This module has all methods of FileUtils module, but it outputs messages
+# before acting.  This equates to passing the <tt>:verbose</tt> flag to methods
+# in FileUtils.
+# 
+# == module FileUtils::NoWrite
+# 
+# This module has all methods of FileUtils module, but never changes
+# files/directories.  This equates to passing the <tt>:noop</tt> flag to methods
+# in FileUtils.
+# 
+# == module FileUtils::DryRun
+# 
+# This module has all methods of FileUtils module, but never changes
+# files/directories.  This equates to passing the <tt>:noop</tt> and
+# <tt>:verbose</tt> flags to methods in FileUtils.
+# 
+
+module FileUtils
+
+  def self.private_module_function(name)   #:nodoc:
+    module_function name
+    private_class_method name
+  end
+
+  # This hash table holds command options.
+  OPT_TABLE = {}   #:nodoc: internal use only
+
+  #
+  # Options: (none)
+  #
+  # Returns the name of the current directory.
+  #
+  def pwd
+    Dir.pwd
+  end
+  module_function :pwd
+
+  alias getwd pwd
+  module_function :getwd
+
+  #
+  # Options: verbose
+  # 
+  # Changes the current directory to the directory +dir+.
+  # 
+  # If this method is called with block, resumes to the old
+  # working directory after the block execution finished.
+  # 
+  #   FileUtils.cd('/', :verbose => true)   # chdir and report it
+  # 
+  def cd(dir, options = {}, &block) # :yield: dir
+    fu_check_options options, OPT_TABLE['cd']
+    fu_output_message "cd #{dir}" if options[:verbose]
+    Dir.chdir(dir, &block)
+    fu_output_message 'cd -' if options[:verbose] and block
+  end
+  module_function :cd
+
+  alias chdir cd
+  module_function :chdir
+
+  OPT_TABLE['cd']    =
+  OPT_TABLE['chdir'] = [:verbose]
+
+  #
+  # Options: (none)
+  # 
+  # Returns true if +newer+ is newer than all +old_list+.
+  # Non-existent files are older than any file.
+  # 
+  #   FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
+  #       system 'make hello.o'
+  # 
+  def uptodate?(new, old_list, options = nil)
+    raise ArgumentError, 'uptodate? does not accept any option' if options
+
+    return false unless File.exist?(new)
+    new_time = File.mtime(new)
+    old_list.each do |old|
+      if File.exist?(old)
+        return false unless new_time > File.mtime(old)
+      end
+    end
+    true
+  end
+  module_function :uptodate?
+
+  #
+  # Options: mode noop verbose
+  # 
+  # Creates one or more directories.
+  # 
+  #   FileUtils.mkdir 'test'
+  #   FileUtils.mkdir %w( tmp data )
+  #   FileUtils.mkdir 'notexist', :noop => true  # Does not really create.
+  #   FileUtils.mkdir 'tmp', :mode => 0700
+  # 
+  def mkdir(list, options = {})
+    fu_check_options options, OPT_TABLE['mkdir']
+    list = fu_list(list)
+    fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
+    return if options[:noop]
+
+    list.each do |dir|
+      fu_mkdir dir, options[:mode]
+    end
+  end
+  module_function :mkdir
+
+  OPT_TABLE['mkdir'] = [:mode, :noop, :verbose]
+
+  #
+  # Options: mode noop verbose
+  # 
+  # Creates a directory and all its parent directories.
+  # For example,
+  # 
+  #   FileUtils.mkdir_p '/usr/local/lib/ruby'
+  # 
+  # causes to make following directories, if it does not exist.
+  #     * /usr
+  #     * /usr/local
+  #     * /usr/local/lib
+  #     * /usr/local/lib/ruby
+  #
+  # You can pass several directories at a time in a list.
+  # 
+  def mkdir_p(list, options = {})
+    fu_check_options options, OPT_TABLE['mkdir_p']
+    list = fu_list(list)
+    fu_output_message "mkdir -p #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
+    return *list if options[:noop]
+
+    list.map {|path| path.sub(%r</\z>, '') }.each do |path|
+      # optimize for the most common case
+      begin
+        fu_mkdir path, options[:mode]
+        next
+      rescue SystemCallError
+        next if File.directory?(path)
+      end
+
+      stack = []
+      until path == stack.last   # dirname("/")=="/", dirname("C:/")=="C:/"
+        stack.push path
+        path = File.dirname(path)
+      end
+      stack.reverse_each do |path|
+        begin
+          fu_mkdir path, options[:mode]
+        rescue SystemCallError => err
+          raise unless File.directory?(path)
+        end
+      end
+    end
+
+    return *list
+  end
+  module_function :mkdir_p
+
+  alias mkpath    mkdir_p
+  alias makedirs  mkdir_p
+  module_function :mkpath
+  module_function :makedirs
+
+  OPT_TABLE['mkdir_p']  =
+  OPT_TABLE['mkpath']   =
+  OPT_TABLE['makedirs'] = [:mode, :noop, :verbose]
+
+  def fu_mkdir(path, mode)   #:nodoc:
+    path = path.sub(%r</\z>, '')
+    if mode
+      Dir.mkdir path, mode
+      File.chmod mode, path
+    else
+      Dir.mkdir path
+    end
+  end
+  private_module_function :fu_mkdir
+
+  #
+  # Options: noop, verbose
+  # 
+  # Removes one or more directories.
+  # 
+  #   FileUtils.rmdir 'somedir'
+  #   FileUtils.rmdir %w(somedir anydir otherdir)
+  #   # Does not really remove directory; outputs message.
+  #   FileUtils.rmdir 'somedir', :verbose => true, :noop => true
+  # 
+  def rmdir(list, options = {})
+    fu_check_options options, OPT_TABLE['rmdir']
+    list = fu_list(list)
+    fu_output_message "rmdir #{list.join ' '}" if options[:verbose]
+    return if options[:noop]
+    list.each do |dir|
+      Dir.rmdir dir.sub(%r</\z>, '')
+    end
+  end
+  module_function :rmdir
+
+  OPT_TABLE['rmdir'] = [:noop, :verbose]
+
+  #
+  # Options: force noop verbose
+  #
+  # <b><tt>ln(old, new, options = {})</tt></b>
+  #
+  # Creates a hard link +new+ which points to +old+.
+  # If +new+ already exists and it is a directory, creates a link +new/old+.
+  # If +new+ already exists and it is not a directory, raises Errno::EEXIST.
+  # But if :force option is set, overwrite +new+.
+  # 
+  #   FileUtils.ln 'gcc', 'cc', :verbose => true
+  #   FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
+  # 
+  # <b><tt>ln(list, destdir, options = {})</tt></b>
+  # 
+  # Creates several hard links in a directory, with each one pointing to the
+  # item in +list+.  If +destdir+ is not a directory, raises Errno::ENOTDIR.
+  # 
+  #   include FileUtils
+  #   cd '/sbin'
+  #   FileUtils.ln %w(cp mv mkdir), '/bin'   # Now /sbin/cp and /bin/cp are linked.
+  # 
+  def ln(src, dest, options = {})
+    fu_check_options options, OPT_TABLE['ln']
+    fu_output_message "ln#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+    return if options[:noop]
+    fu_each_src_dest0(src, dest) do |s,d|
+      remove_file d, true if options[:force]
+      File.link s, d
+    end
+  end
+  module_function :ln
+
+  alias link ln
+  module_function :link
+
+  OPT_TABLE['ln']   =
+  OPT_TABLE['link'] = [:force, :noop, :verbose]
+
+  #
+  # Options: force noop verbose
+  #
+  # <b><tt>ln_s(old, new, options = {})</tt></b>
+  # 
+  # Creates a symbolic link +new+ which points to +old+.  If +new+ already
+  # exists and it is a directory, creates a symbolic link +new/old+.  If +new+
+  # already exists and it is not a directory, raises Errno::EEXIST.  But if
+  # :force option is set, overwrite +new+.
+  # 
+  #   FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
+  #   FileUtils.ln_s 'verylongsourcefilename.c', 'c', :force => true
+  # 
+  # <b><tt>ln_s(list, destdir, options = {})</tt></b>
+  # 
+  # Creates several symbolic links in a directory, with each one pointing to the
+  # item in +list+.  If +destdir+ is not a directory, raises Errno::ENOTDIR.
+  #
+  # If +destdir+ is not a directory, raises Errno::ENOTDIR.
+  # 
+  #   FileUtils.ln_s Dir.glob('bin/*.rb'), '/home/aamine/bin'
+  # 
+  def ln_s(src, dest, options = {})
+    fu_check_options options, OPT_TABLE['ln_s']
+    fu_output_message "ln -s#{options[:force] ? 'f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+    return if options[:noop]
+    fu_each_src_dest0(src, dest) do |s,d|
+      remove_file d, true if options[:force]
+      File.symlink s, d
+    end
+  end
+  module_function :ln_s
+
+  alias symlink ln_s
+  module_function :symlink
+
+  OPT_TABLE['ln_s']    =
+  OPT_TABLE['symlink'] = [:force, :noop, :verbose]
+
+  #
+  # Options: noop verbose
+  # 
+  # Same as
+  #   #ln_s(src, dest, :force)
+  # 
+  def ln_sf(src, dest, options = {})
+    fu_check_options options, OPT_TABLE['ln_sf']
+    options = options.dup
+    options[:force] = true
+    ln_s src, dest, options
+  end
+  module_function :ln_sf
+
+  OPT_TABLE['ln_sf'] = [:noop, :verbose]
+
+  #
+  # Options: preserve noop verbose
+  #
+  # Copies a file content +src+ to +dest+.  If +dest+ is a directory,
+  # copies +src+ to +dest/src+.
+  #
+  # If +src+ is a list of files, then +dest+ must be a directory.
+  #
+  #   FileUtils.cp 'eval.c', 'eval.c.org'
+  #   FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
+  #   FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
+  #   FileUtils.cp 'symlink', 'dest'   # copy content, "dest" is not a symlink
+  # 
+  def cp(src, dest, options = {})
+    fu_check_options options, OPT_TABLE['cp']
+    fu_output_message "cp#{options[:preserve] ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+    return if options[:noop]
+    fu_each_src_dest(src, dest) do |s, d|
+      copy_file s, d, options[:preserve]
+    end
+  end
+  module_function :cp
+
+	# changed
+  def cp_in_bar(bar, src, dest, options = {})
+    fu_check_options options, OPT_TABLE['cp']
+    fu_output_message "cp#{options[:preserve] ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+    return if options[:noop]
+    fu_each_src_dest(src, dest) do |s, d|
+      copy_file_in_bar bar, s, d, options[:preserve]
+    end
+  end
+  module_function :cp_in_bar
+
+  alias copy cp
+  module_function :copy
+
+  OPT_TABLE['cp']   =
+  OPT_TABLE['copy'] = [:preserve, :noop, :verbose]
+
+  #
+  # Options: preserve noop verbose dereference_root remove_destination
+  # 
+  # Copies +src+ to +dest+. If +src+ is a directory, this method copies
+  # all its contents recursively. If +dest+ is a directory, copies
+  # +src+ to +dest/src+.
+  #
+  # +src+ can be a list of files.
+  # 
+  #   # Installing ruby library "mylib" under the site_ruby
+  #   FileUtils.rm_r site_ruby + '/mylib', :force
+  #   FileUtils.cp_r 'lib/', site_ruby + '/mylib'
+  # 
+  #   # Examples of copying several files to target directory.
+  #   FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
+  #   FileUtils.cp_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true
+  #
+  #   # If you want to copy all contents of a directory instead of the
+  #   # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
+  #   # use following code.
+  #   FileUtils.cp_r 'src/.', 'dest'     # cp_r('src', 'dest') makes src/dest,
+  #                                      # but this doesn't.
+  # 
+  def cp_r(src, dest, options = {})
+    fu_check_options options, OPT_TABLE['cp_r']
+    fu_output_message "cp -r#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+    return if options[:noop]
+    options[:dereference_root] = true unless options.key?(:dereference_root)
+    fu_each_src_dest(src, dest) do |s, d|
+      copy_entry s, d, options[:preserve], options[:dereference_root], options[:remove_destination]
+    end
+  end
+  module_function :cp_r
+
+	#changed
+  def cp_r_in_bar(bar, src, dest, options = {})
+    fu_check_options options, OPT_TABLE['cp_r']
+    fu_output_message "cp -r#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+    return if options[:noop]
+    options[:dereference_root] = true unless options.key?(:dereference_root)
+    fu_each_src_dest(src, dest) do |s, d|
+      copy_entry_in_bar bar, s, d, options[:preserve], options[:dereference_root], options[:remove_destination]
+    end
+  end
+  module_function :cp_r_in_bar
+
+  OPT_TABLE['cp_r'] = [:preserve, :noop, :verbose,
+                       :dereference_root, :remove_destination]
+
+  #
+  # Copies a file system entry +src+ to +dest+.
+  # If +src+ is a directory, this method copies its contents recursively.
+  # This method preserves file types, c.f. symlink, directory...
+  # (FIFO, device files and etc. are not supported yet)
+  #
+  # Both of +src+ and +dest+ must be a path name.
+  # +src+ must exist, +dest+ must not exist.
+  #
+  # If +preserve+ is true, this method preserves owner, group, permissions
+  # and modified time.
+  #
+  # If +dereference_root+ is true, this method dereference tree root.
+  #
+  # If +remove_destination+ is true, this method removes each destination file before copy.
+  #
+  def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
+    Entry_.new(src, nil, dereference_root).traverse do |ent|
+      destent = Entry_.new(dest, ent.rel, false)
+      File.unlink destent.path if remove_destination && File.file?(destent.path)
+      ent.copy destent.path
+      ent.copy_metadata destent.path if preserve
+    end
+  end
+  module_function :copy_entry
+
+	#changed
+  def copy_entry_in_bar(bar, src, dest, preserve = false, dereference_root = false, remove_destination = false)
+    Entry_.new(src, nil, dereference_root).traverse do |ent|
+      destent = Entry_.new(dest, ent.rel, false)
+      File.unlink destent.path if remove_destination && File.file?(destent.path)
+      ent.copy_in_bar bar, destent.path
+      ent.copy_metadata destent.path if preserve
+    end
+  end
+  module_function :copy_entry_in_bar
+
+  #
+  # Copies file contents of +src+ to +dest+.
+  # Both of +src+ and +dest+ must be a path name.
+  #
+  def copy_file(src, dest, preserve = false, dereference = true)
+    ent = Entry_.new(src, nil, dereference)
+    ent.copy_file dest
+    ent.copy_metadata dest if preserve
+  end
+  module_function :copy_file
+
+	# changed
+  def copy_file_in_bar(bar, src, dest, preserve = false, dereference = true)
+    ent = Entry_.new(src, nil, dereference)
+    ent.copy_file_in_bar bar, dest
+    ent.copy_metadata dest if preserve
+  end
+  module_function :copy_file_in_bar
+
+  #
+  # Copies stream +src+ to +dest+.
+  # +src+ must respond to #read(n) and
+  # +dest+ must respond to #write(str).
+  #
+  def copy_stream(src, dest)
+    fu_copy_stream0 src, dest, fu_stream_blksize(src, dest)
+  end
+  module_function :copy_stream
+
+  #
+  # Options: force noop verbose
+  # 
+  # Moves file(s) +src+ to +dest+.  If +file+ and +dest+ exist on the different
+  # disk partition, the file is copied instead.
+  # 
+  #   FileUtils.mv 'badname.rb', 'goodname.rb'
+  #   FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true  # no error
+  # 
+  #   FileUtils.mv %w(junk.txt dust.txt), '/home/aamine/.trash/'
+  #   FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
+  # 
+  def mv(src, dest, options = {})
+    fu_check_options options, OPT_TABLE['mv']
+    fu_output_message "mv#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+    return if options[:noop]
+    fu_each_src_dest(src, dest) do |s, d|
+      destent = Entry_.new(d, nil, true)
+      begin
+        if destent.exist?
+          if destent.directory?
+            raise Errno::EEXIST, dest
+          else
+            destent.remove_file if rename_cannot_overwrite_file?
+          end
+        end
+        begin
+          File.rename s, d
+        rescue Errno::EXDEV
+          copy_entry s, d, true
+          if options[:secure]
+            remove_entry_secure s, options[:force]
+          else
+            remove_entry s, options[:force]
+          end
+        end
+      rescue SystemCallError
+        raise unless options[:force]
+      end
+    end
+  end
+  module_function :mv
+
+  def mv_in_bar(bar, src, dest, options = {})
+    fu_check_options options, OPT_TABLE['mv']
+    fu_output_message "mv#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+    return if options[:noop]
+    fu_each_src_dest(src, dest) do |s, d|
+      destent = Entry_.new(d, nil, true)
+      begin
+        if destent.exist?
+          if destent.directory?
+            raise Errno::EEXIST, dest
+          else
+            destent.remove_file if rename_cannot_overwrite_file?
+          end
+        end
+        begin
+          File.rename s, d
+        rescue Errno::EXDEV
+          copy_entry_in_bar bar, s, d, true
+          if options[:secure]
+            remove_entry_secure s, options[:force]
+          else
+            remove_entry s, options[:force]
+          end
+        end
+      rescue SystemCallError
+        raise unless options[:force]
+      end
+    end
+  end
+  module_function :mv_in_bar
+
+  alias move mv
+  module_function :move
+
+  OPT_TABLE['mv']   =
+  OPT_TABLE['move'] = [:force, :noop, :verbose, :secure]
+
+  def rename_cannot_overwrite_file?   #:nodoc:
+    /djgpp|cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+  end
+  private_module_function :rename_cannot_overwrite_file?
+
+  #
+  # Options: force noop verbose
+  # 
+  # Remove file(s) specified in +list+.  This method cannot remove directories.
+  # All StandardErrors are ignored when the :force option is set.
+  # 
+  #   FileUtils.rm %w( junk.txt dust.txt )
+  #   FileUtils.rm Dir.glob('*.so')
+  #   FileUtils.rm 'NotExistFile', :force => true   # never raises exception
+  # 
+  def rm(list, options = {})
+    fu_check_options options, OPT_TABLE['rm']
+    list = fu_list(list)
+    fu_output_message "rm#{options[:force] ? ' -f' : ''} #{list.join ' '}" if options[:verbose]
+    return if options[:noop]
+
+    list.each do |path|
+      remove_file path, options[:force]
+    end
+  end
+  module_function :rm
+
+  alias remove rm
+  module_function :remove
+
+  OPT_TABLE['rm']     =
+  OPT_TABLE['remove'] = [:force, :noop, :verbose]
+
+  #
+  # Options: noop verbose
+  # 
+  # Equivalent to
+  #
+  #   #rm(list, :force => true)
+  #
+  def rm_f(list, options = {})
+    fu_check_options options, OPT_TABLE['rm_f']
+    options = options.dup
+    options[:force] = true
+    rm list, options
+  end
+  module_function :rm_f
+
+  alias safe_unlink rm_f
+  module_function :safe_unlink
+
+  OPT_TABLE['rm_f']        =
+  OPT_TABLE['safe_unlink'] = [:noop, :verbose]
+
+  #
+  # Options: force noop verbose secure
+  # 
+  # remove files +list+[0] +list+[1]... If +list+[n] is a directory,
+  # removes its all contents recursively. This method ignores
+  # StandardError when :force option is set.
+  # 
+  #   FileUtils.rm_r Dir.glob('/tmp/*')
+  #   FileUtils.rm_r '/', :force => true          #  :-)
+  #
+  # WARNING: This method causes local vulnerability
+  # if one of parent directories or removing directory tree are world
+  # writable (including /tmp, whose permission is 1777), and the current
+  # process has strong privilege such as Unix super user (root), and the
+  # system has symbolic link.  For secure removing, read the documentation
+  # of #remove_entry_secure carefully, and set :secure option to true.
+  # Default is :secure=>false.
+  #
+  # NOTE: This method calls #remove_entry_secure if :secure option is set.
+  # See also #remove_entry_secure.
+  # 
+  def rm_r(list, options = {})
+    fu_check_options options, OPT_TABLE['rm_r']
+    # options[:secure] = true unless options.key?(:secure)
+    list = fu_list(list)
+    fu_output_message "rm -r#{options[:force] ? 'f' : ''} #{list.join ' '}" if options[:verbose]
+    return if options[:noop]
+    list.each do |path|
+      if options[:secure]
+        remove_entry_secure path, options[:force]
+      else
+        remove_entry path, options[:force]
+      end
+    end
+  end
+  module_function :rm_r
+
+  OPT_TABLE['rm_r'] = [:force, :noop, :verbose, :secure]
+
+  #
+  # Options: noop verbose secure
+  # 
+  # Equivalent to
+  #
+  #   #rm_r(list, :force => true)
+  #
+  # WARNING: This method causes local vulnerability.
+  # Read the documentation of #rm_r first.
+  # 
+  def rm_rf(list, options = {})
+    fu_check_options options, OPT_TABLE['rm_rf']
+    options = options.dup
+    options[:force] = true
+    rm_r list, options
+  end
+  module_function :rm_rf
+
+  alias rmtree rm_rf
+  module_function :rmtree
+
+  OPT_TABLE['rm_rf']  =
+  OPT_TABLE['rmtree'] = [:noop, :verbose, :secure]
+
+  #
+  # This method removes a file system entry +path+.  +path+ shall be a
+  # regular file, a directory, or something.  If +path+ is a directory,
+  # remove it recursively.  This method is required to avoid TOCTTOU
+  # (time-of-check-to-time-of-use) local security vulnerability of #rm_r.
+  # #rm_r causes security hole when:
+  #
+  #   * Parent directory is world writable (including /tmp).
+  #   * Removing directory tree includes world writable directory.
+  #   * The system has symbolic link.
+  #
+  # To avoid this security hole, this method applies special preprocess.
+  # If +path+ is a directory, this method chown(2) and chmod(2) all
+  # removing directories.  This requires the current process is the
+  # owner of the removing whole directory tree, or is the super user (root).
+  #
+  # WARNING: You must ensure that *ALL* parent directories are not
+  # world writable.  Otherwise this method does not work.
+  # Only exception is temporary directory like /tmp and /var/tmp,
+  # whose permission is 1777.
+  #
+  # WARNING: Only the owner of the removing directory tree, or Unix super
+  # user (root) should invoke this method.  Otherwise this method does not
+  # work.
+  #
+  # For details of this security vulnerability, see Perl's case:
+  #
+  #   http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
+  #   http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
+  #
+  # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
+  #
+  def remove_entry_secure(path, force = false)
+    unless fu_have_symlink?
+      remove_entry path, force
+      return
+    end
+    fullpath = File.expand_path(path)
+    st = File.lstat(fullpath)
+    unless st.directory?
+      File.unlink fullpath
+      return
+    end
+    # is a directory.
+    parent_st = File.stat(File.dirname(fullpath))
+    unless fu_world_writable?(parent_st)
+      remove_entry path, force
+      return
+    end
+    unless parent_st.sticky?
+      raise ArgumentError, "parent directory is world writable, FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
+    end
+    # freeze tree root
+    euid = Process.euid
+    File.open(fullpath + '/.') {|f|
+      unless fu_stat_identical_entry?(st, f.stat)
+        # symlink (TOC-to-TOU attack?)
+        File.unlink fullpath
+        return
+      end
+      f.chown euid, -1
+      f.chmod 0700
+    }
+    # ---- tree root is frozen ----
+    root = Entry_.new(path)
+    root.preorder_traverse do |ent|
+      if ent.directory?
+        ent.chown euid, -1
+        ent.chmod 0700
+      end
+    end
+    root.postorder_traverse do |ent|
+      begin
+        ent.remove
+      rescue
+        raise unless force
+      end
+    end
+  rescue
+    raise unless force
+  end
+  module_function :remove_entry_secure
+
+  def fu_world_writable?(st)
+    (st.mode & 0002) != 0
+  end
+  private_module_function :fu_world_writable?
+
+  def fu_have_symlink?   #:nodoc
+    File.symlink nil, nil
+  rescue NotImplementedError
+    return false
+  rescue
+    return true
+  end
+  private_module_function :fu_have_symlink?
+
+  def fu_stat_identical_entry?(a, b)   #:nodoc:
+    a.dev == b.dev and a.ino == b.ino
+  end
+  private_module_function :fu_stat_identical_entry?
+
+  #
+  # This method removes a file system entry +path+.
+  # +path+ might be a regular file, a directory, or something.
+  # If +path+ is a directory, remove it recursively.
+  #
+  # See also #remove_entry_secure.
+  #
+  def remove_entry(path, force = false)
+    Entry_.new(path).postorder_traverse do |ent|
+      begin
+        ent.remove
+      rescue
+        raise unless force
+      end
+    end
+  rescue
+    raise unless force
+  end
+  module_function :remove_entry
+
+  #
+  # Removes a file +path+.
+  # This method ignores StandardError if +force+ is true.
+  #
+  def remove_file(path, force = false)
+    Entry_.new(path).remove_file
+  rescue
+    raise unless force
+  end
+  module_function :remove_file
+
+  #
+  # Removes a directory +dir+ and its contents recursively.
+  # This method ignores StandardError if +force+ is true.
+  #
+  def remove_dir(path, force = false)
+    remove_entry path, force   # FIXME?? check if it is a directory
+  end
+  module_function :remove_dir
+
+  #
+  # Returns true if the contents of a file A and a file B are identical.
+  # 
+  #   FileUtils.compare_file('somefile', 'somefile')  #=> true
+  #   FileUtils.compare_file('/bin/cp', '/bin/mv')    #=> maybe false
+  #
+  def compare_file(a, b)
+    return false unless File.size(a) == File.size(b)
+    File.open(a, 'rb') {|fa|
+      File.open(b, 'rb') {|fb|
+        return compare_stream(fa, fb)
+      }
+    }
+  end
+  module_function :compare_file
+
+  alias identical? compare_file
+  alias cmp compare_file
+  module_function :identical?
+  module_function :cmp
+
+  #
+  # Returns true if the contents of a stream +a+ and +b+ are identical.
+  #
+  def compare_stream(a, b)
+    bsize = fu_stream_blksize(a, b)
+    sa = sb = nil
+    while sa == sb
+      sa = a.read(bsize)
+      sb = b.read(bsize)
+      unless sa and sb
+        if sa.nil? and sb.nil?
+          return true
+        end
+      end
+    end
+    false
+  end
+  module_function :compare_stream
+
+  #
+  # Options: mode preserve noop verbose
+  # 
+  # If +src+ is not same as +dest+, copies it and changes the permission
+  # mode to +mode+.  If +dest+ is a directory, destination is +dest+/+src+.
+  # This method removes destination before copy.
+  # 
+  #   FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true
+  #   FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
+  # 
+  def install(src, dest, options = {})
+    fu_check_options options, OPT_TABLE['install']
+    fu_output_message "install -c#{options[:preserve] && ' -p'}#{options[:mode] ? (' -m 0%o' % options[:mode]) : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
+    return if options[:noop]
+    fu_each_src_dest(src, dest) do |s, d|
+      unless File.exist?(d) and compare_file(s, d)
+        remove_file d, true
+        st = File.stat(s) if options[:preserve]
+        copy_file s, d
+        File.utime st.atime, st.mtime, d if options[:preserve]
+        File.chmod options[:mode], d if options[:mode]
+      end
+    end
+  end
+  module_function :install
+
+  OPT_TABLE['install'] = [:mode, :preserve, :noop, :verbose]
+
+  #
+  # Options: noop verbose
+  # 
+  # Changes permission bits on the named files (in +list+) to the bit pattern
+  # represented by +mode+.
+  # 
+  #   FileUtils.chmod 0755, 'somecommand'
+  #   FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
+  #   FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
+  # 
+  def chmod(mode, list, options = {})
+    fu_check_options options, OPT_TABLE['chmod']
+    list = fu_list(list)
+    fu_output_message sprintf('chmod %o %s', mode, list.join(' ')) if options[:verbose]
+    return if options[:noop]
+    list.each do |path|
+      Entry_.new(path).chmod mode
+    end
+  end
+  module_function :chmod
+
+  OPT_TABLE['chmod'] = [:noop, :verbose]
+
+  #
+  # Options: noop verbose force
+  # 
+  # Changes permission bits on the named files (in +list+)
+  # to the bit pattern represented by +mode+.
+  # 
+  #   FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
+  # 
+  def chmod_R(mode, list, options = {})
+    fu_check_options options, OPT_TABLE['chmod_R']
+    list = fu_list(list)
+    fu_output_message sprintf('chmod -R%s %o %s',
+                              (options[:force] ? 'f' : ''),
+                              mode, list.join(' ')) if options[:verbose]
+    return if options[:noop]
+    list.each do |root|
+      Entry_.new(root).traverse do |ent|
+        begin
+          ent.chmod mode
+        rescue
+          raise unless options[:force]
+        end
+      end
+    end
+  end
+  module_function :chmod_R
+
+  OPT_TABLE['chmod_R'] = [:noop, :verbose, :force]
+
+  #
+  # Options: noop verbose
+  # 
+  # Changes owner and group on the named files (in +list+)
+  # to the user +user+ and the group +group+.  +user+ and +group+
+  # may be an ID (Integer/String) or a name (String).
+  # If +user+ or +group+ is nil, this method does not change
+  # the attribute.
+  # 
+  #   FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
+  #   FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
+  # 
+  def chown(user, group, list, options = {})
+    fu_check_options options, OPT_TABLE['chown']
+    list = fu_list(list)
+    fu_output_message sprintf('chown %s%s',
+                              [user,group].compact.join(':') + ' ',
+                              list.join(' ')) if options[:verbose]
+    return if options[:noop]
+    uid = fu_get_uid(user)
+    gid = fu_get_gid(group)
+    list.each do |path|
+      Entry_.new(path).chown uid, gid
+    end
+  end
+  module_function :chown
+
+  OPT_TABLE['chown'] = [:noop, :verbose]
+
+  #
+  # Options: noop verbose force
+  # 
+  # Changes owner and group on the named files (in +list+)
+  # to the user +user+ and the group +group+ recursively.
+  # +user+ and +group+ may be an ID (Integer/String) or
+  # a name (String).  If +user+ or +group+ is nil, this
+  # method does not change the attribute.
+  # 
+  #   FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
+  #   FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true
+  # 
+  def chown_R(user, group, list, options = {})
+    fu_check_options options, OPT_TABLE['chown_R']
+    list = fu_list(list)
+    fu_output_message sprintf('chown -R%s %s%s',
+                              (options[:force] ? 'f' : ''),
+                              [user,group].compact.join(':') + ' ',
+                              list.join(' ')) if options[:verbose]
+    return if options[:noop]
+    uid = fu_get_uid(user)
+    gid = fu_get_gid(group)
+    return unless uid or gid
+    list.each do |root|
+      Entry_.new(root).traverse do |ent|
+        begin
+          ent.chown uid, gid
+        rescue
+          raise unless options[:force]
+        end
+      end
+    end
+  end
+  module_function :chown_R
+
+  OPT_TABLE['chown_R'] = [:noop, :verbose, :force]
+
+  begin
+    require 'etc'
+
+    def fu_get_uid(user)   #:nodoc:
+      return nil unless user
+      user = user.to_s
+      if /\A\d+\z/ =~ user
+      then user.to_i
+      else Etc.getpwnam(user).uid
+      end
+    end
+    private_module_function :fu_get_uid
+
+    def fu_get_gid(group)   #:nodoc:
+      return nil unless group
+      if /\A\d+\z/ =~ group
+      then group.to_i
+      else Etc.getgrnam(group).gid
+      end
+    end
+    private_module_function :fu_get_gid
+
+  rescue LoadError
+    # need Win32 support???
+
+    def fu_get_uid(user)   #:nodoc:
+      user    # FIXME
+    end
+    private_module_function :fu_get_uid
+
+    def fu_get_gid(group)   #:nodoc:
+      group   # FIXME
+    end
+    private_module_function :fu_get_gid
+  end
+
+  #
+  # Options: noop verbose
+  # 
+  # Updates modification time (mtime) and access time (atime) of file(s) in
+  # +list+.  Files are created if they don't exist.
+  # 
+  #   FileUtils.touch 'timestamp'
+  #   FileUtils.touch Dir.glob('*.c');  system 'make'
+  # 
+  def touch(list, options = {})
+    fu_check_options options, OPT_TABLE['touch']
+    list = fu_list(list)
+    created = nocreate = options[:nocreate]
+    t = options[:mtime]
+    if options[:verbose]
+      fu_output_message "touch #{nocreate ? ' -c' : ''}#{t ? t.strftime(' -t %Y%m%d%H%M.%S') : ''}#{list.join ' '}"
+    end
+    return if options[:noop]
+    list.each do |path|
+      created = nocreate
+      begin
+        File.utime(t, t, path)
+      rescue Errno::ENOENT
+        raise if created
+        File.open(path, 'a') {
+          ;
+        }
+        created = true
+        retry if t
+      end
+    end
+  end
+  module_function :touch
+
+  OPT_TABLE['touch'] = [:noop, :verbose, :mtime, :nocreate]
+
+  private
+
+  module StreamUtils_
+    private
+
+    def fu_windows?
+      /mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM
+    end
+
+		# changed
+    def fu_copy_stream0_in_bar(bar, src, dest, blksize)   #:nodoc:
+			report = false
+			if File.size?(src)
+				report = true
+				ticks = File.size(src) / blksize
+				bar.max = ticks
+				bar.counter = i = MutableNumber.new(0)
+				log('.')
+			end
+			txt = "cp #{File.basename(src.path)} ..."
+			bar.set_text_prefix(txt)
+
+      while s = src.read(blksize)
+        dest.write s
+				i.add 1
+      end
+    end
+
+    def fu_copy_stream0(src, dest, blksize)   #:nodoc:
+      # FIXME: readpartial?
+      while s = src.read(blksize)
+        dest.write s
+      end
+    end
+
+    def fu_stream_blksize(*streams)
+      streams.each do |s|
+        next unless s.respond_to?(:stat)
+        size = fu_blksize(s.stat)
+        return size if size
+      end
+      fu_default_blksize()
+    end
+
+    def fu_blksize(st)
+      s = st.blksize
+      return nil unless s
+      return nil if s == 0
+      s
+    end
+
+    def fu_default_blksize
+      1024
+    end
+  end
+
+  include StreamUtils_
+  extend StreamUtils_
+
+  class Entry_   #:nodoc: internal use only
+    include StreamUtils_
+
+    def initialize(a, b = nil, deref = false)
+      @prefix = @rel = @path = nil
+      if b
+        @prefix = a
+        @rel = b
+      else
+        @path = a
+      end
+      @deref = deref
+      @stat = nil
+      @lstat = nil
+    end
+
+    def inspect
+      "\#<#{self.class} #{path()}>"
+    end
+
+    def path
+      if @path
+        @path.to_str
+      else
+        join(@prefix, @rel)
+      end
+    end
+
+    def prefix
+      @prefix || @path
+    end
+
+    def rel
+      @rel
+    end
+
+    def dereference?
+      @deref
+    end
+
+    def exist?
+      lstat! ? true : false
+    end
+
+    def file?
+      s = lstat!
+      s and s.file?
+    end
+
+    def directory?
+      s = lstat!
+      s and s.directory?
+    end
+
+    def symlink?
+      s = lstat!
+      s and s.symlink?
+    end
+
+    def chardev?
+      s = lstat!
+      s and s.chardev?
+    end
+
+    def blockdev?
+      s = lstat!
+      s and s.blockdev?
+    end
+
+    def socket?
+      s = lstat!
+      s and s.socket?
+    end
+
+    def pipe?
+      s = lstat!
+      s and s.pipe?
+    end
+
+    S_IF_DOOR = 0xD000
+
+    def door?
+      s = lstat!
+      s and (s.mode & 0xF000 == S_IF_DOOR)
+    end
+
+    def entries
+      Dir.entries(path())\
+          .reject {|n| n == '.' or n == '..' }\
+          .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) }
+    end
+
+    def stat
+      return @stat if @stat
+      if lstat() and lstat().symlink?
+        @stat = File.stat(path())
+      else
+        @stat = lstat()
+      end
+      @stat
+    end
+
+    def stat!
+      return @stat if @stat
+      if lstat! and lstat!.symlink?
+        @stat = File.stat(path())
+      else
+        @stat = lstat!
+      end
+      @stat
+    rescue SystemCallError
+      nil
+    end
+
+    def lstat
+      if dereference?
+        @lstat ||= File.stat(path())
+      else
+        @lstat ||= File.lstat(path())
+      end
+    end
+
+    def lstat!
+      lstat()
+    rescue SystemCallError
+      nil
+    end
+
+    def chmod(mode)
+      if symlink?
+        File.lchmod mode, path() if have_lchmod?
+      else
+        File.chmod mode, path()
+      end
+    end
+
+    def chown(uid, gid)
+      if symlink?
+        File.lchown uid, gid, path() if have_lchown?
+      else
+        File.chown uid, gid, path()
+      end
+    end
+
+		#changed
+    def copy_in_bar(bar, dest)
+      case
+      when file?
+        copy_file_in_bar bar, dest
+      when directory?
+        begin
+          Dir.mkdir dest
+        rescue
+          raise unless File.directory?(dest)
+        end
+      when symlink?
+        File.symlink File.readlink(path()), dest
+      when chardev?
+        raise "cannot handle device file" unless File.respond_to?(:mknod)
+        mknod dest, ?c, 0666, lstat().rdev
+      when blockdev?
+        raise "cannot handle device file" unless File.respond_to?(:mknod)
+        mknod dest, ?b, 0666, lstat().rdev
+      when socket?
+        raise "cannot handle socket" unless File.respond_to?(:mknod)
+        mknod dest, nil, lstat().mode, 0
+      when pipe?
+        raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
+        mkfifo dest, 0666
+      when door?
+        raise "cannot handle door: #{path()}"
+      else
+        raise "unknown file type: #{path()}"
+      end
+    end
+
+    def copy(dest)
+      case
+      when file?
+        copy_file dest
+      when directory?
+        begin
+          Dir.mkdir dest
+        rescue
+          raise unless File.directory?(dest)
+        end
+      when symlink?
+        File.symlink File.readlink(path()), dest
+      when chardev?
+        raise "cannot handle device file" unless File.respond_to?(:mknod)
+        mknod dest, ?c, 0666, lstat().rdev
+      when blockdev?
+        raise "cannot handle device file" unless File.respond_to?(:mknod)
+        mknod dest, ?b, 0666, lstat().rdev
+      when socket?
+        raise "cannot handle socket" unless File.respond_to?(:mknod)
+        mknod dest, nil, lstat().mode, 0
+      when pipe?
+        raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
+        mkfifo dest, 0666
+      when door?
+        raise "cannot handle door: #{path()}"
+      else
+        raise "unknown file type: #{path()}"
+      end
+    end
+
+    def copy_file(dest)
+      st = stat()
+      File.open(path(),  'rb') {|r|
+        File.open(dest, 'wb', st.mode) {|w|
+          fu_copy_stream0 r, w, (fu_blksize(st) || fu_default_blksize())
+        }
+      }
+    end
+
+		# changed
+    def copy_file_in_bar(bar, dest)
+      st = stat()
+      File.open(path(),  'rb') {|r|
+        File.open(dest, 'wb', st.mode) {|w|
+          fu_copy_stream0_in_bar bar, r, w, (fu_blksize(st) || fu_default_blksize())
+        }
+      }
+    end
+
+    def copy_metadata(path)
+      st = lstat()
+      File.utime st.atime, st.mtime, path
+      begin
+        File.chown st.uid, st.gid, path
+      rescue Errno::EPERM
+        # clear setuid/setgid
+        File.chmod st.mode & 01777, path
+      else
+        File.chmod st.mode, path
+      end
+    end
+
+    def remove
+      if directory?
+        remove_dir1
+      else
+        remove_file
+      end
+    end
+
+    def remove_dir1
+      platform_support {
+        Dir.rmdir path().sub(%r</\z>, '')
+      }
+    end
+
+    def remove_file
+      platform_support {
+        File.unlink path
+      }
+    end
+
+    def platform_support
+      return yield unless fu_windows?
+      first_time_p = true
+      begin
+        yield
+      rescue Errno::ENOENT
+        raise
+      rescue => err
+        if first_time_p
+          first_time_p = false
+          begin
+            File.chmod 0700, path()   # Windows does not have symlink
+            retry
+          rescue SystemCallError
+          end
+        end
+        raise err
+      end
+    end
+
+    def preorder_traverse
+      stack = [self]
+      while ent = stack.pop
+        yield ent
+        stack.concat ent.entries.reverse if ent.directory?
+      end
+    end
+
+    alias traverse preorder_traverse
+
+    def postorder_traverse
+      if directory?
+        entries().each do |ent|
+          ent.postorder_traverse do |e|
+            yield e
+          end
+        end
+      end
+      yield self
+    end
+
+    private
+
+    $fileutils_rb_have_lchmod = nil
+
+    def have_lchmod?
+      # This is not MT-safe, but it does not matter.
+      if $fileutils_rb_have_lchmod == nil
+        $fileutils_rb_have_lchmod = check_have_lchmod?
+      end
+      $fileutils_rb_have_lchmod
+    end
+
+    def check_have_lchmod?
+      return false unless File.respond_to?(:lchmod)
+      File.lchmod 0
+      return true
+    rescue NotImplementedError
+      return false
+    end
+
+    $fileutils_rb_have_lchown = nil
+
+    def have_lchown?
+      # This is not MT-safe, but it does not matter.
+      if $fileutils_rb_have_lchown == nil
+        $fileutils_rb_have_lchown = check_have_lchown?
+      end
+      $fileutils_rb_have_lchown
+    end
+
+    def check_have_lchown?
+      return false unless File.respond_to?(:lchown)
+      File.lchown nil, nil
+      return true
+    rescue NotImplementedError
+      return false
+    end
+
+    def join(dir, base)
+      return dir.to_str if not base or base == '.'
+      return base.to_str if not dir or dir == '.'
+      File.join(dir, base)
+    end
+  end   # class Entry_
+
+  def fu_list(arg)   #:nodoc:
+    [arg].flatten.map {|path| path.to_str }
+  end
+  private_module_function :fu_list
+
+  def fu_each_src_dest(src, dest)   #:nodoc:
+    fu_each_src_dest0(src, dest) do |s, d|
+      raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d)
+      yield s, d
+    end
+  end
+  private_module_function :fu_each_src_dest
+
+  def fu_each_src_dest0(src, dest)   #:nodoc:
+    if src.is_a?(Array)
+      src.each do |s|
+        s = s.to_str
+        yield s, File.join(dest, File.basename(s))
+      end
+    else
+      src = src.to_str
+      if File.directory?(dest)
+        yield src, File.join(dest, File.basename(src))
+      else
+        yield src, dest.to_str
+      end
+    end
+  end
+  private_module_function :fu_each_src_dest0
+
+  def fu_same?(a, b)   #:nodoc:
+    if fu_have_st_ino?
+      st1 = File.stat(a)
+      st2 = File.stat(b)
+      st1.dev == st2.dev and st1.ino == st2.ino
+    else
+      File.expand_path(a) == File.expand_path(b)
+    end
+  rescue Errno::ENOENT
+    return false
+  end
+  private_module_function :fu_same?
+
+  def fu_have_st_ino?   #:nodoc:
+    not fu_windows?
+  end
+  private_module_function :fu_have_st_ino?
+
+  def fu_check_options(options, optdecl)   #:nodoc:
+    h = options.dup
+    optdecl.each do |opt|
+      h.delete opt
+    end
+    raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
+  end
+  private_module_function :fu_check_options
+
+  def fu_update_option(args, new)   #:nodoc:
+    if args.last.is_a?(Hash)
+      args[-1] = args.last.dup.update(new)
+    else
+      args.push new
+    end
+    args
+  end
+  private_module_function :fu_update_option
+
+  @fileutils_output = $stderr
+  @fileutils_label  = ''
+
+  def fu_output_message(msg)   #:nodoc:
+    @fileutils_output ||= $stderr
+    @fileutils_label  ||= ''
+    @fileutils_output.puts @fileutils_label + msg
+  end
+  private_module_function :fu_output_message
+
+  #
+  # Returns an Array of method names which have any options.
+  #
+  #   p FileUtils.commands  #=> ["chmod", "cp", "cp_r", "install", ...]
+  #
+  def FileUtils.commands
+    OPT_TABLE.keys
+  end
+
+  #
+  # Returns an Array of option names.
+  #
+  #   p FileUtils.options  #=> ["noop", "force", "verbose", "preserve", "mode"]
+  #
+  def FileUtils.options
+    OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
+  end
+
+  #
+  # Returns true if the method +mid+ have an option +opt+.
+  #
+  #   p FileUtils.have_option?(:cp, :noop)     #=> true
+  #   p FileUtils.have_option?(:rm, :force)    #=> true
+  #   p FileUtils.have_option?(:rm, :perserve) #=> false
+  #
+  def FileUtils.have_option?(mid, opt)
+    li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
+    li.include?(opt)
+  end
+
+  #
+  # Returns an Array of option names of the method +mid+.
+  #
+  #   p FileUtils.options(:rm)  #=> ["noop", "verbose", "force"]
+  #
+  def FileUtils.options_of(mid)
+    OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
+  end
+
+  #
+  # Returns an Array of method names which have the option +opt+.
+  #
+  #   p FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
+  #
+  def FileUtils.collect_method(opt)
+    OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
+  end
+
+  METHODS = singleton_methods() - %w( private_module_function
+      commands options have_option? options_of collect_method )
+
+  # 
+  # This module has all methods of FileUtils module, but it outputs messages
+  # before acting.  This equates to passing the <tt>:verbose</tt> flag to
+  # methods in FileUtils.
+  # 
+  module Verbose
+    include FileUtils
+    @fileutils_output  = $stderr
+    @fileutils_label   = ''
+    ::FileUtils.collect_method(:verbose).each do |name|
+      module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+        def #{name}(*args)
+          super(*fu_update_option(args, :verbose => true))
+        end
+        private :#{name}
+      EOS
+    end
+    extend self
+    class << self
+      ::FileUtils::METHODS.each do |m|
+        public m
+      end
+    end
+  end
+
+  # 
+  # This module has all methods of FileUtils module, but never changes
+  # files/directories.  This equates to passing the <tt>:noop</tt> flag
+  # to methods in FileUtils.
+  # 
+  module NoWrite
+    include FileUtils
+    @fileutils_output  = $stderr
+    @fileutils_label   = ''
+    ::FileUtils.collect_method(:noop).each do |name|
+      module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+        def #{name}(*args)
+          super(*fu_update_option(args, :noop => true))
+        end
+        private :#{name}
+      EOS
+    end
+    extend self
+    class << self
+      ::FileUtils::METHODS.each do |m|
+        public m
+      end
+    end
+  end
+
+  # 
+  # This module has all methods of FileUtils module, but never changes
+  # files/directories, with printing message before acting.
+  # This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag
+  # to methods in FileUtils.
+  # 
+  module DryRun
+    include FileUtils
+    @fileutils_output  = $stderr
+    @fileutils_label   = ''
+    ::FileUtils.collect_method(:noop).each do |name|
+      module_eval(<<-EOS, __FILE__, __LINE__ + 1)
+        def #{name}(*args)
+          super(*fu_update_option(args, :noop => true, :verbose => true))
+        end
+        private :#{name}
+      EOS
+    end
+    extend self
+    class << self
+      ::FileUtils::METHODS.each do |m|
+        public m
+      end
+    end
+  end
+end
+
+# vi: sts=2 ts=2 sw=2
diff --git a/code/fm.rb b/code/fm.rb
index e5b66aad..d6c8cb85 100644
--- a/code/fm.rb
+++ b/code/fm.rb
@@ -9,6 +9,9 @@ OPTIONS = {
 
 
 module Fm
+	SCHEDULER_PRIORITY = -1
+	COPY_PRIORITY = -2
+
 	SCHEDULED = []
 	COLUMNS = 4
 	UPDATE_SIGNAL = 31
@@ -53,9 +56,26 @@ module Fm
 			draw
 		end
 
+#		for i in 1..20
+#			eval "Signal.trap(#{i}) do
+#				log #{i}
+#				exit if #{i} == 9 end"
+#		end
+
+		boot_up(pwd)
+	end
+
+	attr_reader(:dirs, :pwd)
+
+	def self.pwd() @pwd end
 
+	def self.boot_up(pwd=nil)
+		pwd ||= @pwd.path || Dir.getwd
 		# This thread inspects directories
 		@scheduler_active = false
+		if defined? @scheduler and Thread === @scheduler
+			@scheduler.kill
+		end
 		@scheduler = Thread.new do
 			while true
 				Thread.stop
@@ -70,11 +90,12 @@ module Fm
 				end
 			end
 		end
+		@scheduler.priority = SCHEDULER_PRIORITY
 
 
 		@dirs = Hash.new() do |hash, key|
 			hash[key] = newdir = Directory.new(key)
-			schedule newdir
+#			newdir.schedule
 			newdir
 		end
 
@@ -85,8 +106,6 @@ module Fm
 		@scheduler.run
 	end
 
-	attr_reader(:dirs, :pwd)
-
 	def self.force_update
 		# Send a signal to this process
 		Process.kill(UPDATE_SIGNAL, PID)
@@ -110,6 +129,7 @@ module Fm
 	end
 
 	def self.main_loop
+		bool = false
 		while true
 			if @pwd.size == 0 or @pwd.pos < 0
 				@pwd.pos = 0
@@ -119,17 +139,24 @@ module Fm
 
 			begin
 #				@mutex.synchronize {
+					log "drawing"
 					draw()
 #				}
 			rescue Interrupt
 				on_interrupt
 			rescue Exception
-#				log($!)
-#				log(caller)
+				log($!)
+				log(caller)
 			end
 
 			begin
-				key = geti
+#				unless bool
+#					bool = true
+					key = geti
+#				else
+#					key = geti
+#					key = 'j'
+#				end
 #				@mutex.synchronize {
 					press(key)
 #				}
@@ -141,6 +168,8 @@ module Fm
 
 	def self.current_path() @pwd.path end
 
+	def self.reset_title() set_title("fm: #{@pwd.path}") end
+
 	def self.enter_dir_safely(dir)
 		dir = File.expand_path(dir)
 		if File.exists?(dir) and File.directory?(dir)
@@ -149,6 +178,9 @@ module Fm
 				enter_dir(dir)
 				return true
 			rescue
+				log("NIGGER" * 100)
+				log($!)
+				log(caller)
 				enter_dir(olddir)
 				return false
 			end
@@ -157,7 +189,7 @@ module Fm
 
 	def self.enter_dir(dir)
 		@pwd.restore if @pwd
-		@marks = 0
+		@marked = []
 		dir = File.expand_path(dir)
 
 		oldpath = @path.dup
@@ -176,12 +208,13 @@ module Fm
 		end
 
 		@pwd = @path.last
+		@pwd.pos = @pwd.pos
 
 		@pwd.files_raw.dup.each do |x|
 			@dirs[x] if File.directory?(x)
 		end
 
-		set_title "fm: #{@pwd.path}"
+		reset_title()
 
 		if @path.size < oldpath.size
 			@pwd.pos = @pwd.files_raw.index(oldpath.last.path) || 0
@@ -200,8 +233,16 @@ module Fm
 	end
 
 	def self.currentfile() @pwd.files[@pwd.pos] end
+	def self.selection()
+		if @marked.empty?
+			[currentfile]
+		else
+			@marked.dup
+		end
+	end
 
 	def self.schedule(dir)
+		dir.scheduled = true
 		SCHEDULED << dir
 		@scheduler.run
 	end
diff --git a/code/keys.rb b/code/keys.rb
index d6ae01ea..edcbab53 100644
--- a/code/keys.rb
+++ b/code/keys.rb
@@ -2,11 +2,13 @@ module Fm
 	# ALL combinations of multiple keys (but without the last letter)
 	# or regexps which match combinations need to be in here!
 	COMBS = %w(
-		g d y c Z delet cu
-		t S ? ?g ?f
+		g d df y c Z delet cu
+		ter ta S ? ?g ?f :q
 
-		/[m`']/ /[fF/!].*/
+		/[m`']/ /[fF/!q].*/
+		/[ri]\d*\w*[^ri]/
 		/(cw|cd|mv).*/
+		/b(l(o(c(k(.*)?)?)?)?)?/
 		/m(k(d(i(r(.*)?)?)?)?)?/
 		/r(e(n(a(m(e(.*)?)?)?)?)?)?/
 	)
@@ -54,6 +56,16 @@ module Fm
 		end
 	end
 
+	def self.find_newest()
+		newest = nil
+		for f in @pwd.files
+			if newest.nil? or newest.ctime < f.ctime
+				newest = f
+			end
+		end
+		@pwd.pointed_file = newest.path
+	end
+
 	def self.hints(str)
 		begin
 			rx = Regexp.new(str, Regexp::IGNORECASE)
@@ -70,6 +82,7 @@ module Fm
 			g = File.basename(f)
 			if g =~ rx
 				unless pointed
+					log "point at #{f}"
 					@pwd.pointed_file = f
 					pointed = true
 				end
@@ -99,6 +112,8 @@ module Fm
 			else
 				@buffer.slice! -1
 			end
+		elsif key == '<c-u>'
+			@buffer = ''
 		else
 			@buffer << key
 		end
@@ -123,26 +138,59 @@ module Fm
 			starti
 
 
-		when /S(.)/
+		when /^S(.)$/
 			OPTIONS['sort_reverse'] = $1.ord.between?(65, 90)
 
 			case $1
 			when 'n'
 				OPTIONS['sort'] = :name
+			when 'e'
+				OPTIONS['sort'] = :ext
+			when 't'
+				OPTIONS['sort'] = :type
 			when 's'
 				OPTIONS['sort'] = :size
-			when 'e'
-				OPTIONS['sort'] = :extension
 			when 'm'
 				OPTIONS['sort'] = :mtime
-			when 'c', 't'
+			when 'c'
 				OPTIONS['sort'] = :ctime
 			end
 			@pwd.schedule
 
-		when 'r', 'R'
+		when 'tar'
+			closei
+			system('tar', 'cvvf', 'pack.tar', *selection.map{|x| x.basename})
+			@pwd.refresh!
+			starti
+
+		when 'R'
 			@pwd.refresh!
 
+		when '@', '.'
+			if defined? @record
+				@buffer = ''
+				memo = ''
+				@record.each_char do |c|
+					if memo.empty?
+						if c == '<'
+							memo << c
+						else
+							press c
+						end
+					else
+						memo << c
+						if c == '>'
+							press memo
+							memo.clear
+						end
+					end
+				end
+			end
+			
+		when /^q.+q$/
+			@record = @buffer[1...-1]
+			@buffer = ''
+
 		when 'x'
 			@bars.first.kill unless @bars.empty?
 
@@ -156,23 +204,19 @@ module Fm
 			@pwd.pos -= lines/2
 
 		when 'cp', 'yy'
-			if @marked.empty?
-				@copy = [currentfile]
-			else
-				@copy = @marked.dup
-			end
+			@copy = selection
 			@cut = false
 
 		when 'cut'
-			if @marked.empty?
-				@copy = [currentfile]
-			else
-				@copy = @marked.dup
-			end
+			@copy = selection
 			@cut = true
 
 		when 'n'
-			search(@search_string, 1)
+			if @search_string.empty?
+				find_newest
+			else
+				search(@search_string, 1)
+			end
 
 		when 'N'
 			search(@search_string, 0, true)
@@ -206,6 +250,9 @@ module Fm
 				end
 			end
 
+		when 'A'
+			@buffer = "cw #{currentfile.name}"
+
 		when /^f(.+)$/
 			str = $1
 			if str =~ /^\s?(.*)(L|;|<cr>|<esc>)$/
@@ -245,6 +292,9 @@ module Fm
 					@pwd.schedule
 				end
 			end
+
+		when /^block.*stop$/
+			@buffer = ''
 			
 		when /^!(.+)$/
 			str = $1
@@ -269,14 +319,41 @@ module Fm
 				end
 			end
 
-		when /^(?:mv|cw|rename)(.+)$/
-			str = $1
+		when /^(mv|cw|rename)(.+)$/
+			str = $2
+			if $1 == 'mv'
+				if str =~ /['`"]([\w\d])/
+					if path = @memory[$1]
+						str = ''
+						@buffer.clear
+						if File.exists?(path) and File.directory?(path)
+							Action.move(selection, path)
+						end
+					end
+				end
+			end
+			log str
 			if str =~ /^\s?(.*)(<cr>|<esc>)$/
 				@buffer = ''
 				if $2 == '<cr>'
-					Action.move(currentfile, $1)
+					files = selection
+					if files.size == 1
+						fn = $1
+						log "!!! #{fn}"
+						unless fn.include? '.'
+							if ext = files.first.basename.from_last('.')
+								fn << ".#{ext}"
+							end
+							log "??? #{ext}"
+						end
+						Action.move(files, fn)
+						@pwd.refresh!
+						@pwd.find_file(fn)
+					else
+						Action.move(files, $1)
+						@pwd.refresh!
+					end
 				end
-				@pwd.schedule
 			end
 
 		when 'tc'
@@ -294,7 +371,7 @@ module Fm
 			@pwd.schedule
 
 		when 'delete'
-			files = @marked.empty? ? [currentfile] : @marked
+			files = selection
 			@marked = []
 			for f in files
 				if f and f.exists? and f.dir?
@@ -310,6 +387,10 @@ module Fm
 			else
 				Action.copy(@copy, @pwd.path)
 			end
+			@pwd.refresh!
+			if @copy.size == 1
+				@pwd.find_file(@copy[0].basename)
+			end
 
 		when /^[`'](.)$/
 			if dir = @memory[$1] and not @pwd.path == dir
@@ -317,12 +398,22 @@ module Fm
 				enter_dir_safely(dir)
 			end
 
-		when '<tab>'
+		when '<s-tab>'
 			if dir = @memory['`'] and not @pwd.path == dir
 				remember_dir
 				enter_dir_safely(dir)
 			end
 			
+		when '<tab>'
+			if dir = @memory['9'] and dir != '/'
+				unless @pwd.path == dir
+					enter_dir_safely(dir)
+				end
+			elsif dir = @memory['`'] and not @pwd.path == dir
+				remember_dir
+				enter_dir_safely(dir)
+			end
+			
 
 		when /^m(.)$/
 			@memory[$1] = @pwd.path
@@ -369,13 +460,16 @@ module Fm
 			end
 			@pwd.schedule
 
-		when 'dD'
+		when 'dD', 'dfd'
 			cf = currentfile
 			if cf and cf.exists?
 				cf.delete!
 				@pwd.schedule
 			end
 
+		when 'term'
+			fork do exec 'x-terminal-emulator' end
+
 		when 'g0'
 			remember_dir
 			enter_dir('/')
@@ -418,31 +512,93 @@ module Fm
 			end
 
 		when '<cr>', 'l', ';', 'L', '<right>'
-			ascend(@buffer=='L')
+			ascend(@buffer=='L', @buffer=='l')
+
+		# a = run all
+		# d or e = detach
+		# t = run in a terminal
+		# w = wait for <enter> after execution
+		# capital letter inverts
+		when /^[ri](\d*)([adetw]*)[ri]$/
+			if $2.empty?
+				f = @marked.empty?? currentfile : @marked.first
+				flags = get_default_flags(f)
+			else
+				flags = $2
+			end
+			opt = OpenStruct.new
+			opt.newway = true
+
+			opt.mode = $1.to_i unless $1.empty?
+
+			# Set options based on flags
+			
+			if flags =~ /a/
+				opt.all = true
+			end
+			if flags =~ /[de]/
+				opt.detach = true
+			end
+			if flags =~ /t/
+				opt.new_term = true
+				opt.detach = true
+			end
+			if flags =~ /w/
+				opt.wait = true
+			end
 
-		when 'q', 'ZZ', "\004"
+			if flags =~ /A/
+				opt.all = false
+			end
+			if flags =~ /[DE]/
+				opt.detach = false
+			end
+			if flags =~ /T/
+				opt.new_term = false
+			end
+			if flags =~ /W/
+				opt.wait = false
+			end
+
+			Action.run(opt.__table__)
+		
+#		when 'ra'
+#			unless File.directory?(currentfile.path)
+#				Action.run(:all=>true)
+#			end
+
+		when 'ZZ', '<c-d>', ':q<cr>'
 			exit
+			
+		when '<c-r>'
+			Fm.boot_up
+
+		when "-", "="
+			val = "2#{key=='-' ? '-' : '+'}"
+			system("amixer", "-q", "set", "PCM", val, "unmute")
+
+		else
+#			log key.ord
 
 		end
 
 		@buffer = '' unless @buffer == '' or @buffer =~ REGX
 	end
 	
-	def self.ascend(wait = false)
-		cf = currentfile
-		enter = enter_dir_safely(cf.path)
-		unless cf.nil? or enter
-			handler, wait = getfilehandler(currentfile)
-			if handler
-				closei
-				log handler
-				system(handler)
-				gets if wait
-				starti
-				return true
+	def self.ascend(wait = false, all=false)
+		if all and !@marked.empty?
+			closei
+			system(*['mplayer', '-fs', *@marked.map{|x| x.path}])
+			starti
+			return true
+		else
+			cf = currentfile
+			enter = enter_dir_safely(cf.path)
+			unless enter
+				return Action.run(:detach=>false)
 			end
+			return false
 		end
-		return false
 	end
 
 	def self.descend
diff --git a/code/screensaver/clock.rb b/code/screensaver/clock.rb
new file mode 100644
index 00000000..6289c809
--- /dev/null
+++ b/code/screensaver/clock.rb
@@ -0,0 +1,10 @@
+def screensaver
+	cleari
+
+	str = Time.now.to_s
+	s = Fm.cols - str.size
+	s = 1 if s < 0
+
+	puti((Fm.lines.to_f/2).floor, s/2, str)
+#	puti(rand(Fm.lines), rand(Fm.cols), 'MEDITONSIN')
+end
diff --git a/code/types.rb b/code/types.rb
index 0fc0bf7c..96d67d06 100644
--- a/code/types.rb
+++ b/code/types.rb
@@ -1,5 +1,96 @@
 module Fm
-	def self.getfilehandler_frompath(file)
+	MIMETYPES = Marshal.load(File.read(
+		File.join(FM_DIR, 'data', 'mime.dat')))
+
+	def self.get_default_flags(file)
+		case file.mimetype
+		when /^(?:image|video)\//; 'd'
+		when 'application/pdf'; 'd'
+		else '' end
+	end
+
+	def self.filehandler(files, hash)
+		str = files.map{|x| x.sh}.join(' ')
+		type = files.first.mimetype
+		name = files.first.basename
+#		mode = hash.mode
+
+		use = lambda do |sym|
+			hash.exec = App.send(sym, hash, name, str, files)
+		end
+
+		case type
+		when /^(video|audio)\//
+			use.call :mplayer
+		when "application/pdf"
+			use.call :evince
+		when /^(image)\//
+			use.call :image
+		else
+			case name
+			when /\.(swc|smc)/
+				use.call :zsnes
+			end
+		end
+
+		return hash
+	end
+
+	module App
+		def image(hash, name, str, file)
+			case hash.mode
+			when 4; "feh --bg-scale #{str}"
+			when 5; "feh --bg-tile #{str}"
+			when 6; "feh --bg-center #{str}"
+			when 2; "gimp #{str}"
+			when 1; "feh -F #{str}"
+			else "feh #{str}"
+			end
+		end
+		def evince(hash, name, str, file)
+			"evince #{str}"
+		end
+		def mplayer(*args)
+			hash = args[0] = args[0].dup
+			str = args[2]
+
+			if hash.detach
+				flags = '-msglevel all=-1'
+			else
+				flags = ''
+			end
+
+			case hash.mode
+			when nil
+				if name =~ /720p/
+					hash.mode = 1
+				else
+					hash.mode = 0
+				end
+				mplayer(*args)
+			when 0
+				return "mplayer #{flags} -fs -sid 0 #{str}"
+			when 1
+				return "mplayer #{flags} -vm sdl -sid 0 #{str}"
+			end
+		end
+		def zsnes(hash, name, str, files)
+			case hash.mode
+			when 1
+				return "zsnes -ad sdl -o #{str}"
+			else
+				return "zsnes -ad sdl -u -o #{str}"
+			end
+		end
+
+		module_function *%w*
+			mplayer zsnes evince image
+		*
+	end
+
+	def self.getfilehandler_frompath(*files)
+		file = files.first
+		n = files.size
 		case file
 		when /\.(part|avi|mpe?[g\d]|flv|fid|mkv|mov|wm[av]|vob|php|divx?|og[gmv])$/i
 			if file =~ /720p/
@@ -8,6 +99,12 @@ module Fm
 				return "mplayer -fs #{file.sh}", false
 			end
 
+		when /\.java$/
+			return "javac #{file.sh}", true
+
+		when /\.class$/
+			return log "java #{file.sh.before_last('.')}"
+
 		when /\.part$/
 			test = getfilehandler_frompath($`)
 			if test
@@ -23,10 +120,10 @@ module Fm
 		when "Makefile"
 			return "make"
 
-		when /\.(jpe?g|png)$/i
+		when /\.(jpe?g|png|gif)$/i
 			return "feh #{file.sh}", false
 
-		when /\.html?$/i
+		when /\.(html?|swf)$/i
 			return "firefox #{file.sh}"
 
 		when /\.pdf$/i
@@ -39,7 +136,8 @@ module Fm
 			return "aplay -q #{file.sh}"
 
 		when /\.m3u$/i
-			return "cmus-remote -c && cmus-remote -P #{file} && cmus-remote -C 'set play_library=false' && sleep 0.3 && cmus-remote -n", false
+			return "/home/hut/bin/loadplaylist #{file.sh}"
+#			return "cmus-remote -c && cmus-remote -P #{file} && cmus-remote -C 'set play_library=false' && sleep 0.3 && cmus-remote -n", false
 
 		end
 
diff --git a/data/generate.rb b/data/generate.rb
new file mode 100755
index 00000000..c6038b3a
--- /dev/null
+++ b/data/generate.rb
@@ -0,0 +1,21 @@
+#!/usr/bin/ruby
+
+file = File.read(ARGV[0] || "mime.types")
+
+table = {}
+for l in file.lines
+	next if l[0] == ?#
+	next unless l.size > 3
+	next unless l.include? ?\t
+
+	left, *exts = l.split(/\s+/)
+#	print exts.inspect
+	for ext in exts
+		table[ext] = left
+	end
+end
+
+File.open('mime.dat', 'w') do |f|
+	f.write Marshal.dump(table)
+end
+
diff --git a/data/mime.dat b/data/mime.dat
new file mode 100644
index 00000000..78b0de8c
--- /dev/null
+++ b/data/mime.dat
Binary files differdiff --git a/data/mime.types b/data/mime.types
new file mode 100644
index 00000000..866db2c2
--- /dev/null
+++ b/data/mime.types
@@ -0,0 +1,769 @@
+###############################################################################
+#
+#  MIME-TYPES and the extensions that represent them
+#
+#  This file is part of the "mime-support" package.  Please send email (not a
+#  bug report) to mime-support@packages.debian.org if you would like new types
+#  and/or extensions to be added.
+#
+#  The reason that all types are managed by the mime-support package instead
+#  allowing individual packages to install types in much the same way as they
+#  add entries in to the mailcap file is so these types can be referenced by
+#  other programs (such as a web server) even if the specific support package
+#  for that type is not installed.
+#
+#  Users can add their own types if they wish by creating a ".mime.types"
+#  file in their home directory.  Definitions included there will take
+#  precedence over those listed here.
+#
+#  Note: Compression schemes like "gzip", "bzip", and "compress" are not
+#  actually "mime-types".  They are "encodings" and hence must _not_ have
+#  entries in this file to map their extensions.  The "mime-type" of an
+#  encoded file refers to the type of data that has been encoded, not the
+#  type of encoding.
+#
+###############################################################################
+
+
+application/activemessage
+application/andrew-inset			ez
+application/annodex				anx
+application/applefile
+application/atom+xml				atom
+application/atomcat+xml				atomcat
+application/atomserv+xml			atomsrv
+application/atomicmail
+application/batch-SMTP
+application/beep+xml
+application/bbolin				lin
+application/cals-1840
+application/cap					cap pcap
+application/commonground
+application/cu-seeme				cu
+application/cybercash
+application/davmount+xml			davmount
+application/dca-rft
+application/dec-dx
+application/docbook+xml
+application/dsptype				tsp
+application/dvcs
+application/ecmascript				es
+application/edi-consent
+application/edi-x12
+application/edifact
+application/eshop
+application/font-tdpfr
+application/futuresplash			spl
+application/ghostview
+application/hta					hta
+application/http
+application/hyperstudio
+application/iges
+application/index
+application/index.cmd
+application/index.obj
+application/index.response
+application/index.vnd
+application/iotp
+application/ipp
+application/isup
+application/java-archive			jar
+application/java-serialized-object		ser
+application/java-vm				class
+application/javascript				js
+application/m3g					m3g
+application/mac-binhex40			hqx
+application/mac-compactpro			cpt
+application/macwriteii
+application/marc
+application/mathematica				nb nbp
+application/ms-tnef
+application/msaccess				mdb
+application/msword				doc dot
+application/news-message-id
+application/news-transmission
+application/ocsp-request
+application/ocsp-response
+application/octet-stream			bin
+application/oda					oda
+application/ogg					ogx
+application/parityfec
+application/pdf					pdf
+application/pgp-encrypted
+application/pgp-keys				key
+application/pgp-signature			pgp
+application/pics-rules				prf
+application/pkcs10
+application/pkcs7-mime
+application/pkcs7-signature
+application/pkix-cert
+application/pkix-crl
+application/pkixcmp
+application/postscript				ps ai eps espi epsf eps2 eps3
+application/prs.alvestrand.titrax-sheet
+application/prs.cww
+application/prs.nprend
+application/qsig
+application/rar					rar
+application/rdf+xml				rdf
+application/remote-printing
+application/riscos
+application/rss+xml				rss
+application/rtf					rtf
+application/sdp
+application/set-payment
+application/set-payment-initiation
+application/set-registration
+application/set-registration-initiation
+application/sgml
+application/sgml-open-catalog
+application/sieve
+application/slate
+application/smil				smi smil
+application/timestamp-query
+application/timestamp-reply
+application/vemmi
+application/whoispp-query
+application/whoispp-response
+application/wita
+application/x400-bp
+application/xhtml+xml				xhtml xht
+application/xml					xml xsl xsd
+application/xml-dtd
+application/xml-external-parsed-entity
+application/xspf+xml				xspf
+application/zip					zip
+application/vnd.3M.Post-it-Notes
+application/vnd.accpac.simply.aso
+application/vnd.accpac.simply.imp
+application/vnd.acucobol
+application/vnd.aether.imp
+application/vnd.anser-web-certificate-issue-initiation
+application/vnd.anser-web-funds-transfer-initiation
+application/vnd.audiograph
+application/vnd.bmi
+application/vnd.businessobjects
+application/vnd.canon-cpdl
+application/vnd.canon-lips
+application/vnd.cinderella			cdy
+application/vnd.claymore
+application/vnd.commerce-battelle
+application/vnd.commonspace
+application/vnd.comsocaller
+application/vnd.contact.cmsg
+application/vnd.cosmocaller
+application/vnd.ctc-posml
+application/vnd.cups-postscript
+application/vnd.cups-raster
+application/vnd.cups-raw
+application/vnd.cybank
+application/vnd.dna
+application/vnd.dpgraph
+application/vnd.dxr
+application/vnd.ecdis-update
+application/vnd.ecowin.chart
+application/vnd.ecowin.filerequest
+application/vnd.ecowin.fileupdate
+application/vnd.ecowin.series
+application/vnd.ecowin.seriesrequest
+application/vnd.ecowin.seriesupdate
+application/vnd.enliven
+application/vnd.epson.esf
+application/vnd.epson.msf
+application/vnd.epson.quickanime
+application/vnd.epson.salt
+application/vnd.epson.ssf
+application/vnd.ericsson.quickcall
+application/vnd.eudora.data
+application/vnd.fdf
+application/vnd.ffsns
+application/vnd.flographit
+application/vnd.framemaker
+application/vnd.fsc.weblaunch
+application/vnd.fujitsu.oasys
+application/vnd.fujitsu.oasys2
+application/vnd.fujitsu.oasys3
+application/vnd.fujitsu.oasysgp
+application/vnd.fujitsu.oasysprs
+application/vnd.fujixerox.ddd
+application/vnd.fujixerox.docuworks
+application/vnd.fujixerox.docuworks.binder
+application/vnd.fut-misnet
+application/vnd.google-earth.kml+xml		kml
+application/vnd.google-earth.kmz		kmz
+application/vnd.grafeq
+application/vnd.groove-account
+application/vnd.groove-identity-message
+application/vnd.groove-injector
+application/vnd.groove-tool-message
+application/vnd.groove-tool-template
+application/vnd.groove-vcard
+application/vnd.hhe.lesson-player
+application/vnd.hp-HPGL
+application/vnd.hp-PCL
+application/vnd.hp-PCLXL
+application/vnd.hp-hpid
+application/vnd.hp-hps
+application/vnd.httphone
+application/vnd.hzn-3d-crossword
+application/vnd.ibm.MiniPay
+application/vnd.ibm.afplinedata
+application/vnd.ibm.modcap
+application/vnd.informix-visionary
+application/vnd.intercon.formnet
+application/vnd.intertrust.digibox
+application/vnd.intertrust.nncp
+application/vnd.intu.qbo
+application/vnd.intu.qfx
+application/vnd.irepository.package+xml
+application/vnd.is-xpr
+application/vnd.japannet-directory-service
+application/vnd.japannet-jpnstore-wakeup
+application/vnd.japannet-payment-wakeup
+application/vnd.japannet-registration
+application/vnd.japannet-registration-wakeup
+application/vnd.japannet-setstore-wakeup
+application/vnd.japannet-verification
+application/vnd.japannet-verification-wakeup
+application/vnd.koan
+application/vnd.lotus-1-2-3
+application/vnd.lotus-approach
+application/vnd.lotus-freelance
+application/vnd.lotus-notes
+application/vnd.lotus-organizer
+application/vnd.lotus-screencam
+application/vnd.lotus-wordpro
+application/vnd.mcd
+application/vnd.mediastation.cdkey
+application/vnd.meridian-slingshot
+application/vnd.mif
+application/vnd.minisoft-hp3000-save
+application/vnd.mitsubishi.misty-guard.trustweb
+application/vnd.mobius.daf
+application/vnd.mobius.dis
+application/vnd.mobius.msl
+application/vnd.mobius.plc
+application/vnd.mobius.txf
+application/vnd.motorola.flexsuite
+application/vnd.motorola.flexsuite.adsi
+application/vnd.motorola.flexsuite.fis
+application/vnd.motorola.flexsuite.gotap
+application/vnd.motorola.flexsuite.kmr
+application/vnd.motorola.flexsuite.ttc
+application/vnd.motorola.flexsuite.wem
+application/vnd.mozilla.xul+xml			xul
+application/vnd.ms-artgalry
+application/vnd.ms-asf
+application/vnd.ms-excel			xls xlb xlt
+application/vnd.ms-lrm
+application/vnd.ms-pki.seccat			cat
+application/vnd.ms-pki.stl			stl
+application/vnd.ms-powerpoint			ppt pps
+application/vnd.ms-project
+application/vnd.ms-tnef
+application/vnd.ms-works
+application/vnd.mseq
+application/vnd.msign
+application/vnd.music-niff
+application/vnd.musician
+application/vnd.netfpx
+application/vnd.noblenet-directory
+application/vnd.noblenet-sealer
+application/vnd.noblenet-web
+application/vnd.novadigm.EDM
+application/vnd.novadigm.EDX
+application/vnd.novadigm.EXT
+application/vnd.oasis.opendocument.chart			odc
+application/vnd.oasis.opendocument.database			odb
+application/vnd.oasis.opendocument.formula			odf
+application/vnd.oasis.opendocument.graphics			odg
+application/vnd.oasis.opendocument.graphics-template		otg
+application/vnd.oasis.opendocument.image			odi
+application/vnd.oasis.opendocument.presentation			odp
+application/vnd.oasis.opendocument.presentation-template	otp
+application/vnd.oasis.opendocument.spreadsheet			ods
+application/vnd.oasis.opendocument.spreadsheet-template		ots
+application/vnd.oasis.opendocument.text				odt
+application/vnd.oasis.opendocument.text-master			odm
+application/vnd.oasis.opendocument.text-template		ott
+application/vnd.oasis.opendocument.text-web			oth
+application/vnd.osa.netdeploy
+application/vnd.palm
+application/vnd.pg.format
+application/vnd.pg.osasli
+application/vnd.powerbuilder6
+application/vnd.powerbuilder6-s
+application/vnd.powerbuilder7
+application/vnd.powerbuilder7-s
+application/vnd.powerbuilder75
+application/vnd.powerbuilder75-s
+application/vnd.previewsystems.box
+application/vnd.publishare-delta-tree
+application/vnd.pvi.ptid1
+application/vnd.pwg-xhtml-print+xml
+application/vnd.rapid
+application/vnd.rim.cod				cod
+application/vnd.s3sms
+application/vnd.seemail
+application/vnd.shana.informed.formdata
+application/vnd.shana.informed.formtemplate
+application/vnd.shana.informed.interchange
+application/vnd.shana.informed.package
+application/vnd.smaf				mmf
+application/vnd.sss-cod
+application/vnd.sss-dtf
+application/vnd.sss-ntf
+application/vnd.stardivision.calc		sdc
+application/vnd.stardivision.chart		sds
+application/vnd.stardivision.draw		sda
+application/vnd.stardivision.impress		sdd
+application/vnd.stardivision.math		sdf
+application/vnd.stardivision.writer		sdw
+application/vnd.stardivision.writer-global	sgl
+application/vnd.street-stream
+application/vnd.sun.xml.calc			sxc
+application/vnd.sun.xml.calc.template		stc
+application/vnd.sun.xml.draw			sxd
+application/vnd.sun.xml.draw.template		std
+application/vnd.sun.xml.impress			sxi
+application/vnd.sun.xml.impress.template	sti
+application/vnd.sun.xml.math			sxm
+application/vnd.sun.xml.writer			sxw
+application/vnd.sun.xml.writer.global		sxg
+application/vnd.sun.xml.writer.template		stw
+application/vnd.svd
+application/vnd.swiftview-ics
+application/vnd.symbian.install			sis
+application/vnd.triscape.mxs
+application/vnd.trueapp
+application/vnd.truedoc
+application/vnd.tve-trigger
+application/vnd.ufdl
+application/vnd.uplanet.alert
+application/vnd.uplanet.alert-wbxml
+application/vnd.uplanet.bearer-choice
+application/vnd.uplanet.bearer-choice-wbxml
+application/vnd.uplanet.cacheop
+application/vnd.uplanet.cacheop-wbxml
+application/vnd.uplanet.channel
+application/vnd.uplanet.channel-wbxml
+application/vnd.uplanet.list
+application/vnd.uplanet.list-wbxml
+application/vnd.uplanet.listcmd
+application/vnd.uplanet.listcmd-wbxml
+application/vnd.uplanet.signal
+application/vnd.vcx
+application/vnd.vectorworks
+application/vnd.vidsoft.vidconference
+application/vnd.visio				vsd
+application/vnd.vividence.scriptfile
+application/vnd.wap.sic
+application/vnd.wap.slc
+application/vnd.wap.wbxml			wbxml
+application/vnd.wap.wmlc			wmlc
+application/vnd.wap.wmlscriptc			wmlsc
+application/vnd.webturbo
+application/vnd.wordperfect			wpd
+application/vnd.wordperfect5.1			wp5
+application/vnd.wrq-hp3000-labelled
+application/vnd.wt.stf
+application/vnd.xara
+application/vnd.xfdl
+application/vnd.yellowriver-custom-menu
+application/x-123				wk
+application/x-7z-compressed			7z
+application/x-abiword				abw
+application/x-apple-diskimage			dmg
+application/x-bcpio				bcpio
+application/x-bittorrent			torrent
+application/x-cab				cab
+application/x-cbr				cbr
+application/x-cbz				cbz
+application/x-cdf				cdf cda
+application/x-cdlink				vcd
+application/x-chess-pgn				pgn
+application/x-core
+application/x-cpio				cpio
+application/x-csh				csh
+application/x-debian-package			deb udeb
+application/x-director				dcr dir dxr
+application/x-dms				dms
+application/x-doom				wad
+application/x-dvi				dvi
+application/x-httpd-eruby			rhtml
+application/x-executable
+application/x-font				pfa pfb gsf pcf pcf.Z
+application/x-freemind				mm
+application/x-futuresplash			spl
+application/x-gnumeric				gnumeric
+application/x-go-sgf				sgf
+application/x-graphing-calculator		gcf
+application/x-gtar				gtar tgz taz
+application/x-hdf				hdf
+application/x-httpd-php				phtml pht php
+application/x-httpd-php-source			phps
+application/x-httpd-php3			php3
+application/x-httpd-php3-preprocessed		php3p
+application/x-httpd-php4			php4
+application/x-ica				ica
+application/x-info				info
+application/x-internet-signup			ins isp
+application/x-iphone				iii
+application/x-iso9660-image			iso
+application/x-jam				jam
+application/x-java-applet
+application/x-java-bean
+application/x-java-jnlp-file			jnlp
+application/x-jmol				jmz
+application/x-kchart				chrt
+application/x-kdelnk
+application/x-killustrator			kil
+application/x-koan				skp skd skt skm
+application/x-kpresenter			kpr kpt
+application/x-kspread				ksp
+application/x-kword				kwd kwt
+application/x-latex				latex
+application/x-lha				lha
+application/x-lyx				lyx
+application/x-lzh				lzh
+application/x-lzx				lzx
+application/x-maker				frm maker frame fm fb book fbdoc
+application/x-mif				mif
+application/x-ms-wmd				wmd
+application/x-ms-wmz				wmz
+application/x-msdos-program			com exe bat dll
+application/x-msi				msi
+application/x-netcdf				nc
+application/x-ns-proxy-autoconfig		pac dat
+application/x-nwc				nwc
+application/x-object				o
+application/x-oz-application			oza
+application/x-pkcs7-certreqresp			p7r
+application/x-pkcs7-crl				crl
+application/x-python-code			pyc pyo
+application/x-qgis				qgs shp shx
+application/x-quicktimeplayer			qtl
+application/x-redhat-package-manager		rpm
+application/x-ruby				rb
+application/x-rx
+application/x-sh				sh
+application/x-shar				shar
+application/x-shellscript
+application/x-shockwave-flash			swf swfl
+application/x-stuffit				sit sitx
+application/x-sv4cpio				sv4cpio
+application/x-sv4crc				sv4crc
+application/x-tar				tar
+application/x-tcl				tcl
+application/x-tex-gf				gf
+application/x-tex-pk				pk
+application/x-texinfo				texinfo texi
+application/x-trash				~ % bak old sik
+application/x-troff				t tr roff
+application/x-troff-man				man
+application/x-troff-me				me
+application/x-troff-ms				ms
+application/x-ustar				ustar
+application/x-videolan
+application/x-wais-source			src
+application/x-wingz				wz
+application/x-x509-ca-cert			crt
+application/x-xcf				xcf
+application/x-xfig				fig
+application/x-xpinstall				xpi
+
+audio/32kadpcm
+audio/3gpp
+audio/amr					amr
+audio/amr-wb					awb
+audio/amr					amr
+audio/amr-wb					awb
+audio/annodex					axa
+audio/basic					au snd
+audio/flac					flac
+audio/g.722.1
+audio/l16
+audio/midi					mid midi kar
+audio/mp4a-latm
+audio/mpa-robust
+audio/mpeg					mpga mpega mp2 mp3 m4a
+audio/mpegurl					m3u
+audio/ogg					oga ogg spx
+audio/parityfec
+audio/prs.sid					sid
+audio/telephone-event
+audio/tone
+audio/vnd.cisco.nse
+audio/vnd.cns.anp1
+audio/vnd.cns.inf1
+audio/vnd.digital-winds
+audio/vnd.everad.plj
+audio/vnd.lucent.voice
+audio/vnd.nortel.vbk
+audio/vnd.nuera.ecelp4800
+audio/vnd.nuera.ecelp7470
+audio/vnd.nuera.ecelp9600
+audio/vnd.octel.sbc
+audio/vnd.qcelp
+audio/vnd.rhetorex.32kadpcm
+audio/vnd.vmx.cvsd
+audio/x-aiff					aif aiff aifc
+audio/x-gsm					gsm
+audio/x-mpegurl					m3u
+audio/x-ms-wma					wma
+audio/x-ms-wax					wax
+audio/x-pn-realaudio-plugin
+audio/x-pn-realaudio				ra rm ram
+audio/x-realaudio				ra
+audio/x-scpls					pls
+audio/x-sd2					sd2
+audio/x-wav					wav
+
+chemical/x-alchemy				alc
+chemical/x-cache				cac cache
+chemical/x-cache-csf				csf
+chemical/x-cactvs-binary			cbin cascii ctab
+chemical/x-cdx					cdx
+chemical/x-cerius				cer
+chemical/x-chem3d				c3d
+chemical/x-chemdraw				chm
+chemical/x-cif					cif
+chemical/x-cmdf					cmdf
+chemical/x-cml					cml
+chemical/x-compass				cpa
+chemical/x-crossfire				bsd
+chemical/x-csml					csml csm
+chemical/x-ctx					ctx
+chemical/x-cxf					cxf cef
+#chemical/x-daylight-smiles			smi
+chemical/x-embl-dl-nucleotide			emb embl
+chemical/x-galactic-spc				spc
+chemical/x-gamess-input				inp gam gamin
+chemical/x-gaussian-checkpoint			fch fchk
+chemical/x-gaussian-cube			cub
+chemical/x-gaussian-input			gau gjc gjf
+chemical/x-gaussian-log				gal
+chemical/x-gcg8-sequence			gcg
+chemical/x-genbank				gen
+chemical/x-hin					hin
+chemical/x-isostar				istr ist
+chemical/x-jcamp-dx				jdx dx
+chemical/x-kinemage				kin
+chemical/x-macmolecule				mcm
+chemical/x-macromodel-input			mmd mmod
+chemical/x-mdl-molfile				mol
+chemical/x-mdl-rdfile				rd
+chemical/x-mdl-rxnfile				rxn
+chemical/x-mdl-sdfile				sd sdf
+chemical/x-mdl-tgf				tgf
+#chemical/x-mif					mif
+chemical/x-mmcif				mcif
+chemical/x-mol2					mol2
+chemical/x-molconn-Z				b
+chemical/x-mopac-graph				gpt
+chemical/x-mopac-input				mop mopcrt mpc zmt
+chemical/x-mopac-out				moo
+chemical/x-mopac-vib				mvb
+chemical/x-ncbi-asn1				asn
+chemical/x-ncbi-asn1-ascii			prt ent
+chemical/x-ncbi-asn1-binary			val aso
+chemical/x-ncbi-asn1-spec			asn
+chemical/x-pdb					pdb ent
+chemical/x-rosdal				ros
+chemical/x-swissprot				sw
+chemical/x-vamas-iso14976			vms
+chemical/x-vmd					vmd
+chemical/x-xtel					xtel
+chemical/x-xyz					xyz
+
+image/cgm
+image/g3fax
+image/gif					gif
+image/ief					ief
+image/jpeg					jpeg jpg jpe
+image/naplps
+image/pcx					pcx
+image/png					png
+image/prs.btif
+image/prs.pti
+image/svg+xml					svg svgz
+image/tiff					tiff tif
+image/vnd.cns.inf2
+image/vnd.djvu					djvu djv
+image/vnd.dwg
+image/vnd.dxf
+image/vnd.fastbidsheet
+image/vnd.fpx
+image/vnd.fst
+image/vnd.fujixerox.edmics-mmr
+image/vnd.fujixerox.edmics-rlc
+image/vnd.mix
+image/vnd.net-fpx
+image/vnd.svf
+image/vnd.wap.wbmp				wbmp
+image/vnd.xiff
+image/x-cmu-raster				ras
+image/x-coreldraw				cdr
+image/x-coreldrawpattern			pat
+image/x-coreldrawtemplate			cdt
+image/x-corelphotopaint				cpt
+image/x-icon					ico
+image/x-jg					art
+image/x-jng					jng
+image/x-ms-bmp					bmp
+image/x-photoshop				psd
+image/x-portable-anymap				pnm
+image/x-portable-bitmap				pbm
+image/x-portable-graymap			pgm
+image/x-portable-pixmap				ppm
+image/x-rgb					rgb
+image/x-xbitmap					xbm
+image/x-xpixmap					xpm
+image/x-xwindowdump				xwd
+
+inode/chardevice
+inode/blockdevice
+inode/directory-locked
+inode/directory
+inode/fifo
+inode/socket
+
+message/delivery-status
+message/disposition-notification
+message/external-body
+message/http
+message/s-http
+message/news
+message/partial
+message/rfc822					eml
+
+model/iges					igs iges
+model/mesh					msh mesh silo
+model/vnd.dwf
+model/vnd.flatland.3dml
+model/vnd.gdl
+model/vnd.gs-gdl
+model/vnd.gtw
+model/vnd.mts
+model/vnd.vtu
+model/vrml					wrl vrml
+
+multipart/alternative
+multipart/appledouble
+multipart/byteranges
+multipart/digest
+multipart/encrypted
+multipart/form-data
+multipart/header-set
+multipart/mixed
+multipart/parallel
+multipart/related
+multipart/report
+multipart/signed
+multipart/voice-message
+
+text/calendar					ics icz
+text/css					css
+text/csv					csv
+text/directory
+text/english
+text/enriched
+text/h323					323
+text/html					html htm shtml
+text/iuls					uls
+text/mathml					mml
+text/parityfec
+text/plain					asc txt text pot brf
+text/prs.lines.tag
+text/rfc822-headers
+text/richtext					rtx
+text/rtf
+text/scriptlet					sct wsc
+text/t140
+text/texmacs					tm ts
+text/tab-separated-values			tsv
+text/uri-list
+text/vnd.abc
+text/vnd.curl
+text/vnd.DMClientScript
+text/vnd.flatland.3dml
+text/vnd.fly
+text/vnd.fmi.flexstor
+text/vnd.in3d.3dml
+text/vnd.in3d.spot
+text/vnd.IPTC.NewsML
+text/vnd.IPTC.NITF
+text/vnd.latex-z
+text/vnd.motorola.reflex
+text/vnd.ms-mediapackage
+text/vnd.sun.j2me.app-descriptor		jad
+text/vnd.wap.si
+text/vnd.wap.sl
+text/vnd.wap.wml				wml
+text/vnd.wap.wmlscript				wmls
+text/x-bibtex					bib
+text/x-boo					boo
+text/x-c++hdr					h++ hpp hxx hh
+text/x-c++src					c++ cpp cxx cc
+text/x-chdr					h
+text/x-component				htc
+text/x-crontab
+text/x-csh					csh
+text/x-csrc					c
+text/x-dsrc					d
+text/x-diff					diff patch
+text/x-haskell					hs
+text/x-java					java
+text/x-literate-haskell				lhs
+text/x-makefile
+text/x-moc					moc
+text/x-pascal					p pas
+text/x-pcs-gcd					gcd
+text/x-perl					pl pm
+text/x-python					py
+text/x-scala					scala
+text/x-server-parsed-html
+text/x-setext					etx
+text/x-sh					sh
+text/x-tcl					tcl tk
+text/x-tex					tex ltx sty cls
+text/x-vcalendar				vcs
+text/x-vcard					vcf
+
+video/3gpp					3gp
+video/annodex					axv
+video/dl					dl
+video/dv					dif dv
+video/fli					fli
+video/gl					gl
+video/mpeg					mpeg mpg mpe
+video/mkv					mkv
+video/mp4					mp4
+video/quicktime					qt mov
+video/mp4v-es
+video/ogg					ogv
+video/parityfec
+video/pointer
+video/vnd.fvt
+video/vnd.motorola.video
+video/vnd.motorola.videop
+video/vnd.mpegurl				mxu
+video/vnd.mts
+video/vnd.nokia.interleaved-multimedia
+video/vnd.vivo
+video/x-flv					flv
+video/x-la-asf					lsf lsx
+video/x-mng					mng
+video/x-ms-asf					asf asx
+video/x-ms-wm					wm
+video/x-ms-wmv					wmv
+video/x-ms-wmx					wmx
+video/x-ms-wvx					wvx
+video/x-msvideo					avi
+video/x-sgi-movie				movie
+video/x-matroska				mpv
+
+x-conference/x-cooltalk				ice
+
+x-epoc/x-sisx-app				sisx
+x-world/x-vrml					vrm vrml wrl
diff --git a/fm b/fm
index 1129a9d4..5aa8bae3 100755
--- a/fm
+++ b/fm
@@ -8,6 +8,7 @@
 LOG_LEVEL = 3
 #LOG_LEVEL = 0
 
+
 def File::resolve_symlink( path = __FILE__ )
    path = readlink(path) while symlink?(path)
    expand_path(path)
@@ -17,30 +18,57 @@ def require_from_here ( *list )
    require File.join( FM_DIR, *list )
 end
 
+def fj( *args ) File.join( *args ) end
+
 $: << FM_DIR = File::dirname(File::resolve_symlink)
 
+#SCREENSAVER = fj FM_DIR, 'code', 'screensaver', 'clock.rb'
+
 PID = Process.pid
 
 if ARGV.size > 0
+	case ARGV.first
+	when '-k'
+		exec "killall -9 fm"
+	end
 	pwd = ARGV.first
 	if pwd =~ /^file:\/\//
 		pwd = $'
 	end
+
+	unless File.exists?(pwd)
+		pwd = nil
+	end
+
 else
 	pwd = nil
 end
 
 #require 'ftools'
 require 'pp'
+require 'ostruct'
+class OpenStruct; def __table__() @table end end
 require 'thread'
 
 require_from_here 'interface/ncurses.rb'
+require_from_here 'code/extensions/basic.rb'
+require_from_here 'code/extensions/fileutils.rb'
 require_from_here 'code/fm.rb'
 require_from_here 'code/keys.rb'
 require_from_here 'code/types.rb'
+require_from_here 'code/bars.rb'
+require_from_here 'code/action.rb'
 require_from_here 'code/draw.rb'
-require_from_here 'code/extensions.rb'
+require_from_here 'code/directory.rb'
 require_from_here 'code/debug.rb'
+
+# Screensaver
+require_from_here 'code/screensaver/clock.rb'
+
+unless ARGV.empty? or File.directory?(pwd)
+	exec(Fm.getfilehandler_frompath(pwd))
+end
+
 include Interface
 include Debug
 
@@ -59,10 +87,13 @@ begin
 	Fm.initialize( pwd )
 	Fm.main_loop
 ensure
-	closei
+	log "exiting!"
+	log ""
+	closei if Interface.running?
 	Fm.dump
 	ERROR_STREAM.close
 
+	# Kill all other threads
 	for thr in Thread.list
 		unless thr == Thread.current
 			thr.kill
diff --git a/interface/ncurses.rb b/interface/ncurses.rb
index a383a570..fe208e53 100644
--- a/interface/ncurses.rb
+++ b/interface/ncurses.rb
@@ -19,13 +19,16 @@ module Interface
 			'<down>'
 		when ?\e
 			'<esc>'
+# keep spaces as they are, makes more sense
 #		when ?\s
 #			'<space>'
-		when ?\t
+		when Ncurses::KEY_BTAB
+			'<s-tab>'
+		when 9
 			'<tab>'
-		when 32
-			' '
-		when 0..127
+		when 1..26 # CTRL + ...
+			"<c-#{(key+96).chr}>"
+		when 32..127
 			key.chr
 		else
 			log(key)
@@ -33,62 +36,14 @@ module Interface
 		end
 	end
 
-#	def key c#{{{
-#		case c
-#		when 12
-#			:redraw
-#		when ?\n
-#			:enter
-#		when ?\b, Ncurses::KEY_BACKSPACE
-#			:backspace
-#		when 32
-#			:space
-#		when ?\t
-#			:tab
-#		when Ncurses::KEY_BTAB
-#			:TAB
-#		when ?\e
-#			:escape
-#		when 0..127
-#			c
-#		when Ncurses::KEY_F1..Ncurses::KEY_F30
-#			('F' + (c-Ncurses::KEY_F1+1).to_s).to_sym
-#		when Ncurses::KEY_HOME
-#			:home
-#		when Ncurses::KEY_END
-#			:end
-#		when Ncurses::KEY_RESIZE
-#			:resize
-#		when Ncurses::KEY_DC
-#			:delete
-#		when Ncurses::KEY_ENTER
-#			?\n
-#		when Ncurses::KEY_RIGHT
-#			:right
-#		when Ncurses::KEY_LEFT
-#			:left
-#		when Ncurses::KEY_UP
-#			:up
-#		when Ncurses::KEY_DOWN
-#			:down
-#		when Ncurses::KEY_NPAGE
-#			:pagedown
-#		when Ncurses::KEY_PPAGE
-#			:pageup
-#		when Ncurses::KEY_IC
-#			:insert
-#		else
-##			c
-#			:error
-#		end
-#	end#}}}
-
 	def self.included(this)
 		@@window = Ncurses.initscr
 		starti
 	end
 
+	# (Re)Start the Interface
 	def starti
+		@@running = true
 		@@screen = Ncurses.stdscr
 		@@screen.keypad(true)
 		Ncurses.start_color
@@ -96,17 +51,21 @@ module Interface
 
 		Ncurses.noecho
 		Ncurses.curs_set 0
-		Ncurses.halfdelay(10000)
+		Ncurses.halfdelay(0)
 		@@colortable = []
 	end
 
+	# Close the Interface
 	def closei
+		@@running = false
 		Ncurses.echo
 		Ncurses.cbreak
 		Ncurses.curs_set 1
 		Ncurses.endwin
 	end
 
+	def running?() @@running end
+
 	def cleari
 		Ncurses.mvaddstr(0, 0, ' ' * (lines * cols))
 	end
@@ -116,7 +75,7 @@ module Interface
 	end
 
 	def set_title(x)
-		print "\e]2;#{x}\b"
+		print "\e]2;#{x}\007"
 	end
 
 	def lines
@@ -151,48 +110,32 @@ module Interface
 		end
 	end
 
-	def color_at y, x=0, len=-1, fg=-1, bg=-1
+	def color_at y, x=0, len=-1, fg=-1, bg=-1, attributes=0
 		if OPTIONS['color']
 			if y < 0 then y += Ncurses.LINES end
-			Ncurses.mvchgat(y, x, len, 0, get_color(fg, bg), nil)
+			Ncurses.mvchgat(y, x, len, attributes, get_color(fg, bg), nil)
 		end
 	end
 
 	def color_bold_at y, x=0, len=-1, fg=-1, bg=-1
-		if OPTIONS['color']
-			if y < 0 then y += Ncurses.LINES end
-			Ncurses.mvchgat(y, x, len, Ncurses::A_BOLD, get_color(fg, bg), nil)
-		end
+		color_at(y, x, len, fg, bg, attributes = Ncurses::A_BOLD)
 	end
 
-	def color_reverse_at y, x=0, len=-1, fg=-1, bg=-1
+	def color_reverse_bold_at y, x=0, len=-1, fg=-1, bg=-1
 		if OPTIONS['color']
-			if y < 0 then y += Ncurses.LINES end
-			Ncurses.mvchgat(y, x, len, Ncurses::A_REVERSE, get_color(fg, bg), nil)
+			color_at(y, x, len, fg, bg, Ncurses::A_REVERSE | Ncurses::A_BOLD)
 		else
-			Ncurses.mvchgat(y, x, len, Ncurses::A_REVERSE, 0, nil)
+			Ncurses.mvchgat(y, x, len, Ncurses::A_REVERSE | Ncurses::A_BOLD, 0, nil)
 		end
 	end
+	alias color_bold_reverse_at color_reverse_bold_at
 
-#	runi(:command => String/Array, :wait=>false)
-#	runi('ä́', 'b', 'c')
-	def runi(hash, *args)
-		wait = false
-		if Array === hash
-			command = hash
-		elsif String === hash
-			command = [hash, *args]
+	def color_reverse_at y, x=0, len=-1, fg=-1, bg=-1
+		if OPTIONS['color']
+			color_at(y, x, len, fg, bg, Ncurses::A_REVERSE)
 		else
-			command = hash[:command] or return false
-			wait = hash[:wait] if hash.has_key? :wait
+			Ncurses.mvchgat(y, x, len, Ncurses::A_REVERSE, 0, nil)
 		end
-
-		closei
-
-		system(*command)
-		gets if wait
-
-		starti
 	end
 
 	def get_color(fg, bg)
@@ -214,6 +157,7 @@ module Interface
 			Ncurses.attroff(Ncurses::A_BOLD) 
 		end
 	end
+
 	def reverse(b = true)
 		if b
 			Ncurses.attron(Ncurses::A_REVERSE)