#!/usr/bin/perl

$default_vshnucap = '';
$default_vshnurc  = '';

# .vshnucfg - required general vshnu configuration file
# Steve Kinzler, steve@kinzler.com, Aug 99/Mar 00/Sep 00
# see website https://kinzler.com/me/vshnu/
# https://kinzler.com/me/home.html#unixcfg

# Names used only within and below this vshnucfg are placed in the cfg::
# package.  This file should be reloadable.

###############################################################################
## Change Log #################################################################

($cfg::VNAME, $cfg::VERSION, $cfg::REQUIRE) = qw(vshnucfg 1.0509 1.0502);
&addversions($cfg::VNAME, $cfg::VERSION);

&quit("$0: $cfg::VNAME $cfg::VERSION requires at least $VNAME $cfg::REQUIRE ",
      "($VERSION)\n") if $cfg::REQUIRE > $VERSION;

# 1.0000   7 Nov 2000	Initial public release
# 1.0001  15 Nov 2000	$cfg::pagerr defaults to `less -r` if PAGER=less
# 1.0002   5 Dec 2000	Add 'sleep 1' to /D command; *bz2 support
# 1.0003  13 Dec 2000	Version format x.y.z -> x.0y0z
# 1.0004  25 Jan 2001	Add ReadLine package to ^V command output
# 1.0007  25 May 2001	Recognize .\d\w extensions as man pages
# 1.0008   6 Jun 2001	Add `rpm -Fhv` freshen option for rpm files
# 1.0009  15 Jun 2001	Add loading of .vshnu* files via typemap
# 1.0010   2 Jul 2001	Define some function keys for navigation
# 1.0011  19 Oct 2001	Add 5 command for `md5sum` long listing
# 1.0012   1 Feb 2002	Add viewing actions for certificate-related files
# 1.0100  29 Mar 2002	Move Y command to U, add Y as tree list cwd
# 1.0101   1 Apr 2002	Enhance directory actions, use ifopt
# 1.0103  16 Apr 2002	Add ^_ command for variable listing
# 1.0104  13 Mar 2003	Add list and extract actions for tnef files
# 1.0105  31 May 2003	Add ReadLine package version to ^V command output
# 1.0106  11 Jun 2003	Full versions support including ^V command
# 1.0107   2 Jul 2003	Add binding for Gnu ReadLine insert-vshnu-chosen
# 1.0108   9 Jul 2003	Add /`p for choosing all PATH elements
# 1.0109  11 Jul 2003	Add /`d for disks; Add `xpdf` fallback for .pdf
# 1.0110   2 Dec 2003	Add decryption actions for PGP and GPG files
# 1.0111   1 Jun 2004	Switch to better multimedia and office apps
# 1.0112   2 Jun 2004	Add suspend choice to ^Q table and alias ^Z
# 1.0113   3 Jun 2004	Add /`v for choosing all disk devices
# 1.0114   4 Jun 2004	Add `stat` for special file action and /A command
# 1.0115   4 Jun 2004	Add + command for longtrunc toggle
# 1.0116   7 Jun 2004	Add ^G cmd for cwd df reporting; Use &pwd in ^A cmd
# 1.0117   8 Jun 2004	Recognize .csv and .tsv extensions for office files
# 1.0118  22 Jun 2004	Switch image viewer from `xv` to `display`, add .ico
# 1.0119  29 Jun 2004	Add $insertkey and $insertcmd
# 1.0120   6 Aug 2004	Add Q command for long listing by arbitrary shell cmd
# 1.0121   2 Nov 2004	Add `konqueror` as a browser for tar and zip files
# 1.0122  15 Nov 2004	Add info, list and dump actions for iso files
# 1.0123  19 Nov 2004	Recognize .wmv extension for gmplayer files
# 1.0124  19 Nov 2004	Recognize .st? and .sx? extensions for office files
# 1.0125  23 Nov 2004	Add X cut buffer option to $insertcmd
# 1.0126  26 Nov 2004	Move X cmd to I; Add X command to copy to X cut buffer
# 1.0127  27 Nov 2004	Move Y tree command into dir action menu; Move U to B
# 1.0128  27 Nov 2004	Add Y/U commands to expand/collapse display files
# 1.0129  27 Nov 2004	Add ^Y command for file count reporting
# 1.0130  28 Nov 2004	Add sort by path depth option
# 1.0131  12 Dec 2004	Drop .tsv extension for office files
# 1.0132  31 Jan 2005	Use &expand, &collapse and &expandtoggle aliases
# 1.0133  13 Feb 2005	Add &cfg::abssets to maintain filesets across cd's
# 1.0134  20 Feb 2005	Revise X command for chosen files options vs lsall
# 1.0135  18 Mar 2005	Add user@host:file choice to X command menu
# 1.0136  14 Apr 2005	Use new rules for default pagers, see comments below
# 1.0137  14 Apr 2005	Add 6 command for disks file set df display
# 1.0138  15 Apr 2005	Add $full threshold for df coloring, skel &cfgcolorlong
# 1.0139  17 Apr 2005	Add long coloring for md5sum and rpm
# 1.0140  18 Apr 2005	Add text view and info action options for .pdf files
# 1.0141  20 Apr 2005	Document file variables ($_, $_q, $_r, etc)
# 1.0142  22 Apr 2005	Move 6 cmd to 8; Add 6 cmd for mimetype long listing
# 1.0143  24 Apr 2005	Add V and v options for audio and visual beeps
# 1.0200  29 Apr 2005	Use &mailcap2typemap for vshnucap and $MAILCAPS, &xsh
# 1.0201   3 May 2005	Add ^\ command, $initmouse, $forcemouse and &onrestart
# 1.0202   4 May 2005	Add mous command to call &domouse; Set $initmouse on
# 1.0203   6 May 2005	Use $hostr vs $host in X command
# 1.0204   9 May 2005	Add P option to sort by file basename
# 1.0205   9 May 2005	Update . command description; Fix bug in $onsub{cd}
# 1.0206  10 May 2005	Add username and groupname rl completion to chown/chgrp
# 1.0207  11 May 2005	Use &completetypepath for . command completions
# 1.0208  11 May 2005	Add /`P for choosing all CD_PATH elements
# 1.0209  18 May 2005	Add ordering arrays of typemap and keymap keys
# 1.0210   1 Jul 2005	Fix redundant &win in 8 command
# 1.0211   5 Jul 2005	Use &quit vs die
# 1.0212   4 Dec 2005	Add &opton
# 1.0213  28 Dec 2005	Remove typemap ordering kludges
# 1.0214  28 Jan 2006	Use &ext and &Ext
# 1.0215   6 Apr 2006	Rename $cfg::pager* to $pager*; Delete &help pager arg
# 1.0216   8 Apr 2006	Document and use &run and special ext syntax
# 1.0217  23 Apr 2006	Add base %mousemap_ and ^ command
# 1.0218  14 May 2006	Add ^J and ^^ commands
# 1.0219   8 Feb 2007	Revise _ command for incremental, wrapping shifting
# 1.0220  14 Feb 2007	Add MAILER support
# 1.0221  19 Mar 2007	Add and use &cfg::maketargets with make commands
# 1.0222  11 Apr 2007	Use keymap = flag; Fix ^X pop bug
# 1.0223  15 Apr 2007	Use new screen zones in mousemaps; Fix mouse support
# 1.0224  17 May 2007	Use &atabsfile; Add user@host:chosen to X command menu
# 1.0225  13 Jun 2007	Add $cfg::xcb, &cfg::xcut and &cfg::xpaste
# 1.0300   8 Jul 2007	Version normalization
# 1.0301  15 Jul 2007	Use enhanced &mapadd $before argument syntax
# 1.0302  16 Mar 2008	Add $co_title and map titles; Add *, H and k options
# 1.0303   3 Jun 2008	Add `chmod +[rwx]/go-rwx` choices to C choose command
# 1.0304   4 Sep 2008	Recognize .asf extension for gmplayer files
# 1.0305  12 Feb 2009	Add &cfg::pipetoshell and S command; Add ^O choose cmd
# 1.0306  11 May 2009	Make tests distinct between vshnucap and mailcap
# 1.0307  15 May 2009	Add dotype option to cfg::pointormark
# 1.0308  28 May 2009	Add L flag to &run and use in C choose commands
# 1.0309   3 Aug 2009	Recognize .km[lz] extensions for google-earth files
# 1.0310  20 Aug 2009	Add run option for jar files
# 1.0311  18 Sep 2009	Recognize ODF extensions for office files
# 1.0312  12 Nov 2009	Recognize .m4a extension for gmplayer files
# 1.0313  25 Dec 2009	Add info, contents and install actions for deb files
# 1.0314   4 Jan 2010	Add list action for rar files
# 1.0315  23 Mar 2010	Use `evince` as PS/PDF viewer vs `ghostview`/`xpdf`
# 1.0316   8 Jan 2011	Add and use &cfg::onecmd
# 1.0317   3 Jun 2011	Add Z command for security context long listing
# 1.0318  12 Jun 2011	Use gnome-mplayer instead of gmplayer
# 1.0319  22 Jul 2011	Use `libreoffice` instead of `ooffice`
# 1.0320  25 Jul 2011	Switch image viewer from `display` to `eog`
# 1.0321   1 Aug 2011	Add `alien -i` install deb option for rpm files
# 1.0322  21 Sep 2011	Add &cfg::mapchoose
# 1.0323  30 Mar 2012	Add B choose command for `konqueror` item browser
# 1.0324   5 Apr 2012	Modify ^O choose command to not use "--"
# 1.0325   8 Apr 2012	Recognize .apk extension for app package files
# 1.0326  23 Jul 2012	Recognize .3gp extension for gmplayer files
# 1.0327  22 Aug 2012	Add ` command for `cal -3`
# 1.0328  27 Nov 2012	Recognize .vsd extension for office files
# 1.0329   7 Jan 2013	Recognize .vob extension for gmplayer files
# 1.0330   1 Feb 2013	Add list action for 7z files
# 1.0331   5 Apr 2014	Initialize empty %nobag
# 1.0332  10 Oct 2014	Use google-earth instead of googleearth
# 1.0333  19 Feb 2015	Move install deb option for rpm files to vshnurc
# 1.0334  14 Sep 2015	Add extract option for deb files
# 1.0335   7 Dec 2015	Recognize .ear and .war extensions as same as .jar
# 1.0400  12 Feb 2016	Version normalization
# 1.0401  12 Feb 2016	Uppercase $cfg::vname, version and require variables
# 1.0402  21 Jul 2016	Recognize .jks and .*store for Java KeyStore files
# 1.0404   7 Sep 2016	Add 2 flag to &run and use in various commands
# 1.0405  28 Sep 2016	Combine 6 command (mimetype) into F command (file)
# 1.0406  28 Sep 2016	Remove Z command for security context
# 1.0407  27 Jan 2017	Add x option to suppress file coloring as executable
# 1.0408  29 Nov 2017	Use smplayer instead of gnome-mplayer
# 1.0409  26 Dec 2017	Use vlc instead of smplayer
# 1.0410  28 Nov 2018	Add cfg::lsexpand for Y command, excludes .git
# 1.0411  18 Aug 2019	Recognize .webm extension for vlc files
# 1.0500  25 Aug 2019	Version normalization
# 1.0501   4 Jul 2020	Use google-earth-pro instead of google-earth
# 1.0502  17 Nov 2023	Add $pagerc and use with tree commands
# 1.0503  25 Feb 2024	Recognize .aac extension for vlc files
# 1.0504  19 Sep 2024	Recognize .jnlp extension for javaws files
# 1.0505  26 Sep 2024	Recognize more .og* extensions for vlc files
# 1.0506  12 Apr 2025	Add png convert action option for .pdf files
# 1.0507  12 May 2025	Recognize .m4b extension for vlc files
# 1.0508   2 Sep 2025	Recognize .heic extension for eog files
# 1.0509  11 Jan 2026	Add play action for iso files using vlc

