summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.flake82
-rw-r--r--.gitignore3
-rw-r--r--Dockerfile8
-rw-r--r--README.md3
-rw-r--r--doc/ranger.141
-rw-r--r--doc/ranger.pod42
-rw-r--r--examples/plugin_avfs.py33
-rw-r--r--examples/rc_emacs.conf5
-rw-r--r--ranger/colorschemes/default.py2
-rw-r--r--ranger/config/__init__.py2
-rwxr-xr-xranger/config/commands.py26
-rw-r--r--ranger/config/rc.conf22
-rw-r--r--ranger/config/rifle.conf3
-rw-r--r--ranger/container/directory.py5
-rw-r--r--ranger/container/fsobject.py6
-rw-r--r--ranger/container/settings.py1
-rw-r--r--ranger/core/actions.py63
-rw-r--r--ranger/core/fm.py4
-rw-r--r--ranger/core/main.py80
-rwxr-xr-xranger/data/scope.sh3
-rw-r--r--ranger/ext/direction.py4
-rw-r--r--ranger/ext/human_readable.py4
-rw-r--r--ranger/ext/img_display.py16
-rwxr-xr-xranger/ext/rifle.py8
-rw-r--r--ranger/gui/context.py2
-rw-r--r--ranger/gui/ui.py6
-rw-r--r--ranger/gui/widgets/__init__.py8
-rw-r--r--ranger/gui/widgets/browsercolumn.py4
-rw-r--r--ranger/gui/widgets/statusbar.py15
-rw-r--r--ranger/gui/widgets/view_base.py4
-rw-r--r--tests/ranger/core/__init__.py0
-rw-r--r--tests/ranger/core/test_main.py18
32 files changed, 337 insertions, 106 deletions
diff --git a/.flake8 b/.flake8
index f5072c08..b77e4e6c 100644
--- a/.flake8
+++ b/.flake8
@@ -1,3 +1,3 @@
 [flake8]
 max-line-length = 99
