summary refs log blame commit diff stats
path: root/ranger/core/main.py
blob: c3a3d76bfe3e78f0ae85e0e644420b223a65864d (plain) (tree)
1
2
3
4
5
6
7
8
9
                                                                       
                                                                        




                                                                    

              


                                                        
                     
                  
                                                                      
                                     
 



                                                                          




                                                                                  






                                                                   



                                            



                                                     













                                                                                    
 

                                             
                          
                                                                

                                                                      






                                                                             
                                


                                                   
                                                          
                                 


                                                                                       
                                                                           

                                                
                                                                                              
                                                             



                                    
                                      
                                        
                                            

                                        

                                                                               
                                                       
                                                                                       
                                                   
                                                                                  

                                                   


                                                             
                                         
                                                         
                                                              
                                 
                                                                       

                                                                            


                                                      


                                      

                           

                                                           
 





                                                                                 
                                                                                        

                                 





                                                        
                                   
                            
                                                                                      

                                                 



                                                   
                                                  
                                                                                     
                                   
                                                                             

                                                                                          



                                                                    
                                              
                                                   
                                                                   


                                                                                           

















































































































































                                                                                                
# Copyright (C) 2009, 2010, 2011  Roman Zimbelmann <romanz@lavabit.com>
# This software is distributed under the terms of the GNU GPL version 3.

"""
The main function responsible to initialize the FM object and stuff.
"""

import os.path

def main():
	"""initialize objects and run the filemanager"""
	import locale
	import ranger
	import sys
	from ranger.core.shared import FileManagerAware, SettingsAware
	from ranger.core.fm import FM

	if not sys.stdin.isatty():
		sys.stderr.write("Error: Must run ranger from terminal\n")
		raise SystemExit(1)

	try:
		locale.setlocale(locale.LC_ALL, '')
	except:
		print("Warning: Unable to set locale.  Expect encoding problems.")

	# so that programs can know that ranger spawned them:
	level = 'RANGER_LEVEL'
	if level in os.environ and os.environ[level].isdigit():
		os.environ[level] = str(int(os.environ[level]) + 1)
	else:
		os.environ[level] = '1'

	if not 'SHELL' in os.environ:
		os.environ['SHELL'] = 'bash'

	ranger.arg = arg = parse_arguments()
	if arg.copy_config is not None:
		fm = FM()
		fm.copy_config_files(arg.copy_config)
		return 1 if arg.fail_unless_cd else 0
	if arg.list_tagged_files:
		fm = FM()
		try:
			f = open(fm.confpath('tagged'), 'r')
		except:
			pass
		else:
			for line in f.readlines():
				if len(line) > 2 and line[1] == ':':
					if line[0] in arg.list_tagged_files:
						sys.stdout.write(line[2:])
				elif len(line) > 0 and '*' in arg.list_tagged_files:
					sys.stdout.write(line)
		return 1 if arg.fail_unless_cd else 0

	SettingsAware._setup(clean=arg.clean)

	if arg.selectfile:
		arg.selectfile = os.path.abspath(arg.selectfile)
		arg.targets.insert(0, os.path.dirname(arg.selectfile))

	targets = arg.targets or ['.']
	target = targets[0]
	if arg.targets:
		if target.startswith('file://'):
			target = target[7:]
		if not os.access(target, os.F_OK):
			print("File or directory doesn't exist: %s" % target)
			return 1
		elif os.path.isfile(target):
			def print_function(string):
				print(string)
			from ranger.ext.rifle import Rifle
			fm = FM()
			if not arg.clean and os.path.isfile(fm.confpath('rifle.conf')):
				rifleconf = fm.confpath('rifle.conf')
			else:
				rifleconf = fm.relpath('config/rifle.conf')
			rifle = Rifle(rifleconf)
			rifle.reload_config()
			rifle.execute(targets, number=ranger.arg.mode, flags=ranger.arg.flags)
			return 1 if arg.fail_unless_cd else 0

	crash_traceback = None
	try:
		# Initialize objects
		fm = FM(paths=targets)
		FileManagerAware.fm = fm
		load_settings(fm, arg.clean)

		if arg.list_unused_keys:
			from ranger.ext.keybinding_parser import (special_keys,
					reversed_special_keys)
			maps = fm.ui.keymaps['browser']
			for key in sorted(special_keys.values(), key=lambda x: str(x)):
				if key not in maps:
					print("<%s>" % reversed_special_keys[key])
			for key in range(33, 127):
				if key not in maps:
					print(chr(key))
			return 1 if arg.fail_unless_cd else 0

		if fm.username == 'root':
			fm.settings.preview_files = False
			fm.settings.use_preview_script = False
		if not arg.debug:
			from ranger.ext import curses_interrupt_handler
			curses_interrupt_handler.install_interrupt_handler()

		if arg.selectfile:
			fm.select_file(arg.selectfile)

		# Run the file manager
		fm.initialize()
		fm.ui.initialize()

		if arg.cmd:
			for command in arg.cmd:
				fm.execute_console(command)

		if ranger.arg.profile:
			import cProfile
			import pstats
			profile = None
			ranger.__fm = fm
			cProfile.run('ranger.__fm.loop()', '/tmp/ranger_profile')
			profile = pstats.Stats('/tmp/ranger_profile', stream=sys.stderr)
		else:
			fm.loop()
	except Exception:
		import traceback
		crash_traceback = traceback.format_exc()
	except SystemExit as error:
		return error.args[0]
	finally:
		if crash_traceback:
			try:
				filepath = fm.thisfile.path if fm.thisfile else "None"
			except:
				filepath = "None"
		try:
			fm.ui.destroy()
		except (AttributeError, NameError):
			pass
		if ranger.arg.profile and profile:
			profile.strip_dirs().sort_stats('cumulative').print_callees()
		if crash_traceback:
			print("ranger version: %s, executed with python %s" %
					(ranger.__version__, sys.version.split()[0]))
			print("Locale: %s" % '.'.join(str(s) for s in locale.getlocale()))
			try:
				print("Current file: %s" % filepath)
			except:
				pass
			print(crash_traceback)
			print("ranger crashed.  " \
				"Please report this traceback at:")
			print("http://savannah.nongnu.org/bugs/?group=ranger&func=additem")
			return 1
		return 0