###############################################################################
## External configuration #####################################################

$cfg::editor = $ENV{VISUAL} || $ENV{EDITOR} || 'vi';
$cfg::mailer = $ENV{MAILER} || 'mail';

# $pager	normal pager
#		default $PAGER  or less or more
# $pagera	pager that can display text as available
#		default $PAGERA or less or more
# $pagerr	raw pager for colored text
#		default $PAGERR or `less -R` or `less -r` or more
# $pagerc	raw pager for all text, ie all control characters
#		default $PAGERC or `less -r`

$pager   = do { `less -V > /dev/null 2>&1`; ($?) ? 'more' : 'less' };
$pager  .= "; echo Press Return | tr -d '\\012'; sh -c 'read x' < /dev/tty"
	if $pager  eq 'more';
$pagera  = $ENV{PAGERA} || $pager;
$pagerr  = $ENV{PAGERR} || $pager;
$pagerr .= do { `less -R /dev/null > /dev/null 2>&1`; ($?) ? ' -r' : ' -R' }
	if $pagerr =~ /\bless\b/ && $pagerr !~ /\s-\S*r/i;
$pagerc  = $ENV{PAGERC} || 'less -r';
$pager   = $ENV{PAGER}  || $pager;
$pager  .= "; echo Press Return | tr -d '\\012'; sh -c 'read x' < /dev/tty"
	if $pager  =~ /\bmore\b/ && $pager  !~ /(\s-\S*w|Press)/;

# Tip: When viewing a vshnu help listing with `less`, you can save the
# listing into a FILE with this `less` command:		g|$cat > FILE<Ret>
# Set the vshnu H option beforehand for an uncolored version.

$mailbox = $ENV{MAIL};
@bak	 = qw/~$/;	# regexps identifying backup files

$cfg::xcb = 0;
sub cfg::xcut	{ &pipeto("xcb -s $cfg::xcb", @_) }
sub cfg::xpaste { `xcb -p $cfg::xcb` }

###############################################################################
## Color configuration ########################################################

# This color configuration should work reasonably well on either a dark
# or light background.

# Filename coloring is specified via the LS_COLORS environment variable.
# See GNU's ls(1).

$co_decor = ($>)     ? 'reverse'     : ($color) ? 'on_red' : '';
$co_error = ($color) ? 'bold red'    : 'bold';
$co_msg   = ($color) ? 'green'	     : '';

# File permissions
$co_ftype = ($color) ? 'yellow'	     : '';		# file type character
$co_perms = ($color) ? 'white'	     : '';		# triplets
$co_myper = ($color) ? ''	     : 'bold';		# triplet for me
$co_write = ($color) ? 'red'	     : 'reverse';	# write bits above mine
$co_sbits = ($color) ? 'cyan'	     : '';		# set-id & sticky bits

# Long listings
$co_nlink = '';						# number of links
$co_size1 = '';						# 1000^even size digits
$co_size2 = ($color) ? 'white'	     : 'bold';		# 1000^odd  size digits
$co_xaged = ($color) ? ''	     : 'bold';		# non-aged files
$co_aged  = ($color) ? 'white'	     : '';		# aged files
$co_symln = ($color) ? 'yellow'	     : '';		# symlink paths
%co_user  = ($color) ? ($user => 'blue', 0 => 'red', 'other' => 'green')
		     : ();		# '' is a default key (name or uid)

# Help/Prompt listings
$co_title = 'reverse';					# title
$co_key   = ($color) ? ''	     : '';		# key
$co_ckey  = ($color) ? 'green'	     : 'bold';		# ^key
$co_nkey  = ($color) ? 'red'	     : 'reverse';	# \key
$co_wkey  = ($color) ? 'red'	     : 'reverse';	# <key>
$co_0key  = ($color) ? 'cyan'	     : 'bold';		# key [0-9]
$co_Akey  = ($color) ? 'green'	     : 'bold';		# key [A-Z]
$co_akey  = ($color) ? 'white'	     : 'bold';		# key [a-z]
$co_code  = ($color) ? 'yellow'	     : '';		# key code
$co_cmd   = ($color) ? ''	     : 'bold';		# main commands
$co_tail  = ($color) ? 'white'	     : '';		# tail commands
$co_com   = ($color) ? 'yellow'	     : 'reverse';	# command comment
$co_desc  = ($color) ? 'bold cyan'   : '';		# description
$co_prmt  = ($color) ? 'bold green'  : '';		# prompt
$co_xuse  = ($color) ? 'on_green'    : 'reverse';	# unused
@tail	  = qw/ret keymap home win winch\s*\d*/;
					# regexps identifying tail commands

###############################################################################
## General configuration ######################################################

