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.py1
-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
9 files changed, 99 insertions, 62 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 f96caeee..d7e33c9b 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -22,7 +22,6 @@ from inspect import cleandoc
 import ranger
 from ranger.ext.direction import Direction
 from ranger import fsobject
-from ranger.ext.direction import Direction
 from ranger.shared import FileManagerAware, EnvironmentAware, SettingsAware
 from ranger.gui.widgets import console_mode as cmode
 from ranger.fsobject import File
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'