def parse_arguments():
	"""Parse the program arguments"""
	from optparse import OptionParser
	from os.path import expanduser
	from ranger import CONFDIR, USAGE, VERSION
	from ranger.ext.openstruct import OpenStruct

	if 'XDG_CONFIG_HOME' in os.environ and os.environ['XDG_CONFIG_HOME']:
		default_confdir = os.environ['XDG_CONFIG_HOME'] + '/ranger'
	else:
		default_confdir = CONFDIR

	parser = OptionParser(usage=USAGE, version=VERSION)

	parser.add_option('-d', '--debug', action='store_true',
			help="activate debug mode")
	parser.add_option('-c', '--clean', action='store_true',
			help="don't touch/require any config files. ")
	parser.add_option('--copy-config', type='string', metavar='which',
			help="copy the default configs to the local config directory. "
			"Possible values: all, rc, rifle, commands, options, scope")
	parser.add_option('--fail-unless-cd', action='store_true',
			help="experimental: return the exit code 1 if ranger is" \
					"used to run a file (with `ranger filename`)")
	parser.add_option('-r', '--confdir', type='string',
			metavar='dir', default=default_confdir,
			help="the configuration directory. (%default)")
	parser.add_option('-m', '--mode', type='int', default=0, metavar='n',
			help="if a filename is supplied, run it with this mode")
	parser.add_option('-f', '--flags', type='string', default='',
			metavar='string',
			help="if a filename is supplied, run it with these flags.")
	parser.add_option('--choosefile', type='string', metavar='TARGET',
			help="Makes ranger act like a file chooser. When opening "
			"a file, it will quit and write the name of the selected "
			"file to TARGET.")
	parser.add_option('--choosefiles', type='string', metavar='TARGET',
			help="Makes ranger act like a file chooser for multiple files "
			"at once. When opening a file, it will quit and write the name "
			"of all selected files to TARGET.")
	parser.add_option('--choosedir', type='string', metavar='TARGET',
			help="Makes ranger act like a directory chooser. When ranger quits"
			", it will write the name of the last visited directory to TARGET")
	parser.add_option('--list-unused-keys', action='store_true',
			help="List common keys which are not bound to any action.")
	parser.add_option('--selectfile', type='string', metavar='filepath',
			help="Open ranger with supplied file selected.")
	parser.add_option('--list-tagged-files', type='string', default=None,
			metavar='tag',
			help="List all files which are tagged with the given tag, default: *")
	parser.add_option('--profile', action='store_true',
			help="Print statistics of CPU usage on exit.")
	parser.add_option('--cmd', action='append', type='string', metavar='COMMAND',
			help="Execute COMMAND after the configuration has been read. "
			"Use this option multiple times to run multiple commands.")

	options, positional = parser.parse_args()
	arg = OpenStruct(options.__dict__, targets=positional)
	arg.confdir = expanduser(arg.confdir)

	return arg


