summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ranger/core/actions.py14
-rw-r--r--ranger/defaults/commands.py2
-rw-r--r--ranger/defaults/rifle.conf46
-rwxr-xr-xranger/ext/rifle.py103
4 files changed, 113 insertions, 52 deletions
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index d5d38de9..f701c08f 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -17,6 +17,7 @@ from ranger.ext.relative_symlink import relative_symlink
 from ranger.ext.keybinding_parser import key_to_string, construct_keybinding
 from ranger.ext.shell_escape import shell_quote
 from ranger.ext.next_available_filename import next_available_filename
+from ranger.ext.rifle import squash_flags
 from ranger.core.shared import FileManagerAware, EnvironmentAware, \
 		SettingsAware
 from ranger.fsobject import File
@@ -292,19 +293,16 @@ class Actions(FileManagerAware, EnvironmentAware, SettingsAware):
 		elif type(files) not in (list, tuple):
 			files = [files]
 
-		if 'flags' in kw:
-			from ranger.core.runner import Context
-			context = Context(files=list(files), flags=kw['flags'])
-			context.squash_flags()
-			if 'c' in context.flags:
-				files = [self.fm.env.cf]
+		flags = squash_flags(kw.get('flags', ''))
+		if 'c' in flags:
+			files = [self.fm.env.cf]
 
 		self.signal_emit('execute.before', keywords=kw)
 		filenames = [f.path for f in files]
 		mimetype = files[0].mimetype if files else None
-		label = kw['label'] if 'label' in kw else None
+		label = kw.get('app', None)
 		try:
-			return self.rifle.execute(filenames, mode, label, mimetype)
+			return self.rifle.execute(filenames, mode, label, flags, mimetype)
 		finally:
 			self.signal_emit('execute.after')
 
diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py
index 054bb020..52a1d30e 100644
--- a/ranger/defaults/commands.py
+++ b/ranger/defaults/commands.py
@@ -312,7 +312,7 @@ class open_with(Command):
 		return app, flags, int(mode)
 
 	def _is_app(self, arg):
-		return (not self._is_flags(arg) and arg in get_executables())
+		return not self._is_flags(arg) and not arg.isdigit()
 
 	def _is_flags(self, arg):
 		return all(x in ALLOWED_FLAGS for x in arg)
diff --git a/ranger/defaults/rifle.conf b/ranger/defaults/rifle.conf
index 5851f447..2e86d463 100644
--- a/ranger/defaults/rifle.conf
+++ b/ranger/defaults/rifle.conf
@@ -7,12 +7,11 @@
 # Syntax:
 #   <condition1> , <condition2> , ... = command
 #
-# Prefixing a condition with a "!" will negate its result.
-#
 # The command can contain these environment variables:
 #   $1-$9 | The n-th selected file
 #   $@    | All selected files
 #
+# Prefixing a condition with "!" will negate its result.
 # These conditions are currently supported:
 #   ext <regexp>  | The regexp matches the extension of $1
 #   mime <regexp> | The regexp matches the mime type of $1
@@ -24,28 +23,39 @@
 #
 # There are also pseudo-conditions which have a "side effect":
 #   flag <flags>  | Change how the program is run. See below.
-#   label <label> | Assign a label or name to the rule.
+#   label <label> | Assign a label or name to the command so it can
+#                 | be started with :open_with <label> in ranger
+#                 | or `rifle -p <label>` in the standalone executable.
 #   else          | Always true.
 #
-# These flags are currently supported for the "flag" condition:
-#   f | fork the program to the background
-#   p | pipe the output into "$PAGER" or "less"
-#   w | ask the user to press enter after the process has been executed
+# Flags are single characters which slightly transform the command:
+#   f | Fork the program, make it run in the background.
+#     |   New command = nohup $command >& /dev/null &
+#   r | Execute the command with root permissions
+#     |   New command = sudo $command
+#   t | Run the program in a new terminal.  If $TERMCMD is not defined,
+#     | rifle will attempt to extract it from $TERM.
+#     |   New command = $TERMCMD -e $command
+#
+# NOTE: FLAGS DO NOT WORK PROPERLY WHEN PIPES ARE IN THE COMMAND
 
 #-------------------------------------------
 # Websites
 #-------------------------------------------