$aged	     = '12h';			# time old to be considered aged
$full	     = '90';			# % disk full warning threshold
$minfilelen  = 15;			# including any tag, recommend <=18
$maxfilecols = 0;			# 0 => as many as possible
$maxcdhist   = '$scr->{ROWS} - 4';	# eval'ed, <0 => no limit
$maxdohist   = '$scr->{ROWS} - 4';	# eval'ed, <0 => no limit
$typemaptab  = 32;			# width of left column of typemap help
$mousemaptab = 16;			# width of left column of mousemap help
$initmouse   = 'on';			# initial xterm mouse mode
$forcemouse  = 0;			# allow mouse mode even if no Km||xterm

# All mapped commands may be a command string, 'cmd', a command string
# with text description, ['cmd', 'desc'], or a multiple-choice table
# of command specifications, [['cmd', 'desc', 'keys', 'prompt'], ...].
# They may also be an unnamed subroutine that returns one of these data
# structures.  If 'desc' is null, then 'cmd' is used.  If 'prompt' is
# null, then 'desc' or 'cmd' is used.  If 'keys' are undefined, then "a"
# through "z" are used.  If 'keys' is empty, this indicates the default.
# 'cmd's are perl code to be eval()ed.  'desc's and 'prompt's are strings
# to be eval()ed as double-quoted.

%onsub = (
	'cd'		=> 'altls; $#cdhist && cfg::abssets $cdhist[1]{ls}',
	'altls'		=> 'longls',
	'winch'		=> 'longls',
);

# A value of ('a') here would implement a simple single file cursor
# instead of a whole bag, and free up 'b' .. 'z' for other uses.
@bagkeys = @cfg::savebagkeys = ('a' .. 'z');
				# <KEY>  is the selected bagkey character
%bagmap  = (			# <FILE> is each bagkey's quoted file
	''		=> ['point <KEY>; dotype <FILE>',
			    'act on <FILE> by its type'],
	'choose'	=> ['point <KEY>; choose <FILE>', 'choose <FILE>'],
);
%nobag	 = ();

$insertkey = 'M-v';
$insertcmd = [
	['@choose',	  'insert chosen files', 'cC/',	'chosen files'],
	['$point',	    'insert point file', 'p>',	'point file'],
	['$_ = $point; s/\.[^\.\/]*$//; $_',
		   'insert point file sans ext', 'P<',	'point file sans ext'],
	['$cwd',     'insert current directory', '.',	'current directory'],
	['pwd',		'insert hard directory', 'hH',	'hard directory'],
	['$cdhist[1]{ls}',
		       'insert prior directory', "\\",	'prior directory'],
	['df && $df->mount(pwd)',
			   'insert mount point', 'mM',	'mount point'],
	['@cfg::set1',	    'insert file set 1', '1',	'file set 1'],
	['@cfg::set2',	    'insert file set 2', '2',	'file set 2'],
	['@cfg::set3',	    'insert file set 3', '3',	'file set 3'],
	['@cfg::set4',	    'insert file set 4', '4',	'file set 4'],
	['map { $_->{ls} } @cdhist',
		     'insert directory history', 'dD',	'directory history'],
	['@dohist',	  'insert file history', 'fF',	'file history'],
	['getmark getkey("Mark:"), "dir"',
		      'insert marked directory', '(',	'marked directory'],
	['getmark getkey("Mark:"), "file"',
			   'insert marked file', ')',	'marked file'],
	['getmark getkey("Mark:"), "path"',
			   'insert marked path', '9',	'marked path'],
	['split(/\n/, cfg::xpaste)',
		'insert X cut buffer $cfg::xcb', 'xX',	'X cut buffer'],
];

###############################################################################
## Typemap configuration ######################################################

# Using &sh, a multiple argument command and a single argument command
# without shell metacharacters are run directly, while a single argument
# command with shell metacharacters is run with `/bin/sh -c`.  Using &shell,
# all arguments are concatenated to form a command that is run with
# `$shell -c`, where $shell defaults to the SHELL environment variable
# or /bin/sh.  Also using &shell, the command may include embedded perl
# code surrounded by double braces (eg "echo {{reverse @ls}}"), the value
# of which is substituted into the shell command.  &xsh and &xshell are
# versions of these for X Windows commands.

# The following variables are available based on the file being acted upon:
#	$_	orig name, eg foo/bar.baz
#	$_r	root name, eg foo/bar
#	$_e	extension, eg baz
#	$_h	head name, eg foo
#	$_t	tail name, eg bar.baz
#	$_f	full name, eg /foo/bar.baz
#	$_m	MIME type, eg text/plain (requires `file -i` or MIME::Types)
#	$_d	description from `file -L` output
#	$_*q	as above but quoted
#	_	preset file test argument

# &winch = a screen redraw (&win) after a check if the window changed size

# &run('-opts', ...) is a shorthand function for common forms of external
# commands.  A special syntax adjustment is made to make "run -opts " at
# the beginning of a command parse as "run '-opts', ".  These characters in
# "opts" have the following meaning:
#	_	append "$_"     to the command
#	=	append "-- $_"  to the command
#	f	append "$_f"    to the command
#	F	append "-- $_f" to the command
#	+	append @choose  to the command
#	#	append "--" and @choose to the command
#	2	append "2>&1"      to the command
#	p	append "| $pager"  to the command
#	P	append "| $pagera" to the command
#	g	append "| $pagerr" to the command
#	G	append "| $pagerc" to the command
#	s	run the command with &sh
#	S	run the command with &shell (default)
#	x	run the command with &xsh
#	X	run the command with &xshell
#	b	background the command job with a beep at its finish
#	/	follow the command with &setcomplete
#	r	follow the command with &ret prompt
#	R	follow the command with &ret('Remove?') && &remove($_)
#	C	follow the command with &ret('Remove?') && &remove(@choose)
#	u	unchoose @choose after the command
#	k	pop the keymap mode after the command
#	L	follow the command with &longls('-win', 1)
#	w	follow the command with &win   (default with x and X)
#	W	follow the command with &winch (default with s and S)
#	n	follow the command with neither &win nor &winch

# Typemaps are tested in the order present in @typemap_*; if this is
# empty, then all in sort order.  The default ('') is always tested last.
# Tests beginning with "ext " and "Ext " are specially interpreted so that
# the remainder of the test is parsed as whitespace-separated arguments.

%typemap_ = @typemap_ = (		## ##
	'TTL_'		=> 'EDIT MODE FILE ACTIONS',
	'/^$/'		=> 'win',
	'-e _ && -d _'	=> ['cd $_; win', 'enter this directory'],
	''		=> ['run -s_ $cfg::editor', 'edit this file'],
);
@typemap_ = &akeys(@typemap_);

%typemap_expand = @typemap_expand = (	## " ##
	'TTL"'		=> 'EXPAND MODE FILE ACTIONS',
	'/^$/'		=> 'win',
	'! -e _'	=> 'beep; win',
	'-d _'		=> ['expandtoggle $_; win',
			    'expand or collapse this directory'],
	''		=> 'beep',
);
@typemap_expand = &akeys(@typemap_expand);

sub cfg::zarbrowse {
	['run -X "konqueror -- ' . $_[0] . ':$_fq"',
	 "browse the contents of this $_[1]", 'bBkK', 'browse'] }
sub cfg::tarbrowse { &cfg::zarbrowse('tar', @_) }
sub cfg::zipbrowse { &cfg::zarbrowse('zip', @_) }

