about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/colorschemes/default.py10
-rw-r--r--ranger/core/actions.py18
-rw-r--r--ranger/defaults/commands.py2
-rw-r--r--ranger/fsobject/__init__.py2
-rw-r--r--ranger/fsobject/fsobject.py81
-rw-r--r--ranger/gui/context.py4
-rw-r--r--ranger/gui/widgets/browsercolumn.py41
-rw-r--r--ranger/gui/widgets/statusbar.py18
-rw-r--r--ranger/gui/widgets/titlebar.py2
-rw-r--r--ranger/help/console.py2
-rw-r--r--ranger/help/movement.py2
11 files changed, 107 insertions, 75 deletions
diff --git a/ranger/colorschemes/default.py b/ranger/colorschemes/default.py
index ca8456e7..317e8258 100644
--- a/ranger/colorschemes/default.py
+++ b/ranger/colorschemes/default.py
@@ -49,8 +49,11 @@ class Default(ColorScheme):
 				fg = green
 			if context.socket:
 				fg = magenta
-			if context.fifo:
+				attr |= bold
+			if context.fifo or context.device:
 				fg = yellow
+				if context.device:
+					attr |= bold
 			if context.link:
 				fg = context.good and cyan or magenta
 			if context.tag_marker and not context.selected:
@@ -68,6 +71,11 @@ class Default(ColorScheme):
 				if context.marked:
 					attr |= bold
 					fg = yellow
+			if context.badinfo:
+				if attr & reverse:
+					bg = magenta
+				else:
+					fg = magenta
 
 		elif context.in_titlebar:
 			attr |= bold
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index b320396a..2fd7d46d 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -15,14 +15,18 @@
 
 import os
 import shutil
+from os.path import join, isdir
+from os import symlink, getcwd
 from inspect import cleandoc
 
 import ranger
 from ranger.ext.direction import Direction
-from ranger.shared import FileManagerAware, EnvironmentAware, SettingsAware
 from ranger import fsobject
+from ranger.shared import FileManagerAware, EnvironmentAware, SettingsAware
 from ranger.gui.widgets import console_mode as cmode
 from ranger.fsobject import File
+from ranger.ext import shutil_generatorized as shutil_g
+from ranger.fsobject.loader import LoadableObject
 
 class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 	search_method = 'ctime'
@@ -569,14 +573,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 		self.ui.browser.main_column.request_redraw()
 
 	def paste_symlink(self):
-		from os import symlink, getcwd
-		from os.path import join
-
 		copied_files = self.env.copy
-
-		if not copied_files:
-			return
-
 		for f in copied_files:
 			try:
 				symlink(f.path, join(getcwd(), f.basename))
@@ -585,9 +582,6 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 
 	def paste(self, overwrite=False):
 		"""Paste the selected items into the current directory"""
-		from os.path import join, isdir
-		from ranger.ext import shutil_generatorized as shutil_g
-		from ranger.fsobject.loader import LoadableObject
 		copied_files = tuple(self.env.copy)
 
 		if not copied_files:
@@ -643,7 +637,7 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 		self.env.copy -= set(selected)
 		if selected:
 			for f in selected:
-				if os.path.isdir(f.path) and not os.path.islink(f.path):
+				if isdir(f.path) and not os.path.islink(f.path):
 					try:
 						shutil.rmtree(f.path)
 					except OSError as err:
diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py
index 03fa1634..f04c4889 100644
--- a/ranger/defaults/commands.py
+++ b/ranger/defaults/commands.py
@@ -332,7 +332,7 @@ class delete(Command):
 		cwd = self.fm.env.cwd
 		cf = self.fm.env.cf
 