+# Rarely installed browsers get higher priority; It is assumed that if you
+# install a rare browser, you probably use it.  Firefox/konqueror/w3m on the
+# other hand are often only installed as fallback browsers.
 ext x?html?, has surf,           X, flag f = surf -- "$@"
 ext x?html?, has vimprobable,    X, flag f = vimprobable -- "$@"
 ext x?html?, has vimprobable2,   X, flag f = vimprobable2 -- "$@"
 ext x?html?, has jumanji,        X, flag f = jumanji -- "$@"
 ext x?html?, has luakit,         X, flag f = luakit -- "$@"
 ext x?html?, has uzbl,           X, flag f = uzbl -- "$@"
+ext x?html?, has midori,         X, flag f = midori -- "$@"
+ext x?html?, has opera,          X, flag f = opera -- "$@"
 ext x?html?, has firefox,        X, flag f = firefox -- "$@"
 ext x?html?, has seamonkey,      X, flag f = seamonkey -- "$@"
 ext x?html?, has iceweasel,      X, flag f = iceweasel -- "$@"
-ext x?html?, has opera,          X, flag f = opera -- "$@"
-ext x?html?, has midori,         X, flag f = midori -- "$@"
 ext x?html?, has epiphany,       X, flag f = epiphany -- "$@"
 ext x?html?, has konqueror,      X, flag f = konqueror -- "$@"
 ext x?html?, has elinks,          terminal = elinks "$@"
@@ -89,9 +99,9 @@ ext midi?,   terminal, has wildmidi = wildmidi -- "$@"
 mime ^video|audio, has gmplayer, X, flag f = gmplayer -- "$@"
 mime ^video|audio, has smplayer, X, flag f = smplayer -- "$@"
 mime ^video,       has mplayer2, X, flag f = mplayer2 -- "$@"
+mime ^video,       has mplayer2, X, flag f = mplayer2 -fs -- "$@"
 mime ^video,       has mplayer,  X, flag f = mplayer -- "$@"
 mime ^video,       has mplayer,  X, flag f = mplayer -fs -- "$@"
-mime ^video,       has mplayer,  X, flag f = mplayer -mixer software -- "$@"
 mime ^video|audio, has vlc,      X, flag f = vlc -- "$@"
 mime ^video|audio, has totem,    X, flag f = totem -- "$@"
 mime ^video|audio, has totem,    X, flag f = totem --fullscreen -- "$@"
@@ -99,8 +109,8 @@ mime ^video|audio, has totem,    X, flag f = totem --fullscreen -- "$@"
 #--------------------------------------------
 # Video without X:
 #-------------------------------------------
-mime ^video, terminal, has mplayer2   = mplayer2 -- "$@"
-mime ^video, terminal, has mplayer    = mplayer -- "$@"
+mime ^video, terminal, !X, has mplayer2  = mplayer2 -- "$@"
+mime ^video, terminal, !X, has mplayer   = mplayer -- "$@"
 
 #-------------------------------------------
 # Image Viewing:
@@ -135,10 +145,10 @@ ext djvu, has evince, X, flag f = evince -- "$@"
 # Archives
 #-------------------------------------------
 # This requires atool
-ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz,  has als,     flag p = als -- "$@"
-ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has als,     flag p = als -- "$@"
-ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz,  has aunpack, flag p = aunpack -- "$@"
-ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has aunpack, flag p = aunpack -- "$@"
+ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz,  has als     = als -- "$@" | "$PAGER"
+ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has als     = als -- "$@" | "$PAGER"
+ext 7z|ace|ar|arc|bz2?|cab|cpio|cpt|deb|dgc|dmg|gz,  has aunpack = aunpack -- "$@"
+ext iso|jar|msi|pkg|rar|shar|tar|tgz|xar|xpi|xz|zip, has aunpack = aunpack -- "$@"
 
 # Fallback:
 ext tar|gz, has tar = tar vvtf "$@"