%typemap_do = @typemap_do = (		## ? ##
'TTL?'		=> 'DO MODE FILE ACTIONS',
'/^$/'		=> 'win',
'! -e _'	=> 'beep; win',
'-d _'		=> [['run -=g "ls -lR", unlessopt("a"), ifopt("L"),'
		     . ' ifopt("T", "-F")', 'long list'
		     . ' this directory recursively', 'lL',	'ls -lR'],
		    ['run -_G "tree -A", unlessopt("a"), ifopt("C"),'
		     . ' ifopt("L", "-l"), ifopt("T", "-F")',
		     'tree list this directory',      'tT',	'tree'],
		    ['run -_G "tree -Apug", unlessopt("a"), ifopt("C"),'
		     . ' ifopt("L", "-l"), ifopt("T", "-F")',
		     'long tree list this directory', 'pPuUgG',	'tree -pug']],
    &mailcap2typemap($ENV{VSHNUCAP} || $default_vshnucap ||
	((-f "$ENV{HOME}/.vshnucap") ? "$ENV{HOME}/.vshnucap" : 'vshnucap'),
	"'v'; "),
'/(\.tnef|(^|\/)winmail\.dat)$/i'
		=> [['run -s=2p "tnef -tv"',
		     'list the contents of this tnef archive',
		     'tTlL', 'list contents'],
		    ['run -s=r "tnef", "-wv"', 'extract this tnef archive',
		     'xX',   'extract']],
'/(^|\/|\.)mbox$/'
		=> ['run -s_ "mail", "-f"', 'run `mail` on this mbox file'],
'ext 7z'	=> ['run -s=2p "7zr l"',
		    'list the contents of this 7zip archive'],
'Ext \d\w? man'	=> ['run -s_g "nroff -man <"', 'view this man page formatted'],
'Ext aif[cf]? au snd'
		=> ['run -x_ "xplay -stay"', 'play this AU/AIFF audio file'],
'ext aac asf avi flv m[4k][av] m4b mo?v(ie)? mp[34e] mpe?g og[agmvx] qt vob'
 . ' wav webm wm[av] 3gp'
		=> ['run -x= "xterm -e vlc"', 'play this audio/video file'],
'ext bmp gif heic ico jpe?g p[bgnp]m pcx png svg tiff? xbm xpm'
		=> ['run -x= "eog"', 'display this image file'],
'ext csv docx? dot o[dt]t ods pp[ts]x? rtf sdw st[cdiw] sx[cdgimw] vor vsd' .
' xls[xm]?'	=> ['run -x_ "libreoffice"','load this file into LibreOffice'],
'Ext deb'	=> [['run -s=2p "dpkg -I"',
		     'view the info of this Debian package',
						     '?', 'info'],
		    ['run -s=2p "dpkg -c"',
		     'view the contents of this Debian package',
						'cClLtT', 'contents'],
		    ['run -s=r "dpkg", "-i"',
		     'install this Debian package', 'iI', 'install'],
		    ['run -s=r "dpkg", "-i", "--force-architecture"',
		     'install Debian package despite architecture',
						    'fF', 'force install'],
		    ['run -s2p "dpkg-deb", "-X", $_, "."',
		     'extract Debian package to the current directory',
						  'xXeE', 'extract']],
'Ext dir pag'	=> ['run -s2p "makedbm -u $_rq"',
		    'view a dump of this dbm file'],
'Ext (wr|vrm)l(\.g?[Zz])?'
		=> ['run -x= "freewrl"', 'display this VRML file'],
'Ext a'		=> ['run -s_2p "ar tv"', 'list the contents of this archive'],
'Ext apk'	=> [['run -s_2P "unzip -l"',
		     'list the contents of this app package', 'tTlL', 'list'],
		    &cfg::zipbrowse('app package')],
'Ext aup'	=> ['run -x_ "audacity"', 'load this Audacity project'],
'Ext crl'	=> ['run -s_2p "openssl crl -noout -text -in"',
		    'view this certificate revocation list'],
'Ext cer crt pem'
		=> ['run -s_2p "openssl x509 -noout -text -purpose -in"',
		    'view this certificate'],
'Ext csr'	=> ['run -s_2p "openssl req -noout -text -in"',
		    'view this certificate signing request'],
'Ext dvi'	=> ['run -x_ "xdvi"',	'display this DVI file'],
'ext e?ps'	=> ['run -x= "evince"',	'display this PostScript file'],
'Ext fig'	=> ['run -x_ "xfig"',	'load this figure file into Xfig'],
'Ext gpg'	=> ['run -s=r "gpg"',	'decrypt this GPG file'],
'ext iso'	=> [['run -x= "xterm -e vlc"',
		     'play this ISO image',		   'pP', 'play'],
		    ['run -s_2P "isoinfo -d -i"',
		     'list the info about this ISO image', 'iI', 'info'],
		    ['run -s_2P "isoinfo -l -i"',
		     'list the files in this ISO image',   'lL', 'list'],
		    ['run -s= "isodump"',
		     'view the dump of this ISO image',  'dDvV', 'dump']],
'Ext [ejw]ar'	=> [['run -s_2P "unzip -l"',
		     'list the contents of this Java archive', 'tTlL', 'list'],
		    &cfg::zipbrowse('Java archive'),
		    ['run -_r "java -jar"',
		     'run this Java archive',		       'rR',   'run']],
'Ext(qw(jks (key|trust)store)) || /(^|\/)cacerts/'
		=> ['run -s_2p "echo Default password: changeit;'
		    . ' keytool -list -v -keystore"',
		    'view this java keystore'],
'Ext jnlp'	=> ['run -x_ "xterm -e javaws"',
		    'run this Java Web Start file'],
'Ext key'	=> ['run -s_2p "openssl rsa -noout -text -in"',
		    'view this private key'],
'Ext km[lz]'	=> ['run -xF "google-earth-pro"',
		    'load this file into Google Earth'],
'Ext lyx'	=> ['run -x_ "lyx"', 'load this file into LyX'],
'Ext o'		=> ['run -s=p "nm"', 'view the name list of this object file'],
'ext pdf'	=> sub { my $c =
		    ['run -x= "evince"',
		     'display this PDF file', 'dDrR', 'display'];
		   &cfg::onecmd() ? $c : [$c,
		    ['run -p "pdftotext -layout -- $_q -"',	# use pandoc?
		     'view a text version of this PDF file',
					      'tTvV', 'view as text'],
		    ['run -R "pdftoppm -png $_q $_rq"',
		     'convert this PDF file to PNG image files',
					      'pPcC', 'convert to png'],
		    ['run -=r "pdfinfo"',
		     'view the info of this PDF file', 'iI', 'info']] },
'Ext pgp'	=> ['run -s=r "pgp"', 'decrypt this PGP file'],
'Ext prm'	=> ['run -s_2p "openssl dsaparam -noout -text -in"',
		    'view this DSA parameter file'],
'Ext r(am?|m)'	=> ['run -x_ "realplay"', 'play this Real file'],
'ext rar'	=> ['run -s=2p "unrar vt"',
		    'list the contents of this rar archive'],
'Ext rpm'	=> [['run -s=2p "rpm -qisp"',
		     'view the info of this RPM package','qQ', 'query'],
		    ['run -s=r "rpm", "-ihv"',
		     'install this RPM package',	 'iI', 'install'],
		    ['run -s=r "rpm", "-Fhv"',
		     'freshen this RPM package',	 'fF', 'freshen']],
'ext t(a(r\.?)?)?bz2'
		=> [['run -s2P "bzip2 -d < $_q | tar -tvf -"',
		     'list the contents of this bzip2 tarball',
		     'tTlL', 'list'],
		    &cfg::tarbrowse('bzip2 tarball')],
'ext t(a(r\.?)?)?gz'
		=> [['run -s2P "gzip -d < $_q | tar -tvf -"',
		     'list the contents of this gzip tarball', 'tTlL', 'list'],
		    &cfg::tarbrowse('gzip tarball')],
'ext t(a(r\.?)?)?z'
		=> ['run -s2P "uncompress < $_q | tar -tvf -"',
		    'list the contents of this compress tarball'],
'ext tar'	=> [['run -s_2p "tar -tvf"',
		     'list the contents of this tar archive', 'tTlL', 'list'],
		    &cfg::tarbrowse('tar archive'),
		    ['run -s_2p "tar -xpvf"',
		     'extract this entire tar archive', 'xX', 'extract all']],
'Ext uu'	=> [['run -s=2p "uudecode -p"',
		     'view this uuencoded file',    'vVpP', 'view'],
		    ['run -s=r "uudecode"',
		     'extract this uuencoded file', 'xX',   'extract']],
'Ext vshnu(cfg|rc)'
		=> ['do $_q; err $@; win', 'load this vshnu config file'],
'Ext xcf'	=> ['run -x_ "gimp"', 'load this image file into GIMP'],
'Ext xwd'	=> ['run -x_ "xwud -in"', 'display this window dump'],
'ext zip'	=> [['run -s=2P "unzip -l"',
		     'list the contents of this zip archive', 'tTlL', 'list'],
		    &cfg::zipbrowse('zip archive')],
'/(^|\/)Imakefile$/'
		=> ['run -r getcmd "xmkmf"',
		    'run `xmkmf` which will use this Imakefile'],
'/[Ii]makefile/'=> ['run -r getcmd "imake -f $_q"',
		    'run `imake` using this Imakefile'],
'/[Mm]akefile/' => ['@cfg::makefiles = ($_); setcomplete \&cfg::maketargets; '
		    . 'run "-/r", getcmd "make -f $_q"; undef @cfg::makefiles',
		    'run `make` using this Makefile'],
'Ext Z'		=> ['run -s_2P "uncompress <"', 'view this file uncompressed'],
'Ext bz2'	=> ['run -s_2P "bzip2 -d <"',   'view this file bunzipped'],
'Ext g?z'	=> ['run -s_2P "gzip -d <"',    'view this file gunzipped'],
'! -f _'	=> ['run -s=r "stat"', 'run `stat` on this special file'],
# insert all mailcaps in $MAILCAPS || std path here
    &mailcap2typemap('', '', ['take' => 'ALL'],
	'text/plain', 'text/x-mail', 'message/rfc822', 'message/news'),
'-s _ && -B _'	=> ['run -s_2p "strings <"',
		    'view the strings in this binary file'],
''		=> ['run -s_ $pager', 'view this file'],
);
@typemap_do = &akeys(@typemap_do);