-		if cwd.marked_items or (cf.is_directory and not cf.islink \
+		if cwd.marked_items or (cf.is_directory and not cf.is_link \
 				and len(os.listdir(cf.path)) > 0):
 			# better ask for a confirmation, when attempting to
 			# delete multiple files or a non-empty directory.
diff --git a/ranger/fsobject/__init__.py b/ranger/fsobject/__init__.py
index 5f727c87..6796f252 100644
--- a/ranger/fsobject/__init__.py
+++ b/ranger/fsobject/__init__.py
@@ -21,7 +21,7 @@ T_DIRECTORY = 'directory'
 T_UNKNOWN = 'unknown'
 T_NONEXISTANT = 'nonexistant'
 
-BAD_INFO = None
+BAD_INFO = '?'
 
 class NotLoadedYet(Exception):
 	pass
diff --git a/ranger/fsobject/fsobject.py b/ranger/fsobject/fsobject.py
index 1ab3addd..94729b9a 100644
--- a/ranger/fsobject/fsobject.py
+++ b/ranger/fsobject/fsobject.py
@@ -16,11 +16,18 @@
 CONTAINER_EXTENSIONS = 'rar zip tar gz bz bz2 tgz 7z iso cab'.split()
 DOCUMENT_EXTENSIONS = 'pdf doc ppt odt'.split()
 DOCUMENT_BASENAMES = 'README TODO LICENSE COPYING INSTALL'.split()
-
-import time
+DOCUMENT_EXTENSIONS = ()
+DOCUMENT_BASENAMES = ()
+
+import stat
+import os
+from time import time
+from subprocess import Popen, PIPE
+from os.path import abspath, basename, dirname, realpath
 from . import T_FILE, T_DIRECTORY, T_UNKNOWN, T_NONEXISTANT, BAD_INFO
 from ranger.shared import MimeTypeAware, FileManagerAware
 from ranger.ext.shell_escape import shell_escape
+from ranger.ext.human_readable import human_readable
 
 class FileSystemObject(MimeTypeAware, FileManagerAware):
 	is_file = False
@@ -40,7 +47,10 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 	tagged = False
 	loaded = False
 	runnable = False
-	islink = False
+	is_link = False
+	is_device = False
+	is_socket = False
+	is_fifo = False
 	readlink = None
 	stat = None
 	infostring = None
@@ -62,20 +72,17 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 
 	def __init__(self, path):
 		MimeTypeAware.__init__(self)
-		if type(self) == FileSystemObject:
-			raise TypeError("Cannot initialize abstract class FileSystemObject")
-
-		from os.path import abspath, basename, dirname, realpath
 
 		path = abspath(path)
 		self.path = path
 		self.basename = basename(path)
 		self.basename_lower = self.basename.lower()
 		self.dirname = dirname(path)
-		self.realpath = realpath(path)
+		self.realpath = self.path
 
 		try:
-			self.extension = self.basename[self.basename.rindex('.') + 1:].lower()
+			lastdot = self.basename.rindex('.') + 1
+			self.extension = self.basename[lastdot:].lower()
 		except ValueError:
 			self.extension = None
 
@@ -94,10 +101,9 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 	@property
 	def filetype(self):
 		if self._filetype is None:
-			import subprocess
 			try:
-				got = subprocess.Popen(["file", '-Lb', '--mime-type',\
-						self.path], stdout=subprocess.PIPE).communicate()[0]
+				got = Popen(["file", '-Lb', '--mime-type', self.path],
+						stdout=PIPE).communicate()[0]
 			except OSError:
 				self._filetype = ''
 			else:
@@ -113,13 +119,13 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 
 	def use(self):
 		"""mark the filesystem-object as used at the current time"""
-		self.last_used = time.time()
+		self.last_used = time()
 
 	def is_older_than(self, seconds):
 		"""returns whether this object wasn't use()d in the last n seconds"""
 		if seconds < 0:
 			return True
-		return self.last_used + seconds < time.time()
+		return self.last_used + seconds < time()
 
 	def set_mimetype(self):
 		"""assign attributes such as self.video according to the mimetype"""
@@ -155,9 +161,6 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 		reads useful information about the filesystem-object from the
 		filesystem and caches it for later use
 		"""
-		import os
-		import stat
-		from ranger.ext.human_readable import human_readable
 
 		self.loaded = True
 
@@ -165,10 +168,21 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 			self.stat = os.lstat(self.path)
 		except OSError:
 			self.stat = None
-			self.islink = False
+			self.is_link = False
 			self.accessible = False
 		else:
-			self.islink = stat.S_ISLNK(self.stat.st_mode)
+			self.is_link = stat.S_ISLNK(self.stat.st_mode)
+			if self.is_link:
+				try: # try to resolve the link
+					self.readlink = os.readlink(self.path)
+					self.realpath = realpath(self.path)
+					self.stat = os.stat(self.path)
+				except:  # it failed, so it must be a broken link
+					pass
+			mode = self.stat.st_mode
+			self.is_device = bool(stat.S_ISCHR(mode) or stat.S_ISBLK(mode))
+			self.is_socket = bool(stat.S_ISSOCK(mode))
+			self.is_fifo = bool(stat.S_ISFIFO(mode))
 			self.accessible = True
 
 		if self.accessible and os.access(self.path, os.F_OK):
@@ -191,10 +205,17 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 				self.infostring = ' ' + human_readable(self.stat.st_size)
 			else:
 				self.type = T_UNKNOWN
-				self.infostring = None
+				if self.is_device:
+					self.infostring = 'dev'
+				elif self.is_fifo:
+					self.infostring = 'fifo'
+				elif self.is_socket:
+					self.infostring = 'sock'
+				else:
+					self.infostring = BAD_INFO
 
 		else:
-			if self.islink:
+			if self.is_link:
 				self.infostring = '->'
 			else:
 				self.infostring = None
@@ -202,18 +223,14 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 			self.exists = False
 			self.runnable = False
 
-		if self.islink:
-			self.readlink = os.readlink(self.path)
-
 	def get_permission_string(self):
 		if self.permissions is not None:
 			return self.permissions
 
-		if self.accessible is False:
-			return '----------'
-
-		import stat
-		mode = self.stat.st_mode
+		try:
+			mode = self.stat.st_mode
+		except:
+			return '----??----'
 
 		if stat.S_ISDIR(mode):
 			perms = ['d']
@@ -250,9 +267,8 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 		Calls load() if the currently cached information is outdated
 		or nonexistant.
 		"""
-		if self.load_once(): return True
-
-		import os
+		if self.load_once():
+			return True
 		try:
 			real_mtime = os.lstat(self.path).st_mtime
 		except OSError:
@@ -261,7 +277,6 @@ class FileSystemObject(MimeTypeAware, FileManagerAware):
 			cached_mtime = self.stat.st_mtime
 		else:
 			cached_mtime = 0
-
 		if real_mtime != cached_mtime:
 			self.load()
 			return True
diff --git a/ranger/gui/context.py b/ranger/gui/context.py
index d4c1c94d..1e127a2e 100644
--- a/ranger/gui/context.py
+++ b/ranger/gui/context.py
@@ -13,11 +13,11 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-CONTEXT_KEYS = ['reset', 'error',
+CONTEXT_KEYS = ['reset', 'error', 'badinfo',
 		'in_browser', 'in_statusbar', 'in_titlebar', 'in_console',
 		'in_pager', 'in_taskview',
 		'directory', 'file', 'hostname',
-		'executable', 'media', 'link', 'fifo', 'socket',
+		'executable', 'media', 'link', 'fifo', 'socket', 'device',
 		'video', 'audio', 'image', 'media', 'document', 'container',
 		'selected', 'empty', 'main_column', 'message', 'background',
 		'good', 'bad',
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index 8cf8990c..61c74e63 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -20,6 +20,7 @@ from time import time
 
 from . import Widget
 from .pager import Pager
+from ranger.fsobject import BAD_INFO
 
 # Don't even try to preview files which mach this regular expression:
 PREVIEW_BLACKLIST = re.compile(r"""
@@ -95,20 +96,21 @@ class BrowserColumn(Pager):
 			pass
 
 		elif self.target.type is T_DIRECTORY:
-			index = self.scroll_begin + event.y - self.y
-
-			if event.pressed(1):
-				if not self.main_column:
-					self.fm.enter_dir(self.target.path)
-
-				if index < len(self.target):
-					self.fm.move(to=index)
-			elif event.pressed(3):
-				try:
-					clicked_file = self.target.files[index]
-					self.fm.enter_dir(clicked_file.path)
-				except:
-					pass
+			if self.target.accessible and self.target.content_loaded:
+				index = self.scroll_begin + event.y - self.y
+
+				if event.pressed(1):
+					if not self.main_column:
+						self.fm.enter_dir(self.target.path)
+
+					if index < len(self.target):
+						self.fm.move(to=index)
+				elif event.pressed(3):
+					try:
+						clicked_file = self.target.files[index]
+						self.fm.enter_dir(clicked_file.path)
+					except:
+						pass
 
 		else:
 			if self.level > 0:
@@ -173,6 +175,7 @@ class BrowserColumn(Pager):
 				and target.is_file \
 				and target.accessible \
 				and target.stat \
+				and not target.is_device \
 				and not target.stat.st_mode & stat.S_IFIFO):
 			return False
 
@@ -250,6 +253,7 @@ class BrowserColumn(Pager):
 			except IndexError:
 				break
 
+			bad_info_color = None
 			this_color = base_color + list(drawn.mimetype_tuple)
 			text = drawn.basename
 			tagged = self.fm.tags and drawn.realpath in self.fm.tags
@@ -280,11 +284,13 @@ class BrowserColumn(Pager):
 					this_color.append('fifo')
 				if stat.S_ISSOCK(mode):
 					this_color.append('socket')
+				if drawn.is_device:
+					this_color.append('device')
 
 			if self.env.copy and drawn in self.env.copy:
 				this_color.append('cut' if self.env.cut else 'copied')
 
-			if drawn.islink:
+			if drawn.is_link:
 				this_color.append('link')
 				this_color.append(drawn.exists and 'good' or 'bad')
 
@@ -302,6 +308,8 @@ class BrowserColumn(Pager):
 						and self.settings.display_size_in_main_column:
 					info = drawn.infostring
 					x = self.wid - 1 - len(info)
+					if info is BAD_INFO:
+						bad_info_color = (x, len(str(info)))
 					if x > self.x:
 						self.win.addstr(line, x, str(info) + ' ')
 			except:
@@ -310,6 +318,9 @@ class BrowserColumn(Pager):
 				pass
 
 			self.color_at(line, 0, self.wid, this_color)
+			if bad_info_color:
+				start, wid = bad_info_color
+				self.color_at(line, start, wid, this_color, 'badinfo')
 
 			if self.main_column and tagged and self.wid > 2:
 				this_color.append('tag_marker')
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index caf5786e..bff3d8ad 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -145,23 +145,27 @@ class StatusBar(Widget):
 			target = self.column.target.pointed_obj
 		else:
 			target = self.env.at_level(0).pointed_obj
-		if target is None or not target.accessible:
+		try:
+			stat = target.stat
+		except:
+			return
+		if stat is None:
 			return
 
 		perms = target.get_permission_string()
-		how = getuid() == target.stat.st_uid and 'good' or 'bad'
+		how = getuid() == stat.st_uid and 'good' or 'bad'
 		left.add(perms, 'permissions', how)
-
 		left.add_space()
-		left.add(str(target.stat.st_nlink), 'nlink')
+		left.add(str(stat.st_nlink), 'nlink')
 		left.add_space()
 		left.add(self._get_owner(target), 'owner')
 		left.add_space()
 		left.add(self._get_group(target), 'group')
 
-		if target.islink:
+		if target.is_link:
 			how = target.exists and 'good' or 'bad'
-			left.add(' -> ' + target.readlink, 'link', how)
+			dest = target.readlink if target.readlink is not None else '?'
+			left.add(' -> ' + dest, 'link', how)
 		else:
 			if self.settings.display_size_in_status_bar and target.infostring:
 				left.add(target.infostring)
@@ -169,7 +173,7 @@ class StatusBar(Widget):
 			left.add_space()
 
 			left.add(strftime(self.timeformat,
-					localtime(target.stat.st_mtime)), 'mtime')
+					localtime(stat.st_mtime)), 'mtime')
 
 	def _get_owner(self, target):
 		uid = target.stat.st_uid
diff --git a/ranger/gui/widgets/titlebar.py b/ranger/gui/widgets/titlebar.py
index b815a07e..a949df05 100644
--- a/ranger/gui/widgets/titlebar.py
+++ b/ranger/gui/widgets/titlebar.py
@@ -118,7 +118,7 @@ class TitleBar(Widget):
 			bar.add('~/', 'directory', fixed=True)
 
 		for path in pathway:
-			if path.islink:
+			if path.is_link:
 				clr = 'link'
 			else:
 				clr = 'directory'
diff --git a/ranger/help/console.py b/ranger/help/console.py
index 3a4428f3..76ce9f59 100644
--- a/ranger/help/console.py
+++ b/ranger/help/console.py
@@ -149,7 +149,7 @@ the flag "-OO" which removes all docstrings.)
 ==============================================================================
 3.4. The Open Console
 
-Open this console by pressing "!"
+Open this console by pressing "!" or "s"
 
 The Open Console allows you to execute shell commands:
 !vim *         will run vim and open all files in the directory.
diff --git a/ranger/help/movement.py b/ranger/help/movement.py
index 4ea2b0c3..f6a70eb1 100644
--- a/ranger/help/movement.py
+++ b/ranger/help/movement.py
@@ -77,7 +77,7 @@ These keys work like in vim:
 
 	i	inspect the content of the file
 	E	edit the file
-	s	open a shell, starting in the current directory
+	S	open a shell, starting in the current directory
 
 Marking files allows you to use operations on multiple files at once.
 If there are any marked files in this directory, "yy" will copy them instead