@@ -152,5 +162,7 @@ label wallpaper, mime ^image, X = feh --bg-tile "$1"
 label wallpaper, mime ^image, X = feh --bg-center "$1"
 label wallpaper, mime ^image, X = feh --bg-fill "$1"
 
-# Define the editor for non-text files as last action
+# Define the editor for non-text files + pager as last action
 label editor, !mime ^text, !ext xml|csv|tex  = "$EDITOR" -- "$@"
+label pager,   mime ^text                    = "$PAGER" -- "$@"
+label pager,                ext xml|csv|tex  = "$PAGER" -- "$@"
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py
index ea2c113b..4870c36e 100755
--- a/ranger/ext/rifle.py
+++ b/ranger/ext/rifle.py
@@ -37,6 +37,22 @@ def _is_terminal():
 		return False
 	return True
 
+
+def squash_flags(flags):
+	"""
+	Remove lowercase flags if the respective uppercase flag exists
+
+	>>> squash_flags('abc')
+	'abc'
+	>>> squash_flags('abcC')
+	'ab'
+	>>> squash_flags('CabcAd')
+	'bd'
+	"""
+	exclude = ''.join(f.upper() + f.lower() for f in flags if f == f.upper())
+	return ''.join(f for f in flags if f not in exclude)
+
+
 class Rifle(object):
 	delimiter1 = '='
 	delimiter2 = ','
@@ -61,7 +77,8 @@ class Rifle(object):
 
 	def __init__(self, config_file):
 		self.config_file = config_file
-		self._app_flags = False
+		self._app_flags = ''
+		self._app_label = None
 
 	def reload_config(self, config_file=None):
 		"""Replace the current configuration with the one in config_file"""
@@ -93,6 +110,8 @@ class Rifle(object):
 		# Handle the negation of conditions starting with an exclamation mark,
 		# then pass on the arguments to _eval_condition2().
 
+		if not condition:
+			return True
 		if condition[0].startswith('!'):
 			new_condition = tuple([condition[0][1:]]) + tuple(condition[1:])
 			return not self._eval_condition2(new_condition, files, label)
@@ -107,9 +126,6 @@ class Rifle(object):
 		if not files:
 			return False
 
-		self._app_flags = ''
-		self._app_label = None
-
 		if function == 'ext':
 			extension = os.path.basename(files[0]).rsplit('.', 1)[-1]
 			return bool(re.search('^' + argument + '$', extension))
@@ -144,17 +160,38 @@ class Rifle(object):
 		self._mimetype = mimetype
 		return mimetype
 
-	def _build_command(self, files, action):
+	def _build_command(self, files, action, flags):
+		# Get the flags
+		if isinstance(flags, str):
+			self._app_flags += flags
+		self._app_flags = squash_flags(self._app_flags)
 		flags = self._app_flags
+
 		_filenames = "' '".join(f.replace("'", "'\\\''") for f in files)
 		command = "set -- '%s'" % _filenames + '\n'
-		if 'p' in flags and not 'f' in flags and _is_terminal():
-			action += '| less'
-		if 'f' in flags:
-			action = "nohup %s >&/dev/null &" % action
-		command += action
+
+		# Apply flags
+		command += self._apply_flags(action, flags)
 		return command
 