###############################################################################
## Main keymap configuration ##################################################

$cfg::quemarkmsg = 'For help, press % or ^ or &; To quit, press ^Q';

sub cfg::pipetoshell { &pipeto(&getshell('|Shell:'), @_); &ret(); &winch() }

%keymap_ = @keymap_ = (		## ##
'TTL_'	=> 'MAIN MODE KEY COMMANDS',
"\cA"	=> ['cd pwd; win', "cd to the current directory's hard path"],
"\cB"	=> ['@bagkeys ? rebag : rebag(@cfg::savebagkeys)',
	    "toggle the bag's presence"],
"\cC"	=> ['altls \@choose, "Chosen Files"; win',
	    'switch to/from the chosen files display'],
"\cD"	=> ['altls \@cdhist, "Directory History"; win',
	    'switch to/from the directory history display'],
"\cF"	=> ['altls \@dohist, "File History"; win',
	    'switch to/from the file history display'],
"\cG"	=> ['msg diskspace', 'report the disk space for the current disk'],
"\cH"	=> ['bag "["', 'slide the bag backward on the screen'],
"\cI"	=> ['bag "]"', 'slide the bag forward on the screen'],
"\cJ"	=> ['msg $point', 'report the point file'],
"\cL"	=> ['winch', 'redraw the screen, clearing any long listing'],
"\cM"	=> ['cd ".."; win', 'cd to the parent directory'],
"\cN"	=> ['setnorun "once"; win',
	    "don't run but describe the next key command"],
"\cP"	=> ['cdhist "prev"; win',
	    'cd to the previous directory in the history'],
"\cQ"	=> [['do $vshnucfg; err $@; win', 'reload the config file ($vshnucfg)',
				       "cC\cClL\cL",	  'load $vshnucfg'],
	    ['restart', 'restart and reinitialize $VNAME ($0)',
				       "sS\cS",		  'restart'],
	    ['stop; winch', 'suspend to the master shell',
				       "zZ\cZvV\cVyY\cY", 'suspend'],
	    ['last',	'quit $VNAME', "qQ",		  'quit']],
"\cR"	=> ['rechoose; keymap "=choose"',
	    'rechoose the previously chosen set'],
"\cT"	=> ['cfg::setset(@choose) && win',
	    'append the chosen set to the current file set display'],
"\cU"	=> ['clear; win', 'clear the chosen set or current set display'],
"\cV"	=> ['perl "print versions"; ret; winch',
	    'list the $VNAME software versions and packages'],
"\cW"	=> ['cdhist "start"; win',
	    'rewind and cd to the start of the directory history'],
"\cY"	=> ['msg filecount', 'report the numbers of directories and files'],
"\c["	=> ['win "<1"', 'shift the file display left one column'],
"\034"	=> ['mousemode("toggle") || beep; win', 'toggle the mouse mode'], # ^\
"\c]"	=> ['win ">1"', 'shift the file display right one column'],
"\c^"	=> ['msg $cwd', 'report the current directory'],
"\c_"	=> ['pipeto $pager, "#!/bin/perl\n", vardump get "Refs (all):"; winch',
	    'list the given perl variables with their values'],
" "	=> ['win "]#", 1, 1', 'page to the next screen of this display'],
"\""	=> ['typemap "*expand"', 'toggle the expand/collapse file type mode'],
"#"	=> [['$aged = gets "Age ($aged):"; winch',
	     'set the age threshold ($aged) for time coloring',
	     'aA', 'age threshold ($aged)'],
	    ['$depth = gets "Depth ($depth):"; winch',
	     'set the depth limit ($depth) for expanding, <0 means no limit',
	     'dD', 'depth limit ($depth)'],
	    ['$where = gets "Where {$where}:"; winch',
	     'set the where clause {$where} for display subsets',
	     'wW', 'where clause {$where}'],
	    ['$full = gets "Full ($full):"; winch',
	     'set the % disk full warning threshold ($full) for df coloring',
	     'fF', '% disk full threshold ($full)'],
	    ['$cfg::xcb = gets "X Cut Buffer ($cfg::xcb):"; winch',
	     'set the X cut buffer number ($cfg::xcb) for cuts and pastes',
	     'xX', 'X cut buffer ($cfg::xcb)']],
"\$"	=> ['shellv "Shell"; winch',
	    'run a series of shell (or ;perl) commands, `v` to exit'],
"%"	=> ['help "-unused"; winch',
	    'list the commands for the main key mode'],
"&"	=> ['help "=typemap"; winch',
	    'list the commands for the current file type mode'],
"'"	=> ['keymap "=choose"; point',
	    'choose the point file and enter choose mode'],
"("	=> ['setmark(getkey("Set Mark:"), @bagkeys) || beep; home',
	    'mark the current directory position with the given non-bag key'],
")"	=> ['cfg::pointormark getkey "To:"; winch 1',
	    'go to the given mark or point to the given bag key'],
"*"	=> ['winat getfile "Find:"',
	    'page to the file alphabetically >= the given string'],
"+"	=> ['longtrunc "toggle"; win',
	    'truncate the long listing area on the left/right'],
"-"	=> ['point "<1"; point',
	    'act on the file above the point by its type'],
"."	=> ['setcomplete \&completetypepath, "Gnu";'
	    . ' dotypepath getfile "File:"; setcomplete',
	    'act on the given file by its type,' .
	    ' searching in \$CD_PATH and histories'],
"/"	=> ['keymap "choose"', 'enter/exit choose key mode'],
"0"	=> ['point "-\$"; win 1, 1, 1', 'page to the top of the directory'],
"1"	=> ['altls \@cfg::set1, "File Set 1"; win',
	    'switch to/from the first file set display'],
"2"	=> ['altls \@cfg::set2, "File Set 2"; win',
	    'switch to/from the second file set display'],
"3"	=> ['altls \@cfg::set3, "File Set 3"; win',
	    'switch to/from the third file set display'],
"4"	=> ['altls \@cfg::set4, "File Set 4"; win',
	    'switch to/from the fourth file set display'],
"5"	=> ['longls "-win", "md5sum -- \@_ 2>&1"',
	    'long list files with their `md5sum` output'],
"8"	=> ['(@cfg::disks = disks) && longls ";diskspace"'
	    . ' if altls \@cfg::disks, "Disks"; win',
	    'switch to/from the disks file set df display'],
"9"	=> ['helpmarks; winch', 'list the defined marks'],
":"	=> ['shellp getshell "Shell:"; ret; winch',
	    'run a shell (or ;perl) command, leaving output'],
";"	=> ['shellp getshell "Shell;"; winch',
	    'run a shell (or ;perl) command, clearing output'],
"<"	=> ['run -s $cfg::mailer', 'run mailer'],
"="	=> ['point ">1"; point',
	    'act on the file below the point by its type'],
">"	=> ['point', 'act on the point file by its type'],
"?"	=> ['typemap "*do"; msg $cfg::quemarkmsg',
	    'toggle the special action file type mode'],
"B"	=> ['run getcmd "man"', 'run `man` with the given arguments'],
"F"	=> ['cfg::longls2 ["file", ifopt("L"), "--"], [";mimetype"]',
	    'long list files with their `file` output or MIME type'],
"L"	=> ['longls "-win", "+1"',
	    'long list files with their stat info, repeat for more'],
"M"	=> ['setcomplete \&cfg::maketargets; run "-/r", getcmd "make"',
	    'run `make` with the given arguments'],
"O"	=> ['keymap "opts"', 'push to option key mode'],
"P"	=> ['longls "-win", "rpm -qf -- \$_ 2>&1"',
	    'long list files with their owning RPM package'],
"Q"	=> ['longls "-win", gets "Command:"',
	    'long list files with the queried shell command output'],
"S"	=> [['cfg::pipetoshell $cwd',
	     'pipe the current directory to a shell command',
	     '.',   'current directory'],
	    ['cfg::pipetoshell atabsfile $cwd',
	     'pipe the current user\@host:directory to a shell command',
	     'dD',  'user\@host:directory'],
	    ['cfg::pipetoshell absfile $point',
	     'pipe the point file to a shell command',
	     '>pP', 'point file'],
	    ['cfg::pipetoshell atabsfile $point',
	     'pipe the point user\@host:file to a shell command',
	     'fF',  'user\@host:file'],
	    ['cfg::pipetoshell join(" ", map { absfile $_ } @choose)',
	     'pipe the chosen files to a shell command, one-line',
	     '\\',  'chosen files, one-line'],
	    ['cfg::pipetoshell join("\n", map { absfile $_ } @choose)',
	     'pipe the chosen files to a shell command, multi-line',
	     '/',   'chosen files, multi-line'],
	    ['cfg::pipetoshell join("\0", (map { absfile $_ } @choose), "")',
	     'pipe the chosen files to a shell command, null-ended',
	     '0',   'chosen files, null-ended'],
	    ['cfg::pipetoshell join(" ", map { atabsfile $_ } @choose)',
	     'pipe the user\@host:chosen files to a shell command, one-line',
	     'c',   'user\@host:chosen, one-line'],
	    ['cfg::pipetoshell join("\n", map { atabsfile $_ } @choose)',
	     'pipe the user\@host:chosen files to a shell command, multi-line',
	     'C',   'user\@host:chosen, multi-line'],
	    ['cfg::pipetoshell join("\0", (map { atabsfile $_ } @choose), "")',
	     'pipe the user\@host:chosen files to a shell command, null-ended',
	     "\cC", 'user\@host:chosen, null-ended']],
"T"	=> ['run "top"', 'run `top`'],
"U"	=> ['collapse lsall; win', 'collapse all the display files'],
"V"	=> ['stop; winch', 'suspend to the master shell'],
"X"	=> [['cfg::xcut $cwd',		# see also mousemap_ below
	     'copy the current directory to X cut buffer $cfg::xcb',
	     '.',   'current directory'],
	    ['cfg::xcut atabsfile $cwd',
	     'copy the current user\@host:directory to X cut buffer $cfg::xcb',
	     'dD',  'user\@host:directory'],
	    ['cfg::xcut absfile $point',
	     'copy the point file to X cut buffer $cfg::xcb',
	     '>pP', 'point file'],
	    ['cfg::xcut atabsfile $point',
	     'copy the point user\@host:file to X cut buffer $cfg::xcb',
	     'fF',  'user\@host:file'],
	    ['cfg::xcut join(" ", map { absfile $_ } @choose)',
	     'copy the chosen files to X cut buffer $cfg::xcb, one-line',
	     '\\',  'chosen files, one-line'],
	    ['cfg::xcut join("\n", map { absfile $_ } @choose)',
	     'copy the chosen files to X cut buffer $cfg::xcb, multi-line',
	     '/',   'chosen files, multi-line'],
	    ['cfg::xcut join(" ", map { atabsfile $_ } @choose)',
	     'copy the user\@host:chosen files to X cut buffer, one-line',
	     'c',   'user\@host:chosen, one-line'],
	    ['cfg::xcut join("\n", map { atabsfile $_ } @choose)',
	     'copy the user\@host:chosen files to X cut buffer, multi-line',
	     'C',   'user\@host:chosen, multi-line']],
"Y"	=> ['expand cfg::lsexpand; win', 'expand the display files'],
"["	=> ['expand $point; win',	 'expand the point file'],
"\\"	=> ['cdhist "back"; win',	 'cd back to the prior directory'],
"]"	=> ['collapse $point; win',	 'collapse the point file'],
"^"	=> ['help "=mousemap"; winch',
	    'list the commands for the current mouse mode'],
"_"	=> ['longlen "+33%r"; win',
	    'shift the long listing area to the left, wrapping around'],
"`"	=> ['run -sr "cal -3"', 'view the current calendar'],
"{"	=> ['point "<1"', 'slide the point up one file, wrapping around'],
"|"	=> ['columns "<1"', 'decrement the number of file cols,'
			    . ' wrapping around to the max'],
"}"	=> ['point ">1"', 'slide the point down one file, wrapping around'],
"~"	=> ['cd "~"; win', "cd to the user's home directory"],
"kd"	=> ['bag "", "+1"', 'slide the bag down on the screen'],
"kl"	=> ['bag "-1"',	    'slide the bag left on the screen'],
"kr"	=> ['bag "+1"',	    'slide the bag right on the screen'],
"ku"	=> ['bag "", "-1"', 'slide the bag up on the screen'],
"mous"	=> ['domouse', 'handle a mouse event'],
"pgdn"	=> ['point "+1"', 'slide the point down one file'],
"pgup"	=> ['point "-1"', 'slide the point up one file'],
""	=> ['beep; home', 'invalid command key'],
);
@keymap_ = &akeys(@keymap_);