-ignore = E221
+ignore = E221,W503
diff --git a/.gitignore b/.gitignore
index 88c75b90..73ca85e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,6 @@
 /ranger_fm.egg-info
 
 /stuff/*
+
+.idea
+.pytest_cache
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..36ad0a95
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,8 @@
+# Usage instructions:
+# 1. "docker build -t ranger/ranger:latest ."
+# 2. "docker run -it ranger/ranger"
+
+FROM debian
+
+RUN apt-get update && apt-get install -y ranger
+ENTRYPOINT ["ranger"]
diff --git a/README.md b/README.md
index ef644ae6..8abb7265 100644
--- a/README.md
+++ b/README.md
@@ -75,10 +75,11 @@ Optional, for enhanced file previews (with `scope.sh`):
 * `highlight` or `pygmentize` for syntax highlighting of code
 * `atool`, `bsdtar` and/or `unrar` for previews of archives
 * `lynx`, `w3m` or `elinks` for previews of html pages
-* `pdftotext` for pdf previews
+* `pdftotext` or `mutool` for pdf previews
 * `transmission-show` for viewing bit-torrent information
 * `mediainfo` or `exiftool` for viewing information about media files
 * `odt2txt` for OpenDocument text files (`odt`, `ods`, `odp` and `sxw`)
+* `chardet` (Python package) for improved encoding detection of text files
 
 
 Installing
diff --git a/doc/ranger.1 b/doc/ranger.1
index f362d263..1290cb58 100644
--- a/doc/ranger.1
+++ b/doc/ranger.1
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 4.07 (Pod::Simple 3.32)
+.\" Automatically generated by Pod::Man 4.09 (Pod::Simple 3.35)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -129,7 +129,7 @@
 .\" ========================================================================
 .\"
 .IX Title "RANGER 1"
-.TH RANGER 1 "ranger-1.9.1" "05.03.2018" "ranger manual"
+.TH RANGER 1 "ranger-1.9.1" "2018-05-14" "ranger manual"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -145,7 +145,7 @@ ranger \- visual file manager
 [\fB\-\-choosedir\fR=\fItarget\fR] [\fB\-\-selectfile\fR=\fIfilepath\fR]
 [\fB\-\-show\-only\-dirs\fR]
 [\fB\-\-list\-unused\-keys\fR] [\fB\-\-list\-tagged\-files\fR=\fItag\fR]
-[\fB\-\-profile\fR] [\fB\-\-cmd\fR=\fIcommand\fR] [\fIpath\fR]
+[\fB\-\-profile\fR] [\fB\-\-cmd\fR=\fIcommand\fR] [\fIpath ...\fR]
 .SH "DESCRIPTION"
 .IX Header "DESCRIPTION"
 ranger is a console file manager with \s-1VI\s0 key bindings.
@@ -172,6 +172,12 @@ with other software.  They are usually installed to
 The man page of \fIrifle\fR\|(1) describes the functions of the file opener
 .PP
 The section \fI\s-1LINKS\s0\fR of this man page contains further resources.
+.SH "POSITIONAL ARGUMENTS"
+.IX Header "POSITIONAL ARGUMENTS"
+.IP "\fIpath ...\fR" 14
+.IX Item "path ..."
+Each path will be opened in a tab and if the path is a file it will be selected.
+Omitting this is equivalent to providing the current directory.
 .SH "OPTIONS"
 .IX Header "OPTIONS"
 .IP "\fB\-d\fR, \fB\-\-debug\fR" 14
@@ -225,7 +231,8 @@ Allows you to pick a directory with ranger.  When you exit ranger, it will
 write the last visited directory into \fItargetfile\fR.
 .IP "\fB\-\-selectfile\fR=\fItargetfile\fR" 14
 .IX Item "--selectfile=targetfile"
-Open ranger with \fItargetfile\fR selected.
+Open ranger with \fItargetfile\fR selected. This is a legacy option, superseded by
+the behavior for the \s-1POSITIONAL ARGUMENTS.\s0
 .IP "\fB\-\-show\-only\-dirs\fR" 14
 .IX Item "--show-only-dirs"
 Display only the directories. May be used in conjunction with
@@ -267,10 +274,10 @@ typing \fI"<tagname>\fR.
 By default, only text files are previewed, but you can enable external preview
 scripts by setting the option \f(CW\*(C`use_preview_script\*(C'\fR and \f(CW\*(C`preview_files\*(C'\fR to true.
 .PP
-This default script is \fI~/.config/ranger/scope.sh\fR. It contains more
+This default script is \fI\f(CI%rangerdir\fI/data/scope.sh\fR. It contains more
 documentation and calls to the programs \fIlynx\fR and \fIelinks\fR for html,
 \&\fIhighlight\fR for text/code, \fIimg2txt\fR for images, \fIatool\fR for archives,
-\&\fIpdftotext\fR for PDFs and \fImediainfo\fR for video and audio files.
+\&\fIpdftotext\fR or \fImutool\fR for PDFs and \fImediainfo\fR for video and audio files.
 .PP
 Install these programs (just the ones you need) and scope.sh will automatically
 use them.
@@ -463,7 +470,7 @@ sample plugins in the \fI/usr/share/doc/ranger/examples/\fR directory, including
 hello-world plugin that describes this procedure.
 .SH "KEY BINDINGS"
 .IX Header "KEY BINDINGS"
-Key bindings are defined in the file \fIranger/config/rc.conf\fR.  Check this
+Key bindings are defined in the file \fI\f(CI%rangerdir\fI/config/rc.conf\fR.  Check this
 file for a list of all key bindings.  You can copy it to your local
 configuration directory with the \-\-copy\-config=rc option.
 .PP
@@ -599,7 +606,7 @@ Toggle the mark-status of all files
 .IP "V" 14
 .IX Item "V"
 Starts the visual mode, which selects all files between the starting point and
-the cursor until you press \s-1ESC. \s0 To unselect files in the same way, use \*(L"uV\*(R".
+the cursor until you press \s-1ESC.\s0  To unselect files in the same way, use \*(L"uV\*(R".
 .IP "/" 14
 Search for files in the current directory.
 .IP ":" 14
@@ -702,7 +709,7 @@ in ranger.
 .IP "automatically_count_files [bool]" 4
 .IX Item "automatically_count_files [bool]"
 Should ranger count and display the number of files in each directory
-as soon as it's visible?  This gets slow with remote file sytems.  Turning it
+as soon as it's visible?  This gets slow with remote file systems.  Turning it
 off will still allow you to see the number of files after entering the
 directory.
 .IP "autosave_bookmarks [bool]" 4
@@ -764,6 +771,9 @@ Display the file size in the main column?
 .IP "display_size_in_status_bar [bool]" 4
 .IX Item "display_size_in_status_bar [bool]"
 Display the file size in the status bar?
+.IP "display_free_space_in_status_bar [bool]" 4
+.IX Item "display_free_space_in_status_bar [bool]"
+Display the free disk space in the status bar?
 .IP "display_tags_in_all_columns [bool]" 4
 .IX Item "display_tags_in_all_columns [bool]"
 Display tags in all columns?
@@ -989,7 +999,7 @@ ranger.  For your convenience, this is a list of the \*(L"public\*(R" commands i
 .Vb 10
 \& alias [newcommand] [oldcommand]
 \& bulkrename
-\& cd [directory]
+\& cd [path]
 \& chain command1[; command2[; command3...]]
 \& chmod octal_number
 \& cmap key command
@@ -1065,10 +1075,10 @@ renaming according to the changes you did in the file.
 .Sp
 This shell script is opened in an editor for you to review.  After you close
 it, it will be executed.
-.IP "cd [\fIdirectory\fR]" 2
-.IX Item "cd [directory]"
-The cd command changes the directory.  The command \f(CW\*(C`:cd \-\*(C'\fR is equivalent to
-typing ``.
+.IP "cd [\fIpath\fR]" 2
+.IX Item "cd [path]"
+The cd command changes the directory.  If path is a file, selects that file.
+The command \f(CW\*(C`:cd \-\*(C'\fR is equivalent to typing ``.
 .IP "chain \fIcommand1\fR[; \fIcommand2\fR[; \fIcommand3\fR...]]" 2
 .IX Item "chain command1[; command2[; command3...]]"
 Combines multiple commands into one, separated by semicolons.
@@ -1425,6 +1435,9 @@ being bound despite the corresponding line being removed from the user's copy of
 the configuration file. This behavior may be disabled with an environment
 variable (see also: \fB\s-1ENVIRONMENT\s0\fR). Note: All other configuration files only
 read from one source; i.e. default \s-1OR\s0 user, not both.
+\&\fIrc.conf\fR and \fIcommands.py\fR are additionally read from \fI/etc/ranger\fR if they
+exist for system-wide configuration, user configuration overrides system
+configuration which overrides the default configuration.
 .PP
 When starting ranger with the \fB\-\-clean\fR option, it will not access or create
 any of these files.
diff --git a/doc/ranger.pod b/doc/ranger.pod
index 8599a1a6..4cac8ef9 100644
--- a/doc/ranger.pod
+++ b/doc/ranger.pod
@@ -14,7 +14,7 @@ B<ranger> [B<--version>] [B<--help>] [B<--debug>] [B<--clean>]
 [B<--choosedir>=I<target>] [B<--selectfile>=I<filepath>]
 [B<--show-only-dirs>]
 [B<--list-unused-keys>] [B<--list-tagged-files>=I<tag>]
-[B<--profile>] [B<--cmd>=I<command>] [I<path>]
+[B<--profile>] [B<--cmd>=I<command>] [I<path ...>]
 
 
 
@@ -53,6 +53,20 @@ The section I<LINKS> of this man page contains further resources.
 
 
 
+=head1 POSITIONAL ARGUMENTS
+
+=over 14
+
+=item I<path ...>
+
+Each path will be opened in a tab and if the path is a file it will be selected.
+Omitting this is equivalent to providing the current directory.
+
+=back
+
+
+
+
 =head1 OPTIONS
 
 =over 14
@@ -117,7 +131,8 @@ write the last visited directory into I<targetfile>.
 
 =item B<--selectfile>=I<targetfile>
 
-Open ranger with I<targetfile> selected.
+Open ranger with I<targetfile> selected. This is a legacy option, superseded by
+the behavior for the POSITIONAL ARGUMENTS.
 
 =item B<--show-only-dirs>
 
@@ -174,10 +189,10 @@ typing I<"<tagnameE<gt>>.
 By default, only text files are previewed, but you can enable external preview
 scripts by setting the option C<use_preview_script> and C<preview_files> to true.
 
-This default script is F<~/.config/ranger/scope.sh>. It contains more
+This default script is F<%rangerdir/data/scope.sh>. It contains more
 documentation and calls to the programs I<lynx> and I<elinks> for html,
 I<highlight> for text/code, I<img2txt> for images, I<atool> for archives,
-I<pdftotext> for PDFs and I<mediainfo> for video and audio files.
+I<pdftotext> or I<mutool> for PDFs and I<mediainfo> for video and audio files.
 
 Install these programs (just the ones you need) and scope.sh will automatically
 use them.
@@ -364,7 +379,7 @@ hello-world plugin that describes this procedure.
 
 =head1 KEY BINDINGS
 
-Key bindings are defined in the file F<ranger/config/rc.conf>.  Check this
+Key bindings are defined in the file F<%rangerdir/config/rc.conf>.  Check this
 file for a list of all key bindings.  You can copy it to your local
 configuration directory with the --copy-config=rc option.
 
@@ -678,7 +693,7 @@ in ranger.
 =item automatically_count_files [bool]
 
 Should ranger count and display the number of files in each directory
-as soon as it's visible?  This gets slow with remote file sytems.  Turning it
+as soon as it's visible?  This gets slow with remote file systems.  Turning it
 off will still allow you to see the number of files after entering the
 directory.
 
@@ -752,6 +767,10 @@ Display the file size in the main column?
 
 Display the file size in the status bar?
 
+=item display_free_space_in_status_bar [bool]
+
+Display the free disk space in the status bar?
+
 =item display_tags_in_all_columns [bool]
 
 Display tags in all columns?
@@ -1020,7 +1039,7 @@ ranger.  For your convenience, this is a list of the "public" commands including
 
  alias [newcommand] [oldcommand]
  bulkrename
- cd [directory]
+ cd [path]
  chain command1[; command2[; command3...]]
  chmod octal_number
  cmap key command
@@ -1100,10 +1119,10 @@ renaming according to the changes you did in the file.
 This shell script is opened in an editor for you to review.  After you close
 it, it will be executed.
 
-=item cd [I<directory>]
+=item cd [I<path>]
 
-The cd command changes the directory.  The command C<:cd -> is equivalent to
-typing ``.
+The cd command changes the directory.  If path is a file, selects that file.
+The command C<:cd -> is equivalent to typing ``.
 
 =item chain I<command1>[; I<command2>[; I<command3>...]]
 
@@ -1512,6 +1531,9 @@ being bound despite the corresponding line being removed from the user's copy of
 the configuration file. This behavior may be disabled with an environment
 variable (see also: B<ENVIRONMENT>). Note: All other configuration files only
 read from one source; i.e. default OR user, not both.
+F<rc.conf> and F<commands.py> are additionally read from F</etc/ranger> if they
+exist for system-wide configuration, user configuration overrides system
+configuration which overrides the default configuration.
 
 When starting ranger with the B<--clean> option, it will not access or create
 any of these files.
diff --git a/examples/plugin_avfs.py b/examples/plugin_avfs.py
new file mode 100644
index 00000000..07968a03
--- /dev/null
+++ b/examples/plugin_avfs.py
@@ -0,0 +1,33 @@
+# Tested with ranger 1.9.1
+#
+# A very simple and possibly buggy support for AVFS
+# (http://avf.sourceforge.net/), that allows ranger to handle
+# archives.
+#
+# Run `:avfs' to browse the selected archive.
+
+from __future__ import (absolute_import, division, print_function)
+
+import os
+import os.path
+
+from ranger.api.commands import Command
+
+
+class avfs(Command):  # pylint: disable=invalid-name
+    avfs_root = os.path.join(os.environ["HOME"], ".avfs")
+    avfs_suffix = "#"
+
+    def execute(self):
+        if os.path.isdir(self.avfs_root):
+            archive_directory = "".join([
+                self.avfs_root,
+                self.fm.thisfile.path,
+                self.avfs_suffix,
+            ])
+            if os.path.isdir(archive_directory):
+                self.fm.cd(archive_directory)
+            else:
+                self.fm.notify("This file cannot be handled by avfs.", bad=True)
+        else:
+            self.fm.notify("Install `avfs' and run `mountavfs' first.", bad=True)
diff --git a/examples/rc_emacs.conf b/examples/rc_emacs.conf
index 26074a42..0462282e 100644
--- a/examples/rc_emacs.conf
+++ b/examples/rc_emacs.conf
@@ -122,6 +122,9 @@ set mouse_enabled true
 set display_size_in_main_column true
 set display_size_in_status_bar true
 
+# Display the free disk space in the status bar?
+set display_free_space_in_status_bar true
+
 # Display files tags in all columns or only in main column?
 set display_tags_in_all_columns true
 
@@ -129,7 +132,7 @@ set display_tags_in_all_columns true
 set update_title false
 
 # Set the title to "ranger" in the tmux program?
-set update_tmux_title false
+set update_tmux_title true
 
 # Shorten the title if it gets long?  The number defines how many
 # directories are displayed at once, 0 turns off this feature.
diff --git a/ranger/colorschemes/default.py b/ranger/colorschemes/default.py
index c88cdc7c..350c8359 100644
--- a/ranger/colorschemes/default.py
+++ b/ranger/colorschemes/default.py
@@ -138,6 +138,8 @@ class Default(ColorScheme):
             attr &= ~bold
             if context.vcsconflict:
                 fg = magenta
+            elif context.vcsuntracked:
+                fg = cyan
             elif context.vcschanged:
                 fg = red
             elif context.vcsunknown:
diff --git a/ranger/config/__init__.py b/ranger/config/__init__.py
index 71df3cb3..0facbdf8 100644
--- a/ranger/config/__init__.py
+++ b/ranger/config/__init__.py
@@ -1 +1 @@
-"""Default options and configration files"""
+"""Default options and configuration files"""
diff --git a/ranger/config/commands.py b/ranger/config/commands.py
index a3837d8e..7f5fc764 100755
--- a/ranger/config/commands.py
+++ b/ranger/config/commands.py
@@ -3,8 +3,9 @@
 # This configuration file is licensed under the same terms as ranger.
 # ===================================================================
 #
-# NOTE: If you copied this file to ~/.config/ranger/commands_full.py,
-# then it will NOT be loaded by ranger, and only serve as a reference.
+# NOTE: If you copied this file to /etc/ranger/commands_full.py or
+# ~/.config/ranger/commands_full.py, then it will NOT be loaded by ranger,
+# and only serve as a reference.
 #
 # ===================================================================
 # This file contains ranger's commands.
@@ -13,9 +14,14 @@
 # Note that additional commands are automatically generated from the methods
 # of the class ranger.core.actions.Actions.
 #
-# You can customize commands in the file ~/.config/ranger/commands.py.
-# It has the same syntax as this file.  In fact, you can just copy this
-# file there with `ranger --copy-config=commands' and make your modifications.
+# You can customize commands in the files /etc/ranger/commands.py (system-wide)
+# and ~/.config/ranger/commands.py (per user).
+# They have the same syntax as this file.  In fact, you can just copy this
+# file to ~/.config/ranger/commands_full.py with
+# `ranger --copy-config=commands_full' and make your modifications, don't
+# forget to rename it to commands.py.  You can also use
+# `ranger --copy-config=commands' to copy a short sample commands.py that
+# has everything you need to get started.
 # But make sure you update your configs when you update ranger.
 #
 # ===================================================================
@@ -120,9 +126,10 @@ class echo(Command):
 
 
 class cd(Command):
-    """:cd [-r] <dirname>
+    """:cd [-r] <path>
 
     The cd command changes the directory.
+    If the path is a file, selects that file.
     The command 'cd -' is equivalent to typing ``.
     Using the option "-r" will get you to the real path.
     """
@@ -1393,6 +1400,8 @@ class scout(Command):
                 self.fm.cd(pattern)
             else:
                 self.fm.move(right=1)
+                if self.quickly_executed:
+                    self.fm.block_input(0.5)
 
         if self.KEEP_OPEN in flags and thisdir != self.fm.thisdir:
             # reopen the console:
@@ -1718,6 +1727,7 @@ class yank(Command):
 
     modes = {
         '': 'basename',
+        'name_without_extension': 'basename_without_extension',
         'name': 'basename',
         'dir': 'dirname',
         'path': 'path',
@@ -1750,7 +1760,9 @@ class yank(Command):
 
         clipboard_commands = clipboards()
 
-        selection = self.get_selection_attr(self.modes[self.arg(1)])
+        mode = self.modes[self.arg(1)]
+        selection = self.get_selection_attr(mode)
+
         new_clipboard_contents = "\n".join(selection)
         for command in clipboard_commands:
             process = subprocess.Popen(command, universal_newlines=True,
diff --git a/ranger/config/rc.conf b/ranger/config/rc.conf
index 772ce20e..1296f1ca 100644
--- a/ranger/config/rc.conf
+++ b/ranger/config/rc.conf
@@ -1,7 +1,8 @@
 # ===================================================================
 # This file contains the default startup commands for ranger.
-# To change them, it is recommended to create the file
-# ~/.config/ranger/rc.conf and add your custom commands there.
+# To change them, it is recommended to create either /etc/ranger/rc.conf
+# (system-wide) or ~/.config/ranger/rc.conf (per user) and add your custom
+# commands there.
 #
 # If you copy this whole file there, you may want to set the environment
 # variable RANGER_LOAD_DEFAULT_RC to FALSE to avoid loading it twice.
@@ -137,6 +138,9 @@ set mouse_enabled true
 set display_size_in_main_column true
 set display_size_in_status_bar true
 
+# Display the free disk space in the status bar?
+set display_free_space_in_status_bar true
+
 # Display files tags in all columns or only in main column?
 set display_tags_in_all_columns true
 
@@ -144,7 +148,7 @@ set display_tags_in_all_columns true
 set update_title false
 
 # Set the title to "ranger" in the tmux program?
-set update_tmux_title false
+set update_tmux_title true
 
 # Shorten the title if it gets long?  The number defines how many
 # directories are displayed at once, 0 turns off this feature.
@@ -253,6 +257,10 @@ set wrap_scroll false
 # directories, files and symlinks respectively.
 set global_inode_type_filter
 
+# This setting allows to freeze the list of files to save I/O bandwidth.  It
+# should be 'false' during start-up, but you can toggle it by pressing F.
+set freeze_files false
+
 # ===================================================================
 # == Local Options
 # ===================================================================
@@ -274,8 +282,8 @@ alias qall  quitall
 alias qall! quitall!
 alias setl  setlocal
 
-alias filter     scout -prt
-alias find       scout -aeit
+alias filter     scout -prts
+alias find       scout -aets
 alias mark       scout -mr
 alias unmark     scout -Mr
 alias search     scout -rs
@@ -390,6 +398,7 @@ map gL cd -r %f
 map go cd /opt
 map gv cd /var
 map gm cd /media
+map gi eval fm.cd('/run/media/' + os.getenv('USER'))
 map gM cd /mnt
 map gs cd /srv
 map gp cd /tmp
@@ -405,6 +414,7 @@ map dU shell -p du --max-depth=1 -h --apparent-size | sort -rh
 map yp yank path
 map yd yank dir
 map yn yank name
+map y. yank name_without_extension
 
 # Filesystem Operations
 map =  chmod
@@ -508,6 +518,8 @@ map zc    set collapse_preview!
 map zd    set sort_directories_first!
 map zh    set show_hidden!
 map <C-h> set show_hidden!
+copymap <C-h> <backspace>
+copymap <backspace> <backspace2>
 map zI    set flushinput!
 map zi    set preview_images!
 map zm    set mouse_enabled!
diff --git a/ranger/config/rifle.conf b/ranger/config/rifle.conf
index e2653a76..b1a9bb71 100644
--- a/ranger/config/rifle.conf
+++ b/ranger/config/rifle.conf
@@ -151,7 +151,7 @@ ext pdf, has atril,    X, flag f = atril -- "$@"
 ext pdf, has okular,   X, flag f = okular -- "$@"
 ext pdf, has epdfview, X, flag f = epdfview -- "$@"
 ext pdf, has qpdfview, X, flag f = qpdfview "$@"
-ext pdf, has open,     X, flat f = open "$@"
+ext pdf, has open,     X, flag f = open "$@"
 
 ext docx?, has catdoc,       terminal = catdoc -- "$@" | "$PAGER"
 
@@ -183,6 +183,7 @@ mime ^image, has eog,       X, flag f = eog -- "$@"
 mime ^image, has eom,       X, flag f = eom -- "$@"
 mime ^image, has nomacs,    X, flag f = nomacs -- "$@"
 mime ^image, has geeqie,    X, flag f = geeqie -- "$@"
+mime ^image, has gwenview,  X, flag f = gwenview -- "$@"
 mime ^image, has gimp,      X, flag f = gimp -- "$@"
 ext xcf,                    X, flag f = gimp -- "$@"
 
diff --git a/ranger/container/directory.py b/ranger/container/directory.py
index 18b1687c..e281e6c9 100644
--- a/ranger/container/directory.py
+++ b/ranger/container/directory.py
@@ -338,8 +338,9 @@ class Directory(  # pylint: disable=too-many-instance-attributes,too-many-public
                         dirlist = [
                             os.path.join("/", dirpath, d)
                             for d in dirnames
-                            if self.flat == -1 or
-                            (dirpath.count(os.path.sep) - mypath.count(os.path.sep)) <= self.flat
+                            if self.flat == -1
+                            or (dirpath.count(os.path.sep)
+                                - mypath.count(os.path.sep)) <= self.flat
                         ]
                         filelist += dirlist
                         filelist += [os.path.join("/", dirpath, f) for f in filenames]
diff --git a/ranger/container/fsobject.py b/ranger/container/fsobject.py
index 0c9f70f6..37151ecf 100644
--- a/ranger/container/fsobject.py
+++ b/ranger/container/fsobject.py
@@ -6,7 +6,7 @@ from __future__ import (absolute_import, division, print_function)
 import re
 from grp import getgrgid
 from os import lstat, stat
-from os.path import abspath, basename, dirname, realpath, relpath
+from os.path import abspath, basename, dirname, realpath, relpath, splitext
 from pwd import getpwuid
 from time import time
 
@@ -171,6 +171,10 @@ class FileSystemObject(  # pylint: disable=too-many-instance-attributes,too-many
         return basename_list
 
     @lazy_property
+    def basename_without_extension(self):
+        return splitext(self.basename)[0]
+
+    @lazy_property
     def safe_basename(self):
         return self.basename.translate(_SAFE_STRING_TABLE)
 
diff --git a/ranger/container/settings.py b/ranger/container/settings.py
index 9f54f24d..170ace5a 100644
--- a/ranger/container/settings.py
+++ b/ranger/container/settings.py
@@ -38,6 +38,7 @@ ALLOWED_SETTINGS = {
     'dirname_in_tabs': bool,
     'display_size_in_main_column': bool,
     'display_size_in_status_bar': bool,
+    "display_free_space_in_status_bar": bool,
     'display_tags_in_all_columns': bool,
     'draw_borders': bool,
     'draw_progress_bar_in_status_bar': bool,
diff --git a/ranger/core/actions.py b/ranger/core/actions.py
index 68b362b9..e8c47b9f 100644
--- a/ranger/core/actions.py
+++ b/ranger/core/actions.py
@@ -409,7 +409,8 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
         # ranger can act as a file chooser when running with --choosefile=...
         if mode == 0 and 'label' not in kw:
             if ranger.args.choosefile:
-                open(ranger.args.choosefile, 'w').write(self.fm.thisfile.path)
+                with open(ranger.args.choosefile, 'w') as fobj:
+                    fobj.write(self.fm.thisfile.path)
 
             if ranger.args.choosefiles:
                 paths = []
@@ -995,6 +996,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
 
         if not self.settings.preview_script or not self.settings.use_preview_script:
             try:
+                # XXX: properly determine file's encoding
                 return codecs.open(path, 'r', errors='ignore')
             # IOError for Python2, OSError for Python3
             except (IOError, OSError):
@@ -1080,14 +1082,7 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
                 data[(-1, -1)] = None
                 data['foundpreview'] = False
             elif rcode == 2:
-                fobj = codecs.open(path, 'r', errors='ignore')
-                try:
-                    data[(-1, -1)] = fobj.read(1024 * 32)
-                except UnicodeDecodeError:
-                    fobj.close()
-                    fobj = codecs.open(path, 'r', encoding='latin-1', errors='ignore')
-                    data[(-1, -1)] = fobj.read(1024 * 32)
-                fobj.close()
+                data[(-1, -1)] = self.read_text_file(path, 1024 * 32)
             else:
                 data[(-1, -1)] = None
 
@@ -1128,6 +1123,35 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
 
         return None
 
+    @staticmethod
+    def read_text_file(path, count=None):
+        """Encoding-aware reading of a text file."""
+        try:
+            import chardet
+        except ImportError:
+            # Guess encoding ourselves. These should be the most frequently used ones.
+            encodings = ('utf-8', 'utf-16')
+            for encoding in encodings:
+                try:
+                    with codecs.open(path, 'r', encoding=encoding) as fobj:
+                        text = fobj.read(count)
+                except UnicodeDecodeError:
+                    pass
+                else:
+                    LOG.debug("guessed encoding of '%s' as %r", path, encoding)
+                    return text
+        else:
+            with open(path, 'rb') as fobj:
+                data = fobj.read(count)
+            result = chardet.detect(data)
+            LOG.debug("chardet guess for '%s': %s", path, result)
+            guessed_encoding = result['encoding']
+            return codecs.decode(data, guessed_encoding, 'replace')
+
+        # latin-1 as the last resort
+        with codecs.open(path, 'r', encoding='latin-1', errors='replace') as fobj:
+            return fobj.read(count)
+
     # --------------------------
     # -- Tabs
     # --------------------------
@@ -1203,10 +1227,10 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
     def tab_new(self, path=None, narg=None):
         if narg:
             return self.tab_open(narg, path)
-        for i in range(1, 10):
-            if i not in self.tabs:
-                return self.tab_open(i, path)
-        return None
+        i = 1
+        while i in self.tabs:
+            i += 1
+        return self.tab_open(i, path)
 
     def tab_switch(self, path, create_directory=False):
         """Switches to tab of given path, opening a new tab as necessary.
@@ -1254,7 +1278,18 @@ class Actions(  # pylint: disable=too-many-instance-attributes,too-many-public-m
 
     def get_tab_list(self):
         assert self.tabs, "There must be at least 1 tab at all times"
-        return sorted(self.tabs)
+
+        class NaturalOrder(object):  # pylint: disable=too-few-public-methods
+            def __init__(self, obj):
+                self.obj = obj
+
+            def __lt__(self, other):
+                try:
+                    return self.obj < other.obj
+                except TypeError:
+                    return str(self.obj) < str(other.obj)
+
+        return sorted(self.tabs, key=NaturalOrder)
 
     # --------------------------
     # -- Overview of internals
diff --git a/ranger/core/fm.py b/ranger/core/fm.py
index c55a3922..d85dd48c 100644
--- a/ranger/core/fm.py
+++ b/ranger/core/fm.py
@@ -424,5 +424,5 @@ class FM(Actions,  # pylint: disable=too-many-instance-attributes
             if not ranger.args.clean and self.settings.save_tabs_on_exit and len(self.tabs) > 1:
                 with open(self.datapath('tabs'), 'a') as fobj:
                     # Don't save active tab since launching ranger changes the active tab
-                    fobj.write('\0'.join(v.path for t, v in self.tabs.items()
-                                         if t != self.current_tab) + '\0\0')
+                    fobj.write('\0'.join(v.path for t, v in self.tabs.items()) +
+                               '\0\0')
diff --git a/ranger/core/main.py b/ranger/core/main.py
index 4adea918..b5d0af77 100644
--- a/ranger/core/main.py
+++ b/ranger/core/main.py
@@ -89,14 +89,12 @@ def main(
 
     SettingsAware.settings_set(Settings())
 
+    # TODO: deprecate --selectfile
     if args.selectfile:
         args.selectfile = os.path.abspath(args.selectfile)
         args.paths.insert(0, os.path.dirname(args.selectfile))
 
-    if args.paths:
-        paths = [p[7:] if p.startswith('file:///') else p for p in args.paths]
-    else:
-        paths = [os.environ.get('PWD', os.getcwd())]
+    paths = get_paths(args)
     paths_inaccessible = []
     for path in paths:
         try:
@@ -182,6 +180,7 @@ def main(
             fm.select_file(args.selectfile)
 
         if args.cmd:
+            fm.enter_dir(fm.thistab.path)
             for command in args.cmd:
                 fm.execute_console(command)
 
@@ -235,6 +234,24 @@ https://github.com/ranger/ranger/issues
         return exit_code  # pylint: disable=lost-exception
 
 
+def get_paths(args):
+    if args.paths:
+        prefix = 'file:///'
+        prefix_length = len(prefix)
+        paths = [path[prefix_length:] if path.startswith(prefix) else path for path in args.paths]
+    else:
+        start_directory = os.environ.get('PWD')
+        is_valid_start_directory = start_directory and os.path.exists(start_directory)
+        if not is_valid_start_directory:
+            start_directory = __get_home_directory()
+        paths = [start_directory]
+    return paths
+
+
+def __get_home_directory():
+    return os.path.expanduser('~')
+
+
 def xdg_path(env_var):
     path = os.environ.get(env_var)
     if path and os.path.isabs(path):
@@ -339,23 +356,50 @@ def load_settings(  # pylint: disable=too-many-locals,too-many-branches,too-many
     fm.commands.load_commands_from_module(commands_default)
 
     if not clean:
+        system_confdir = os.path.join(os.sep, 'etc', 'ranger')
+        if os.path.exists(system_confdir):
+            sys.path.append(system_confdir)
         allow_access_to_confdir(ranger.args.confdir, True)
 
         # Load custom commands
-        custom_comm_path = fm.confpath('commands.py')
-        if os.path.exists(custom_comm_path):
+        def import_file(name, path):  # From https://stackoverflow.com/a/67692
+            # pragma pylint: disable=no-name-in-module,import-error,no-member
+            if sys.version_info >= (3, 5):
+                import importlib.util as util
+                spec = util.spec_from_file_location(name, path)
+                module = util.module_from_spec(spec)
+                spec.loader.exec_module(module)
+            elif (3, 3) <= sys.version_info < (3, 5):
+                from importlib.machinery import SourceFileLoader
+                module = SourceFileLoader(name, path).load_module()
+            else:
+                import imp
+                module = imp.load_source(name, path)
+            # pragma pylint: enable=no-name-in-module,import-error,no-member
+            return module
+
+        def load_custom_commands(*paths):
             old_bytecode_setting = sys.dont_write_bytecode
             sys.dont_write_bytecode = True
-            try:
-                import commands as commands_custom
-                fm.commands.load_commands_from_module(commands_custom)
-            except ImportError as ex:
-                LOG.debug("Failed to import custom commands from '%s'", custom_comm_path)
-                LOG.exception(ex)
-            else:
-                LOG.debug("Loaded custom commands from '%s'", custom_comm_path)
+            for custom_comm_path in paths:
+                if os.path.exists(custom_comm_path):
+                    try:
+                        commands_custom = import_file('commands',
+                                                      custom_comm_path)
+                        fm.commands.load_commands_from_module(commands_custom)
+                    except ImportError as ex:
+                        LOG.debug("Failed to import custom commands from '%s'",
+                                  custom_comm_path)
+                        LOG.exception(ex)
+                    else:
+                        LOG.debug("Loaded custom commands from '%s'",
+                                  custom_comm_path)
             sys.dont_write_bytecode = old_bytecode_setting
 
+        system_comm_path = os.path.join(system_confdir, 'commands.py')
+        custom_comm_path = fm.confpath('commands.py')
+        load_custom_commands(system_comm_path, custom_comm_path)
+
         # XXX Load plugins (experimental)
         plugindir = fm.confpath('plugins')
         try:
@@ -394,12 +438,16 @@ def load_settings(  # pylint: disable=too-many-locals,too-many-branches,too-many
         allow_access_to_confdir(ranger.args.confdir, False)
         # Load rc.conf
         custom_conf = fm.confpath('rc.conf')
+        system_conf = os.path.join(system_confdir, 'rc.conf')
         default_conf = fm.relpath('config', 'rc.conf')
 
         custom_conf_is_readable = os.access(custom_conf, os.R_OK)
-        if (os.environ.get('RANGER_LOAD_DEFAULT_RC', 'TRUE').upper() != 'FALSE' or
-                not custom_conf_is_readable):
+        system_conf_is_readable = os.access(system_conf, os.R_OK)
+        if (os.environ.get('RANGER_LOAD_DEFAULT_RC', 'TRUE').upper() != 'FALSE'
+                or not (custom_conf_is_readable or system_conf_is_readable)):
             fm.source(default_conf)
+        if system_conf_is_readable:
+            fm.source(system_conf)
         if custom_conf_is_readable:
             fm.source(custom_conf)
 
diff --git a/ranger/data/scope.sh b/ranger/data/scope.sh
index 540a910e..25251533 100755
--- a/ranger/data/scope.sh
+++ b/ranger/data/scope.sh
@@ -60,7 +60,8 @@ handle_extension() {
         # PDF
         pdf)
             # Preview as text conversion
-            pdftotext -l 10 -nopgbrk -q -- "${FILE_PATH}" - && exit 5
+            pdftotext -l 10 -nopgbrk -q -- "${FILE_PATH}" - | fmt -w ${PV_WIDTH} && exit 5
+            mutool draw -F txt -i -- "${FILE_PATH}" 1-10 | fmt -w ${PV_WIDTH} && exit 5
             exiftool "${FILE_PATH}" && exit 5
             exit 1;;
 
diff --git a/ranger/ext/direction.py b/ranger/ext/direction.py
index 7df45556..33ebb604 100644
--- a/ranger/ext/direction.py
+++ b/ranger/ext/direction.py
@@ -97,8 +97,8 @@ class Direction(dict):
         return self.get('cycle') in (True, 'true', 'on', 'yes')
 
     def one_indexed(self):
-        return ('one_indexed' in self and
-                self.get('one_indexed') in (True, 'true', 'on', 'yes'))
+        return ('one_indexed' in self
+                and self.get('one_indexed') in (True, 'true', 'on', 'yes'))
 
     def multiply(self, n):
         for key in ('up', 'right', 'down', 'left'):
diff --git a/ranger/ext/human_readable.py b/ranger/ext/human_readable.py
index df74eabf..f365e594 100644
--- a/ranger/ext/human_readable.py
+++ b/ranger/ext/human_readable.py
@@ -15,6 +15,10 @@ def human_readable(byte, separator=' '):  # pylint: disable=too-many-return-stat
     '1023 M'
     """
 
+    # handle automatically_count_files false
+    if byte is None:
+        return ''
+
     # I know this can be written much shorter, but this long version
     # performs much better than what I had before.  If you attempt to
     # shorten this code, take performance into consideration.
diff --git a/ranger/ext/img_display.py b/ranger/ext/img_display.py
index 67941e27..78d71cb2 100644
--- a/ranger/ext/img_display.py
+++ b/ranger/ext/img_display.py
@@ -434,20 +434,20 @@ class URXVTImageDisplayer(ImageDisplayer, FileManagerAware):
         pct_width, pct_height = self._get_sizes()
 
         sys.stdout.write(
-            self.display_protocol +
-            path +
-            ";{pct_width}x{pct_height}+{pct_x}+{pct_y}:op=keep-aspect".format(
+            self.display_protocol
+            + path
+            + ";{pct_width}x{pct_height}+{pct_x}+{pct_y}:op=keep-aspect".format(
                 pct_width=pct_width, pct_height=pct_height, pct_x=pct_x, pct_y=pct_y
-            ) +
-            self.close_protocol
+            )
+            + self.close_protocol
         )
         sys.stdout.flush()
 
     def clear(self, start_x, start_y, width, height):
         sys.stdout.write(
-            self.display_protocol +
-            ";100x100+1000+1000" +
-            self.close_protocol
+            self.display_protocol
+            + ";100x100+1000+1000"
+            + self.close_protocol
         )
         sys.stdout.flush()
 
diff --git a/ranger/ext/rifle.py b/ranger/ext/rifle.py
index 70215039..672b0597 100755
--- a/ranger/ext/rifle.py
+++ b/ranger/ext/rifle.py
@@ -261,6 +261,14 @@ class Rifle(object):  # pylint: disable=too-many-instance-attributes
             process = Popen(["file", "--mime-type", "-Lb", fname], stdout=PIPE, stderr=PIPE)
             mimetype, _ = process.communicate()
             self._mimetype = mimetype.decode(ENCODING).strip()
+            if self._mimetype == 'application/octet-stream':
+                try:
+                    process = Popen(["mimetype", "--output-format", "%m", fname],
+                                    stdout=PIPE, stderr=PIPE)
+                    mimetype, _ = process.communicate()
+                    self._mimetype = mimetype.decode(ENCODING).strip()
+                except OSError:
+                    pass
         return self._mimetype
 
     def _build_command(self, files, action, flags):
diff --git a/ranger/gui/context.py b/ranger/gui/context.py
index d8d1957c..96849686 100644
--- a/ranger/gui/context.py
+++ b/ranger/gui/context.py
@@ -23,7 +23,7 @@ CONTEXT_KEYS = [
     'keybuffer',
     'infostring',
     'vcsfile', 'vcsremote', 'vcsinfo', 'vcscommit', 'vcsdate',
-    'vcsconflict', 'vcschanged', 'vcsunknown', 'vcsignored',
+    'vcsconflict', 'vcschanged', 'vcsunknown', 'vcsignored', 'vcsuntracked',
     'vcsstaged', 'vcssync', 'vcsnone', 'vcsbehind', 'vcsahead', 'vcsdiverged'
 ]
 
diff --git a/ranger/gui/ui.py b/ranger/gui/ui.py
index 990db0ad..4f76dfab 100644
--- a/ranger/gui/ui.py
+++ b/ranger/gui/ui.py
@@ -113,7 +113,7 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
             self._draw_title = curses.tigetflag('hs')  # has_status_line
 
             # Save tmux setting `automatic-rename`
-            if self.settings.update_tmux_title:
+            if self.settings.update_tmux_title and 'TMUX' in os.environ:
                 try:
                     self._tmux_automatic_rename = check_output(
                         ['tmux', 'show-window-options', '-v', 'automatic-rename']).strip()
@@ -123,7 +123,7 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
         self.update_size()
         self.is_on = True
 
-        if self.settings.update_tmux_title:
+        if self.settings.update_tmux_title and 'TMUX' in os.environ:
             sys.stdout.write("\033kranger\033\\")
             sys.stdout.flush()
 
@@ -172,7 +172,7 @@ class UI(  # pylint: disable=too-many-instance-attributes,too-many-public-method
         DisplayableContainer.destroy(self)
 
         # Restore tmux setting `automatic-rename`
-        if self.settings.update_tmux_title:
+        if self.settings.update_tmux_title and 'TMUX' in os.environ:
             if self._tmux_automatic_rename:
                 try:
                     check_output(['tmux', 'set-window-option',
diff --git a/ranger/gui/widgets/__init__.py b/ranger/gui/widgets/__init__.py
index 36292103..c8f1262b 100644
--- a/ranger/gui/widgets/__init__.py
+++ b/ranger/gui/widgets/__init__.py
@@ -12,11 +12,11 @@ class Widget(Displayable):
         'conflict': (
             'X', ['vcsconflict']),
         'untracked': (
-            '+', ['vcschanged']),
+            '?', ['vcsuntracked']),
         'deleted': (
             '-', ['vcschanged']),
         'changed': (
-            '*', ['vcschanged']),
+            '+', ['vcschanged']),
         'staged': (
             '*', ['vcsstaged']),
         'ignored': (
@@ -26,7 +26,7 @@ class Widget(Displayable):
         'none': (
             ' ', []),
         'unknown': (
-            '?', ['vcsunknown']),
+            '!', ['vcsunknown']),
     }
 
     vcsremotestatus_symb = {
@@ -41,7 +41,7 @@ class Widget(Displayable):
         'none': (
             '⌂', ['vcsnone']),
         'unknown': (
-            '?', ['vcsunknown']),
+            '!', ['vcsunknown']),
     }
 
     ellipsis = {False: '~', True: '…'}
diff --git a/ranger/gui/widgets/browsercolumn.py b/ranger/gui/widgets/browsercolumn.py
index b3272cbc..cf322409 100644
--- a/ranger/gui/widgets/browsercolumn.py
+++ b/ranger/gui/widgets/browsercolumn.py
@@ -318,8 +318,8 @@ class BrowserColumn(Pager):  # pylint: disable=too-many-instance-attributes
 
             text = current_linemode.filetitle(drawn, metadata)
 
-            if drawn.marked and (self.main_column or
-                                 self.settings.display_tags_in_all_columns):
+            if drawn.marked and (self.main_column
+                                 or self.settings.display_tags_in_all_columns):
                 text = " " + text
 
             # Computing predisplay data. predisplay contains a list of lists
diff --git a/ranger/gui/widgets/statusbar.py b/ranger/gui/widgets/statusbar.py
index 266d48ca..3457955e 100644
--- a/ranger/gui/widgets/statusbar.py
+++ b/ranger/gui/widgets/statusbar.py
@@ -275,13 +275,14 @@ class StatusBar(Widget):  # pylint: disable=too-many-instance-attributes
             right.add("/" + str(len(target.marked_items)))
         else:
             right.add(human_readable(target.disk_usage, separator='') + " sum")
-            try:
-                free = get_free_space(target.mount_path)
-            except OSError:
-                pass
-            else:
-                right.add(", ", "space")
-                right.add(human_readable(free, separator='') + " free")
+            if self.settings.display_free_space_in_status_bar:
+                try:
+                    free = get_free_space(target.mount_path)
+                except OSError:
+                    pass
+                else:
+                    right.add(", ", "space")
+                    right.add(human_readable(free, separator='') + " free")
         right.add("  ", "space")
 
         if target.marked_items:
diff --git a/ranger/gui/widgets/view_base.py b/ranger/gui/widgets/view_base.py
index 80061004..4493443e 100644
--- a/ranger/gui/widgets/view_base.py
+++ b/ranger/gui/widgets/view_base.py
@@ -72,8 +72,8 @@ class ViewBase(Widget, DisplayableContainer):  # pylint: disable=too-many-instan
         sorted_bookmarks = sorted(
             (
                 item for item in self.fm.bookmarks
-                if self.fm.settings.show_hidden_bookmarks or
-                '/.' not in item[1].path
+                if self.fm.settings.show_hidden_bookmarks
+                or '/.' not in item[1].path
             ),
             key=lambda t: t[0].lower(),
         )
diff --git a/tests/ranger/core/__init__.py b/tests/ranger/core/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/tests/ranger/core/__init__.py
diff --git a/tests/ranger/core/test_main.py b/tests/ranger/core/test_main.py
new file mode 100644
index 00000000..d992b8a7
--- /dev/null
+++ b/tests/ranger/core/test_main.py
@@ -0,0 +1,18 @@
+import collections
+import os
+
+from ranger.core import main
+
+
+def test_get_paths():
+    args_tuple = collections.namedtuple('args', 'paths')
+    args = args_tuple(paths=None)
+
+    paths = main.get_paths(args)
+
+    for path in paths:
+        assert os.path.exists(path)
+
+
+if __name__ == '__main__':
+    test_get_paths()
>
aaf61a53 ^
a90faae9 ^



a90faae9 ^

455fbac6 ^
a90faae9 ^
f7e75825 ^
a90faae9 ^














136412d2 ^
a90faae9 ^



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635