+	def _apply_flags(self, action, flags):
+		# FIXME: Flags do not work properly when pipes are in the command.
+		if 'r' in flags:
+			action = 'sudo ' + action
+		if 't' in flags:
+			if 'TERMCMD' not in os.environ:
+				term = os.environ['TERM']
+				if term.startswith('rxvt-unicode'):
+					term = 'urxvt'
+				if term not in get_executables():
+					self.hook_logger("Can not determine terminal command.  "
+						"Please set $TERMCMD manually.")
+				os.environ['TERMCMD'] = term
+			action = "$TERMCMD -e %s" % action
+		if 'f' in flags:
+			action = "nohup %s >& /dev/null &" % action
+		return action
+
 	def list_commands(self, files, mimetype=None):
 		"""
 		Returns one 4-tuple for all currently applicable commands
@@ -168,6 +205,8 @@ class Rifle(object):
 		result = []
 		t = time.time()
 		for cmd, tests in self.rules:
+			self._app_flags = ''
+			self._app_label = None
 			for test in tests:
 				if not self._eval_condition(test, files, None):
 					break
@@ -196,23 +235,28 @@ class Rifle(object):
 		count = 0
 		# Determine command
 		for cmd, tests in self.rules:
+			self._app_flags = ''
+			self._app_label = None
 			for test in tests:
 				if not self._eval_condition(test, files, label):
 					break
 			else:
-				if count != way:
-					count += 1
-				else:
-					command = self.hook_command_preprocessing(command)
-					command = self._build_command(files, cmd)
+				if label and label == self._app_label or \
+					not label and count == way:
+					cmd = self.hook_command_preprocessing(cmd)
+					command = self._build_command(files, cmd, flags)
 					break
+				else:
+					count += 1
 		# Execute command
 		if command is None:
-			if count <= 0:
+			if count <= 0 or way <= 0:
 				self.hook_logger("No action found.")
 			else:
 				self.hook_logger("Method number %d is undefined." % way)
 		else:
+			if 'PAGER' not in os.environ:
+				os.environ['PAGER'] = 'less'
 			command = self.hook_command_postprocessing(command)
 			self.hook_before_executing(command, self._mimetype, self._app_flags)
 			try:
@@ -221,8 +265,9 @@ class Rifle(object):
 			finally:
 				self.hook_after_executing(command, self._mimetype, self._app_flags)
 
+
 def main():
-	"""The main function, which is run when you start this program direectly."""
+	"""The main function which is run when you start this program direectly."""
 	import sys
 
 	# Find configuration file path
@@ -236,16 +281,22 @@ def main():
 
 	# Evaluate arguments
 	from optparse import OptionParser
-	parser = OptionParser(usage="%prog [-hlpw] [files]")
-	parser.add_option('-p', type='string', default='0', metavar="KEYWORD",
-			help="pick a method to open the files.  KEYWORD is either the number"
-			" listed by 'rifle -l' or a string that matches a label in the"
-			" configuration file")
+	parser = OptionParser(usage="%prog [-fhlpw] [files]")
+	parser.add_option('-f', type="string", default=None, metavar="FLAGS",
+			help="use additional flags: f=fork, r=root, t=terminal. "
+			"Uppercase flag negates respective lowercase flags.")
 	parser.add_option('-l', action="store_true",
-			help="list possible ways to open the files")
+			help="list possible ways to open the files (id:label:flags:command)")
+	parser.add_option('-p', type='string', default='0', metavar="KEYWORD",
+			help="pick a method to open the files.  KEYWORD is either the "
+			"number listed by 'rifle -l' or a string that matches a label in "
+			"the configuration file")
 	parser.add_option('-w', type='string', default=None, metavar="PROGRAM",
 			help="open the files with PROGRAM")
 	options, positional = parser.parse_args()
+	if not positional:
+		parser.print_help()
+		raise SystemExit(1)
 
 	if options.p.isdigit():
 		way = int(options.p)
@@ -263,10 +314,10 @@ def main():
 		rifle.reload_config()
 		#print(rifle.list_commands(sys.argv[1:]))
 		if options.l:
-			for count, cmd, label, flags in rifle.list_commands(sys.argv[1:]):
-				print("%d: %s" % (count, cmd))
+			for count, cmd, label, flags in rifle.list_commands(positional):
+				print("%d:%s:%s:%s" % (count, label or '', flags, cmd))
 		else:
-			rifle.execute(sys.argv[1:], way=way, label=label)
+			rifle.execute(positional, way=way, label=label, flags=options.f)
 
 
 if __name__ == '__main__':