&mapadd('keymap_', "\cZ",  $keymap_{"\cQ"}, ">\cY");	# screen(1) special
&mapadd('keymap_', "\177", $keymap_{"\cH"}, "<kd");
&mapadd('keymap_', "del",  $keymap_{"\cH"}, "<kd");
&mapadd('keymap_', "end",  $keymap_{"\c["}, "<kd");	# protocol esc char?
&mapadd('keymap_', "home", $keymap_{"~"},   "<kd");	# protocol esc char?
&mapadd('keymap_', "ins",  $keymap_{"\cI"}, "<kd");
# numbered function keys might be usable as "k1"-"k12" or "k0"-"k9"

###############################################################################
## "Choose" keymap configuration ##############################################

# When in the chosen files display, "chosen" files are removed from the
# chosen list, not appended to it.

$cfg::unchoose = '; unchoose @choose; keymap; winch';
sub cfg::mapchoose { "map { @_ } \@choose; ret" . $cfg::unchoose }
%keymap_choose = @keymap_choose = (	## / ##
'TTL/'	=> 'CHOOSE MODE KEY COMMANDS',
"\cB"	=> ['choose @bagfiles', 'choose all the bag files'],
"\cE"	=> ['map { dotype } @choose',
	    'act on each chosen file in turn by its type'],
"\cO"	=> ['do { my $s = gets("Shell:"); map { run "-_n", $s } @choose };'
	    . ' ret' . $cfg::unchoose,
	    'run a shell command with each chosen file'],
"\cX"	=> ['unchoose $choose[$#choose]; win',
	    'unchoose the last chosen file'],
"!"	=> ['choose grepls gets "Expr:"; winch',
	    'choose the display files for which the given expression is true'],
"#"	=> ['choosebyn getkey "Digit:"',
	    'choose the Nth chosen file for the given N'],
"%"	=> ['help "-unused"; winch',
	    'list the commands for the choose key mode'],
"."	=> ['choose matchfiles gets "Regexp:"; winch',
	    'choose the display files that match the given pattern'],
":"	=> ['run -ruk gets("(Shell:"), quote(@choose), get("Shell):")',
	    'run a shell command with the chosen files, leaving output'],
";"	=> ['run -uk gets("(Shell;"), quote(@choose), get("Shell);")',
	    'run a shell command with the chosen files, clearing output'],
"@"	=> ['choose lsall', 'choose all the display files'],
"A"	=> ['run -#puk "stat", ifopt("L")', 'run `stat` on the chosen files'],
"B"	=> ['run -X#uk "konqueror"', 'browse the chosen files'],
"C"	=> [['setcomplete \&users; run "-s+/rukL", "chown", gets "Owner:"',
	     'run `chown` on the chosen files',       'oO', 'chown'],
	    ['setcomplete \&groups;'
	     . ' run "-s+/rukL", "chgrp", ifopt("h"), gets "Group:"',
	     'run `chgrp` on the chosen files',       'gG', 'chgrp'],
	    ['setcomplete sub {}; run "-s+/rukL", "chmod", gets "Mode:"',
	     'run `chmod` on the chosen files',       'mM', 'chmod'],
	    ['setcomplete sub {}; run "-s+/rukL", "chmod", "+r"',
	     'run `chmod +r` on the chosen files',    'rR', 'chmod +r'],
	    ['setcomplete sub {}; run "-s+/rukL", "chmod", "+w"',
	     'run `chmod +w` on the chosen files',    'wW', 'chmod +w'],
	    ['setcomplete sub {}; run "-s+/rukL", "chmod", "+x"',
	     'run `chmod +x` on the chosen files',    'xX', 'chmod +x'],
	    ['setcomplete sub {}; run "-s+/rukL", "chmod", "go-rwx"',
	     'run `chmod go-rwx` on the chosen files', '-', 'chmod go-rwx']],
"D"	=> ['ask "Remove recursively?"; run "-#buk", "rm -r"', 'recursively'
	    . ' remove the chosen files/directories (background)'],
"E"	=> ['run -s+uk $cfg::editor', 'edit the chosen files'],
"I"	=> ['run -x#uk "eog"', 'display the chosen image files'],
"R"	=> ['ask "Remove?"; remove @choose' . $cfg::unchoose,
	    'remove the chosen files and empty directories'],
"["	=> ['expand @choose'   . $cfg::unchoose,
	    'expand the chosen directories'],
"]"	=> ['collapse @choose' . $cfg::unchoose,
	    'collapse the chosen directories'],
"`"	=> [['choose getoutput "`Shell`;"; winch',
	     'choose the file list output of a shell (or ;perl) command',
	     'cCsS`;:$', 'command'],
	    ['choose split /:/, $ENV{PATH}; win',
	     'choose the elements of the PATH environment variable',
	     'p',	 'PATH'],
	    ['choose split /:/, $ENV{CD_PATH}; win',
	     'choose the elements of the CD_PATH environment variable',
	     'P',	 'CD_PATH'],
	    ['choose sort &disks; win',	   'choose all system disks',
	     'dDmM',	 'disks'],
	    ['choose sort &diskdevs; win', 'choose all system disk devices',
	     'vV',	 'disk devices']],
""	=> ['cmdeval keymapcmd ""',
	    'execute the main mode command for this key'],
);
@keymap_choose = &akeys(@keymap_choose);