def load_settings(fm, clean):
	from ranger.core.actions import Actions
	import ranger.core.shared
	import ranger.api.commands
	from ranger.config import commands

	# Load default commands
	fm.commands = ranger.api.commands.CommandContainer()
	exclude = ['settings']
	include = [name for name in dir(Actions) if name not in exclude]
	fm.commands.load_commands_from_object(fm, include)
	fm.commands.load_commands_from_module(commands)

	if not clean:
		allow_access_to_confdir(ranger.arg.confdir, True)

		# Load custom commands
		try:
			import commands
			fm.commands.load_commands_from_module(commands)
		except ImportError:
			pass

		# Load rc.conf
		custom_conf = fm.confpath('rc.conf')
		default_conf = fm.relpath('config', 'rc.conf')
		load_default_rc = fm.settings.load_default_rc

		if load_default_rc:
			fm.source(default_conf)
		if os.access(custom_conf, os.R_OK):
			fm.source(custom_conf)

		# XXX Load plugins (experimental)
		try:
			plugindir = fm.confpath('plugins')
			plugins = [p[:-3] for p in os.listdir(plugindir) \
					if p.endswith('.py') and not p.startswith('_')]
		except:
			pass
		else:
			if not os.path.exists(fm.confpath('plugins', '__init__.py')):
				f = open(fm.confpath('plugins', '__init__.py'), 'w')
				f.close()

			ranger.fm = fm
			for plugin in sorted(plugins):
				try:
					module = __import__('plugins', fromlist=[plugin])
					fm.log.append("Loaded plugin '%s'." % module)
				except Exception as e:
					fm.log.append("Error in plugin '%s'" % plugin)
					import traceback
					for line in traceback.format_exception_only(type(e), e):
						fm.log.append(line)
			ranger.fm = None

		allow_access_to_confdir(ranger.arg.confdir, False)
	else:
		fm.source(fm.relpath('config', 'rc.conf'))


def allow_access_to_confdir(confdir, allow):
	import sys
	from errno import EEXIST

	if allow:
		try:
			os.makedirs(confdir)
		except OSError as err:
			if err.errno != EEXIST:  # EEXIST means it already exists
				print("This configuration directory could not be created:")
				print(confdir)
				print("To run ranger without the need for configuration")
				print("files, use the --clean option.")
				raise SystemExit()
		if not confdir in sys.path:
			sys.path[0:0] = [confdir]
	else:
		if sys.path[0] == confdir:
			del sys.path[0]