###############################################################################
## "Options" keymap configuration #############################################

%keymap_opts = @keymap_opts = (		## O ##
	'TTLO'	=> 'OPTS MODE KEY COMMANDS',
	"\cN"	=> $keymap_{"\cN"},
	"%"	=> ['help "-unused", "=keymap_opts"; winch',
		    'list the commands for the option key mode'],
	"="	=> ['initopts; keymap; win', 'reset all options'],
	"O"	=> ['keymap', 'pop from option key mode'],
	"mous"	=> $keymap_{"mous"},
	""	=> ['beep; keymap; home', 'invalid option'],
);
@keymap_opts = &akeys(@keymap_opts);
$optkeys = '#*/ABCDFHILNPSTVXabcdfghiklmnoprstuvx';	# enabled options
$optons  = ($color) ? '*CHVn' : 'V';		# options with toggled meaning
%cfg::desc_opts = (
	"#"	=> "list inode number instead of size in long listings",
	"*"	=> "use color",
	"/"	=> "sort by increasing path depth",
	"A"	=> "don't list . and ..",
	"B"	=> "don't list backup files",
	"C"	=> "color files",
	"D"	=> "segregate directories to the list top",
	"F"	=> "sort by file color",
	"H"	=> "color command listings and prompts",
	"I"	=> "sort by increasing inode",
	"L"	=> "follow symlinks for long listings, stat sorting, etc",
	"N"	=> "show/sort owners and groups by ids not names",
	"P"	=> "sort by file basename",
	"S"	=> "sort by decreasing size",
	"T"	=> "tag files",
	"V"	=> "use an audio beep",
	"X"	=> "sort by file extension",
	"a"	=> "don't list dot files",
	"b"	=> "segregate dot files to the list bottom",
	"c"	=> "sort by change time (newest first)",
	"d"	=> "segregate directories to the list bottom",
	"f"	=> "don't sort list",
	"g"	=> "sort by group or increasing gid",
	"h"	=> "long list a symlink's stats not its destination, etc",
	"i"	=> "sort case-insensitively",
	"k"	=> "sort command listings by key",
	"l"	=> "sort by decreasing number of links",
	"m"	=> "sort by decreasing permissions mode",
	"n"	=> "color long listings",
	"o"	=> "sort by owner or increasing uid",
	"p"	=> "prefer command strings instead of command descriptions",
	"r"	=> "reverse list",
	"s"	=> "show Internet time in screen date",
	"t"	=> "sort by modification time (newest first)",
	"u"	=> "sort by access time (newest first)",
	"v"	=> "use a visual beep",
	"x"	=> "don't color files as executable",
);
foreach (split(//, $optons))  {
	$cfg::desc_opts{$_} = "don't $cfg::desc_opts{$_}"
		unless $cfg::desc_opts{$_} =~ s/^don't //;
}
foreach (split(//, $optkeys)) {
	&mapadd('keymap_opts', $_,
		["setopt '$_'; keymap; win", $cfg::desc_opts{$_}]);
}
undef %cfg::desc_opts;

sub opton {
	my $opt = shift;
	return unless length($opt) == 1 && index($optkeys, $opt) >= 0;
	$optons .= $opt unless $optons =~ s/$opt//;
	$keymap_opts{$opt}[1] = "don't $keymap_opts{$opt}[1]"
		unless $keymap_opts{$opt}[1] =~ s/^don't //;
}

###############################################################################
## Main mousemap configuration ################################################

# A common default xterm resources configuration provides only unmodified
# Button/Wheel events and Ctrl-Wheel events to applications, so we
# restrict ourselves to these.  (Shift events retain the xterm cut/paste
# functionality; other Ctrl events bring up xterm menus; combo and some
# Mod1 events are unused but would require reconfiguration to enable for
# applications (untested)).

# Mouse events without actions defined for the zone (explicitly or via a
# default '' event) also default to the '' zone.

# Note that if setnorun is "once" (to describe the next key command),
# this'll still report 1d, 2d and 3d mouse event commands, which are
# generally not defined here.  To see the key command for the 1u, 2u or
# 3u mouse events, setnorun in between, eg, the 1d and the 1u events.

%cfg::typering = ('' => 'do', 'do' => 'expand', 'expand' => '');

%mousemap_ = @mousemap_ = (		## ##
'TTL_'		=> 'MOUSE COMMANDS',
'user'		=> [[@{$keymap_{"~"}},		   &mev2c('1u')]],
'dir'		=> [['setopt "t"; win', ${$keymap_opts{"t"}}[1],
						   &mev2c('1u')],
		    [@{${$keymap_{"X"}}[1]}[0, 1], &mev2c('3u')],
		    [@{$keymap_{"Y"}},		   &mev2c('Wd')],
		    [@{$keymap_{"U"}},		   &mev2c('Wu')]],
'dir...'	=> ['msg $_', 'report the full directory name'],
'title'		=> [[@{$keymap_{"\cF"}},	   &mev2c('1u')],
		    [@{$keymap_{"\cD"}},	   &mev2c('2u')],
		    [@{$keymap_{"\cC"}},	   &mev2c('3u')],
		    ['cmdeval $keymap_{cfg::nextset(+1) || 4}',
		     'cycle forward through the file set displays',
						   &mev2c('Wd')],
		    ['cmdeval $keymap_{cfg::nextset(-1) || 1}',
		     'cycle backward through the file set displays',
						   &mev2c('Wu')],
		    [@{$keymap_{"8"}},		   &mev2c('cWd', 'cWu')]],
'title...'	=> ['msg $_', 'report the full title'],
'state'		=> [['initopts; win', ${$keymap_opts{"="}}[1], &mev2c('2u')],
		    [@{$keymap_{"|"}},			       &mev2c('3u')]],
'file'		=> [['dotypein',      'act on the file by its type'
		     . ' per normal mode',		       &mev2c('1u')],
		    ['dotypein "do"', 'act on the file by its type'
		     . ' per special action mode',	       &mev2c('2u')],
		    ['choose $_; keymap "=choose"',
		     'choose the file and enter choose mode',  &mev2c('3u')],
		    ['expand $_; win',	 'expand the file',    &mev2c('Wd')],
		    ['collapse $_; win', 'collapse the file',  &mev2c('Wu')],
		    ['do { local $depth = -1; expand $_ }; win',
		     'completely expand the file',	       &mev2c('cWd')],
		    ['do { local $depth = -1; collapse $_ }; win',
		     'completely collapse the file',	       &mev2c('cWu')]],
'file...'	=> ['msg $_', 'report the full filename'],
'longls'	=> [[@{$keymap_{"_"}},			 &mev2c('1u')],
		    ['setopt "L"; win', ${$keymap_opts{"L"}}[1],
							 &mev2c('2u')],
		    [@{$keymap_{"+"}},			 &mev2c('3u')]],
'long'		=> [[@{$keymap_{"_"}},			 &mev2c('1u')],
		    [@{$keymap_{"+"}},			 &mev2c('3u')]],
'bag'		=> [['point $_[1]', 'point to the file', &mev2c('1u')],
		    [@{$keymap_{"\cB"}},		 &mev2c('2u')],
		    ['cfg::xcut atabsfile $_',
		     'copy the user\@host:file to X cut buffer $cfg::xcb',
							 &mev2c('3u')],
		    [@{$keymap_{"\cI"}},		 &mev2c('Wd', 'cWd')],
		    [@{$keymap_{"\cH"}},		 &mev2c('Wu', 'cWu')]],
'point'		=> [['point $_[1]', 'point to the file', &mev2c('1u')],
		    [@{$keymap_{"\cB"}},		 &mev2c('2u')],
		    [@{${$keymap_{"X"}}[3]}[0, 1],	 &mev2c('3u')],
		    [@{$keymap_{"}"}},			 &mev2c('Wd')],
		    [@{$keymap_{"{"}},			 &mev2c('Wu')],
		    [@{$keymap_{"pgdn"}},		 &mev2c('cWd')],
		    [@{$keymap_{"pgup"}},		 &mev2c('cWu')]],
'chose#'	=> [['$_[1] ? unchoose($_) : choose($_)',
		     "toggle the file's choose status",  &mev2c('1u')],
		    ['unchoose $_', 'unchoose the file', &mev2c('2u')],
		    ['choose $_',   'choose the file',	 &mev2c('3u')]],
'mode'		=> [# reserve 1u for mousemap's
		    ['typemap "*$cfg::typering{$typemap[$#typemap]}"',
		     'change the file type mode',  &mev2c('2u')],
		    [${$keymap_{"/"}}[0],
		     'change the key mode',	   &mev2c('3u')],
		    [@{${$keymap_{"X"}}[6]}[0, 1], &mev2c( 'Wd',  'Wu')],
		    [@{${$keymap_{"X"}}[7]}[0, 1], &mev2c('cWd', 'cWu')]],
'mode...'	=> ['msg $_', 'report the full mode text'],
'time'		=> [['win', 'redraw the screen',	 &mev2c('1u')],
		    ['opton "s"; win',
		     'toggle the Internet time display', &mev2c('2u')],
		    [@{$keymap_{"\cL"}},		 &mev2c('3u')],
		    [@{$keymap_{"L"}},			 &mev2c('Wd', 'Wu')]],
'time_'		=> [['mousemap "test"',
		     'toggle the test mouse mode',	 &mev2c('3u')]],
#'home'		=> unused
''		=> [# avoid 1u for window management use
		    [@{$keymap_{"\cM"}}, &mev2c('2u')],
		    [@{$keymap_{"\\"}},	 &mev2c('3u')],
		    [@{$keymap_{" "}},	 &mev2c('Wd')],
		    [@{$keymap_{"0"}},	 &mev2c('Wu')],
		    [@{$keymap_{"\c]"}}, &mev2c('cWd')],
		    [@{$keymap_{"\c["}}, &mev2c('cWu')]],
);
@mousemap_ = &akeys(@mousemap_);
&mapadd('mousemap_', 'page',	$mousemap_{'state'},   '<title');
&mapadd('mousemap_', 'filetag',	$mousemap_{'file'},    '>file');
&mapadd('mousemap_', 'file/',	$mousemap_{'file...'}, '>file...');

%mousemap_test = @mousemap_test = (	## ##
'TTLt'		=> 'TEST MODE MOUSE COMMANDS',
'time_'		=> $mousemap_{'time_'},
''		=> ['msg mousetxt', 'report the mouse event'],
);
@mousemap_test = &akeys(@mousemap_test);

###############################################################################
## Subroutines ################################################################

sub onrestart { 1 }
sub onquit    { 1 }

sub cfgcolorlong {
	local $_ = join('', @_); my $s;
	($s = &rccolorlong($_)) ne '' && ($_ = $s) if defined &rccolorlong;
	return &colorlongline($_, $co_error)
		if $long =~ /^\s*md5sum\b/	&& ! /^\\?[0-9a-f]*\\?$/i;
	return &colorlongline($_, $co_msg)
		if $long =~ /^\s*rpm\b/		&&   /is ?n[o']t owned/i;
	return &colorlongline($_, $co_error)
		if $long =~ /^\s*;.*mimetype\b/ &&   /Can ?n?['o]t stat/i;
	s/^([^\s,;]*)(\/)([^\s,;]*)(,\s*)?([^;]*)(;\s*)?(.*)/
		&color($1, $co_ftype) . $2 .
		&color($3, $co_myper) . $4 .
		&color($5, $co_sbits) . $6 .
		&color($7, $co_perms)/e if $long =~ /^\s*;.*mimetype/;
	$_;
}

sub cfg::longls2 {
	my @cmd1 = @{$_[0]}; my @cmd2 = @{$_[1]};
	&longls("-win", $long =~ /^\s*\Q$cmd1[0]\E\b/ ? @cmd2 : @cmd1);
}

sub cfg::lsexpand { grep { ! /(^|\/)\.git$/ } &lsall() }

sub cfg::onecmd { 0 }

sub cfg::pointormark {
	return &point($_[0]) if grep($_[0] eq $_, @bagkeys);
	local($mark, $dir, $file, $path) =
		map { &getmark($_[0], $_) } '', qw(dir file path);
	return &beep()	     if ! $mark;
	&cmdeval([['cd $mark',	   "cd to mark $_[0]",
		   'dc',	   "cd to  $dir @ $file"],
		  ['dotype $path', "act on mark $_[0] by its type",
		   'fp',	   "act on $path"]]);
}

sub cfg::setset {
	grep($altls == $_, \@cfg::set1, \@cfg::set2, \@cfg::set3, \@cfg::set4)
		? do { push(@$altls, @_); 1 } : do { &beep(); 0 };
}

sub cfg::abssets {
	foreach (\@cfg::set1, \@cfg::set2, \@cfg::set3, \@cfg::set4) {
		map(/^\// || do { $_ = &absfile($_, $_[0]) }, @$_);
	}
}

sub cfg::nextset {
	my $s = ($altls == \@cfg::set1) ? 1 : ($altls == \@cfg::set2) ? 2 :
		($altls == \@cfg::set3) ? 3 : ($altls == \@cfg::set4) ? 4 : 0;
	($s + ((defined $_[0]) ? $_[0] : 1)) % 5;
}

sub cfg::maketargets {
	my %targets = ();
	foreach my $makefile (@cfg::makefiles ? @cfg::makefiles
					      : ('makefile', 'Makefile')) {
		next unless open(MAKEFILE, $makefile);
		while (<MAKEFILE>) {
			next unless /^[^\t#].*:/;
			chomp; s/:.*//; $targets{$_}++;
		}
		close MAKEFILE;
	}
	sort keys %targets;
}

###############################################################################
## Personal configuration #####################################################

$vshnurc = $ENV{VSHNURC} || $default_vshnurc ||
	   ((-f "$ENV{HOME}/.vshnurc") ? "$ENV{HOME}/.vshnurc" : 'vshnurc.pl');
(-r _) ? do { do $vshnurc; &err($@) } : &err("Cannot read $vshnurc")
	if -f $vshnurc;

1;
