Merge independent repo of config files
authorIan Kelling <iank@fsf.org>
Mon, 22 Jan 2018 02:09:19 +0000 (21:09 -0500)
committerIan Kelling <iank@fsf.org>
Mon, 22 Jan 2018 02:17:00 +0000 (21:17 -0500)
They were really tied together, so bring them into the same repo

125 files changed:
.Xkeymap [new file with mode: 0644]
.Xresources [new file with mode: 0644]
.bash_profile [new file with mode: 0644]
.bashrc [new file with mode: 0644]
.dir_colors [new file with mode: 0644]
.eclipse-dictionary [new file with mode: 0644]
.gdbinit [new file with mode: 0644]
.gemrc [new file with mode: 0644]
.git_template/hooks/pre-commit [new file with mode: 0755]
.gitconfig [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.gitignore_global [new file with mode: 0644]
.gvimrc [new file with mode: 0644]
.hunspell_en_US [new file with mode: 0644]
.hushlogin [new file with mode: 0644]
.inputrc [new file with mode: 0644]
.libao [new file with mode: 0644]
.mailcap [new file with mode: 0644]
.profile [new file with mode: 0644]
.redshift.conf [new file with mode: 0644]
.screenrc [new file with mode: 0644]
.vim/.netrwhist [new file with mode: 0644]
.vim/after/syntax/c.vim [new file with mode: 0644]
.vim/autoload/SyntaxAttr.vim [new file with mode: 0644]
.vim/colors/ir_black.vim [new file with mode: 0644]
.vim/colors/leo.vim [new file with mode: 0644]
.vim/colors/metacosm.vim [new file with mode: 0644]
.vim/colors/nightsky.vim [new file with mode: 0644]
.vim/colors/refactor.vim [new file with mode: 0644]
.vim/colors/vividchalk.vim [new file with mode: 0644]
.vim/doc/bufexplorer.txt [new file with mode: 0644]
.vim/doc/tags [new file with mode: 0644]
.vim/doc/vcscommand.txt [new file with mode: 0644]
.vim/ftplugin/c.vim [new file with mode: 0644]
.vim/ftplugin/imath.vim [new file with mode: 0644]
.vim/ftplugin/java.vim [new file with mode: 0644]
.vim/ftplugin/txt.vim [new file with mode: 0644]
.vim/misc/filetype.vim [new file with mode: 0644]
.vim/misc/sasvimrc [new file with mode: 0644]
.vim/misc/unix.vim [new file with mode: 0644]
.vim/plugin/matrix.vim [new file with mode: 0644]
.vim/plugin/vcsbzr.vim [new file with mode: 0644]
.vim/plugin/vcscommand.vim [new file with mode: 0644]
.vim/plugin/vcscvs.vim [new file with mode: 0644]
.vim/plugin/vcsgit.vim [new file with mode: 0644]
.vim/plugin/vcshg.vim [new file with mode: 0644]
.vim/plugin/vcssvk.vim [new file with mode: 0644]
.vim/plugin/vcssvn.vim [new file with mode: 0644]
.vim/readme-ian.txt [new file with mode: 0644]
.vim/syntax/cvsannotate.vim [new file with mode: 0644]
.vim/syntax/gitannotate.vim [new file with mode: 0644]
.vim/syntax/hgannotate.vim [new file with mode: 0644]
.vim/syntax/sh.vim [new file with mode: 0644]
.vim/syntax/svkannotate.vim [new file with mode: 0644]
.vim/syntax/svnannotate.vim [new file with mode: 0644]
.vim/syntax/vcscommit.vim [new file with mode: 0644]
.vimrc [new file with mode: 0644]
.xbindkeysrc.scm [new file with mode: 0644]
README
brc [new file with mode: 0644]
browser-home.html [new file with mode: 0644]
filesystem/etc/X11/Xsession.d/98iank [new file with mode: 0644]
filesystem/etc/apt/apt.conf.d/02proxy [new file with mode: 0644]
filesystem/etc/apt/detect-http-proxy [new file with mode: 0755]
filesystem/etc/apt/preferences.d/radicale [new file with mode: 0644]
filesystem/etc/client.conf [new file with mode: 0644]
filesystem/etc/cron.d/ian [new file with mode: 0755]
filesystem/etc/cups/client.conf [new file with mode: 0644]
filesystem/etc/dnsmasq.d/iank.conf [new file with mode: 0644]
filesystem/etc/logrotate.d/mylogs [new file with mode: 0644]
filesystem/etc/modprobe.d/blacklist.conf [new file with mode: 0644]
filesystem/etc/profile.d/environment.sh [new file with mode: 0644]
filesystem/etc/synergy.conf [new file with mode: 0644]
filesystem/etc/systemd/system.conf.d/tz.conf [new file with mode: 0644]
filesystem/etc/systemd/system/btrfsmaint.service [new file with mode: 0644]
filesystem/etc/systemd/system/btrfsmaint.timer [new file with mode: 0644]
filesystem/etc/systemd/system/btrfsmaintstop.service [new file with mode: 0644]
filesystem/etc/systemd/system/btrfsmaintstop.timer [new file with mode: 0644]
filesystem/etc/systemd/system/iptables.service [new file with mode: 0644]
filesystem/etc/systemd/system/openvpn-client-nn@.service [new file with mode: 0644]
filesystem/etc/systemd/system/openvpn-nn@.service [new file with mode: 0644]
filesystem/etc/udev/rules.d/99-kaleidoscope.rules [new file with mode: 0644]
filesystem/etc/updatedb.findutils.cron.local [new file with mode: 0644]
filesystem/usr/share/gitweb/gitweb.cgi [new file with mode: 0755]
flidas-2017-12.xkb [new file with mode: 0644]
machine_specific/btrbk.hosts [new file with mode: 0644]
machine_specific/btrbk/filesystem/etc/systemd/system/btrbk.service [new file with mode: 0644]
machine_specific/btrbk/filesystem/etc/systemd/system/btrbk.timer [new file with mode: 0644]
machine_specific/li/filesystem/etc/bind/bind-writable/db.b8.nz [new file with mode: 0644]
machine_specific/li/filesystem/etc/bind/named.conf.local [new file with mode: 0644]
machine_specific/treetowl/filesystem/etc/cron.d/treetowl [new file with mode: 0755]
machine_specific/treetowl/filesystem/etc/cron.daily/check-lets-encrypt-ssl-settings [new file with mode: 0755]
machine_specific/treetowl/filesystem/etc/cron.daily/mediawiki_backup [new file with mode: 0755]
machine_specific/treetowl/filesystem/etc/systemd/system/btrbk.service [new file with mode: 0644]
machine_specific/treetowl/filesystem/etc/systemd/system/btrbk.timer [new file with mode: 0644]
model01.xkb [new file with mode: 0644]
model01.xkm [new file with mode: 0644]
path_add-function [new file with mode: 0644]
playlists/music/10.xsp [new file with mode: 0644]
playlists/music/8.xsp [new file with mode: 0644]
playlists/music/sad.xsp [new file with mode: 0644]
playlists/music/sad5.xsp [new file with mode: 0644]
playlists/music/sy most.xsp [new file with mode: 0644]
subdir_files/.bazaar/authentication.conf [new file with mode: 0644]
subdir_files/.bazaar/bazaar.conf [new file with mode: 0644]
subdir_files/.config/beets/config.yaml [new file with mode: 0644]
subdir_files/.config/i3/config [new file with mode: 0644]
subdir_files/.config/menus/mate-applications.menu [new file with mode: 0644]
subdir_files/.config/mpv/mpv.conf [new file with mode: 0644]
subdir_files/.config/sakura/sakura.conf [new file with mode: 0644]
subdir_files/.gnupg/gpg.conf [new file with mode: 0644]
subdir_files/.local/share/applications/chromium.desktop [new file with mode: 0644]
subdir_files/.local/share/applications/firefox.desktop [new file with mode: 0644]
subdir_files/.local/share/konsole/README.ian [new file with mode: 0644]
subdir_files/.local/share/konsole/default.keytab [new file with mode: 0644]
subdir_files/.local/share/konsole/profileian.profile [new file with mode: 0644]
subdir_files/.local/share/kxmlgui5/konsole/README.ian [new file with mode: 0644]
subdir_files/.local/share/kxmlgui5/konsole/sessionui.rc [new file with mode: 0644]
subdir_files/.xmonad/xmonad.hs [new file with mode: 0644]
subdir_files/sieve/fsf-test.sieve [new file with mode: 0644]
subdir_files/sieve/fsf.sieve [new file with mode: 0644]
subdir_files/sieve/lists.sieve [new file with mode: 0644]
subdir_files/sieve/main.sieve [new file with mode: 0644]
subdir_files/sieve/maintest.sieve [new file with mode: 0644]
x2.xkb [new file with mode: 0644]

diff --git a/.Xkeymap b/.Xkeymap
new file mode 100644 (file)
index 0000000..b0d4293
--- /dev/null
+++ b/.Xkeymap
@@ -0,0 +1,1761 @@
+xkb_keymap {
+xkb_keycodes "evdev+aliases(qwerty)" {
+    minimum = 8;
+    maximum = 255;
+     <ESC> = 9;
+    <AE01> = 10;
+    <AE02> = 11;
+    <AE03> = 12;
+    <AE04> = 13;
+    <AE05> = 14;
+    <AE06> = 15;
+    <AE07> = 16;
+    <AE08> = 17;
+    <AE09> = 18;
+    <AE10> = 19;
+    <AE11> = 20;
+    <AE12> = 21;
+    <BKSP> = 22;
+     <TAB> = 23;
+    <AD01> = 24;
+    <AD02> = 25;
+    <AD03> = 26;
+    <AD04> = 27;
+    <AD05> = 28;
+    <AD06> = 29;
+    <AD07> = 30;
+    <AD08> = 31;
+    <AD09> = 32;
+    <AD10> = 33;
+    <AD11> = 34;
+    <AD12> = 35;
+    <RTRN> = 36;
+    <LCTL> = 37;
+    <AC01> = 38;
+    <AC02> = 39;
+    <AC03> = 40;
+    <AC04> = 41;
+    <AC05> = 42;
+    <AC06> = 43;
+    <AC07> = 44;
+    <AC08> = 45;
+    <AC09> = 46;
+    <AC10> = 47;
+    <AC11> = 48;
+    <TLDE> = 49;
+    <LFSH> = 50;
+    <BKSL> = 51;
+    <AB01> = 52;
+    <AB02> = 53;
+    <AB03> = 54;
+    <AB04> = 55;
+    <AB05> = 56;
+    <AB06> = 57;
+    <AB07> = 58;
+    <AB08> = 59;
+    <AB09> = 60;
+    <AB10> = 61;
+    <RTSH> = 62;
+    <KPMU> = 63;
+    <LALT> = 64;
+    <SPCE> = 65;
+    <CAPS> = 66;
+    <FK01> = 67;
+    <FK02> = 68;
+    <FK03> = 69;
+    <FK04> = 70;
+    <FK05> = 71;
+    <FK06> = 72;
+    <FK07> = 73;
+    <FK08> = 74;
+    <FK09> = 75;
+    <FK10> = 76;
+    <NMLK> = 77;
+    <SCLK> = 78;
+     <KP7> = 79;
+     <KP8> = 80;
+     <KP9> = 81;
+    <KPSU> = 82;
+     <KP4> = 83;
+     <KP5> = 84;
+     <KP6> = 85;
+    <KPAD> = 86;
+     <KP1> = 87;
+     <KP2> = 88;
+     <KP3> = 89;
+     <KP0> = 90;
+    <KPDL> = 91;
+    <LVL3> = 92;
+    <LSGT> = 94;
+    <FK11> = 95;
+    <FK12> = 96;
+    <AB11> = 97;
+    <KATA> = 98;
+    <HIRA> = 99;
+    <HENK> = 100;
+    <HKTG> = 101;
+    <MUHE> = 102;
+    <JPCM> = 103;
+    <KPEN> = 104;
+    <RCTL> = 105;
+    <KPDV> = 106;
+    <PRSC> = 107;
+    <RALT> = 108;
+    <LNFD> = 109;
+    <HOME> = 110;
+      <UP> = 111;
+    <PGUP> = 112;
+    <LEFT> = 113;
+    <RGHT> = 114;
+     <END> = 115;
+    <DOWN> = 116;
+    <PGDN> = 117;
+     <INS> = 118;
+    <DELE> = 119;
+    <I120> = 120;
+    <MUTE> = 121;
+    <VOL-> = 122;
+    <VOL+> = 123;
+    <POWR> = 124;
+    <KPEQ> = 125;
+    <I126> = 126;
+    <PAUS> = 127;
+    <I128> = 128;
+    <I129> = 129;
+    <HNGL> = 130;
+    <HJCV> = 131;
+    <AE13> = 132;
+    <LWIN> = 133;
+    <RWIN> = 134;
+    <COMP> = 135;
+    <STOP> = 136;
+    <AGAI> = 137;
+    <PROP> = 138;
+    <UNDO> = 139;
+    <FRNT> = 140;
+    <COPY> = 141;
+    <OPEN> = 142;
+    <PAST> = 143;
+    <FIND> = 144;
+     <CUT> = 145;
+    <HELP> = 146;
+    <I147> = 147;
+    <I148> = 148;
+    <I149> = 149;
+    <I150> = 150;
+    <I151> = 151;
+    <I152> = 152;
+    <I153> = 153;
+    <I154> = 154;
+    <I155> = 155;
+    <I156> = 156;
+    <I157> = 157;
+    <I158> = 158;
+    <I159> = 159;
+    <I160> = 160;
+    <I161> = 161;
+    <I162> = 162;
+    <I163> = 163;
+    <I164> = 164;
+    <I165> = 165;
+    <I166> = 166;
+    <I167> = 167;
+    <I168> = 168;
+    <I169> = 169;
+    <I170> = 170;
+    <I171> = 171;
+    <I172> = 172;
+    <I173> = 173;
+    <I174> = 174;
+    <I175> = 175;
+    <I176> = 176;
+    <I177> = 177;
+    <I178> = 178;
+    <I179> = 179;
+    <I180> = 180;
+    <I181> = 181;
+    <I182> = 182;
+    <I183> = 183;
+    <I184> = 184;
+    <I185> = 185;
+    <I186> = 186;
+    <I187> = 187;
+    <I188> = 188;
+    <I189> = 189;
+    <I190> = 190;
+    <FK13> = 191;
+    <FK14> = 192;
+    <FK15> = 193;
+    <FK16> = 194;
+    <FK17> = 195;
+    <FK18> = 196;
+    <FK19> = 197;
+    <FK20> = 198;
+    <FK21> = 199;
+    <FK22> = 200;
+    <FK23> = 201;
+    <FK24> = 202;
+    <MDSW> = 203;
+     <ALT> = 204;
+    <META> = 205;
+    <SUPR> = 206;
+    <HYPR> = 207;
+    <I208> = 208;
+    <I209> = 209;
+    <I210> = 210;
+    <I211> = 211;
+    <I212> = 212;
+    <I213> = 213;
+    <I214> = 214;
+    <I215> = 215;
+    <I216> = 216;
+    <I217> = 217;
+    <I218> = 218;
+    <I219> = 219;
+    <I220> = 220;
+    <I221> = 221;
+    <I222> = 222;
+    <I223> = 223;
+    <I224> = 224;
+    <I225> = 225;
+    <I226> = 226;
+    <I227> = 227;
+    <I228> = 228;
+    <I229> = 229;
+    <I230> = 230;
+    <I231> = 231;
+    <I232> = 232;
+    <I233> = 233;
+    <I234> = 234;
+    <I235> = 235;
+    <I236> = 236;
+    <I237> = 237;
+    <I238> = 238;
+    <I239> = 239;
+    <I240> = 240;
+    <I241> = 241;
+    <I242> = 242;
+    <I243> = 243;
+    <I244> = 244;
+    <I245> = 245;
+    <I246> = 246;
+    <I247> = 247;
+    <I248> = 248;
+    <I249> = 249;
+    <I250> = 250;
+    <I251> = 251;
+    <I252> = 252;
+    <I253> = 253;
+    indicator 1 = "Caps Lock";
+    indicator 2 = "Num Lock";
+    indicator 3 = "Scroll Lock";
+    indicator 4 = "Compose";
+    indicator 5 = "Kana";
+    indicator 6 = "Sleep";
+    indicator 7 = "Suspend";
+    indicator 8 = "Mute";
+    indicator 9 = "Misc";
+    indicator 10 = "Mail";
+    indicator 11 = "Charging";
+    virtual indicator 12 = "Shift Lock";
+    virtual indicator 13 = "Group 2";
+    virtual indicator 14 = "Mouse Keys";
+    alias <AC12> = <BKSL>;
+    alias <MENU> = <COMP>;
+    alias <HZTG> = <TLDE>;
+    alias <LMTA> = <LWIN>;
+    alias <RMTA> = <RWIN>;
+    alias <ALGR> = <RALT>;
+    alias <KPPT> = <I129>;
+    alias <LatQ> = <AD01>;
+    alias <LatW> = <AD02>;
+    alias <LatE> = <AD03>;
+    alias <LatR> = <AD04>;
+    alias <LatT> = <AD05>;
+    alias <LatY> = <AD06>;
+    alias <LatU> = <AD07>;
+    alias <LatI> = <AD08>;
+    alias <LatO> = <AD09>;
+    alias <LatP> = <AD10>;
+    alias <LatA> = <AC01>;
+    alias <LatS> = <AC02>;
+    alias <LatD> = <AC03>;
+    alias <LatF> = <AC04>;
+    alias <LatG> = <AC05>;
+    alias <LatH> = <AC06>;
+    alias <LatJ> = <AC07>;
+    alias <LatK> = <AC08>;
+    alias <LatL> = <AC09>;
+    alias <LatZ> = <AB01>;
+    alias <LatX> = <AB02>;
+    alias <LatC> = <AB03>;
+    alias <LatV> = <AB04>;
+    alias <LatB> = <AB05>;
+    alias <LatN> = <AB06>;
+    alias <LatM> = <AB07>;
+};
+
+xkb_types "complete" {
+
+    virtual_modifiers NumLock,Alt,LevelThree,LAlt,RAlt,RControl,LControl,ScrollLock,LevelFive,AltGr,Meta,Super,Hyper;
+
+    type "ONE_LEVEL" {
+        modifiers= none;
+        level_name[Level1]= "Any";
+    };
+    type "TWO_LEVEL" {
+        modifiers= Shift;
+        map[Shift]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+    };
+    type "ALPHABETIC" {
+        modifiers= Shift+Lock;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Caps";
+    };
+    type "KEYPAD" {
+        modifiers= Shift+NumLock;
+        map[Shift]= Level2;
+        map[NumLock]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+    };
+    type "SHIFT+ALT" {
+        modifiers= Shift+Alt;
+        map[Shift+Alt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift+Alt";
+    };
+    type "PC_SUPER_LEVEL2" {
+        modifiers= Mod4;
+        map[Mod4]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Super";
+    };
+    type "PC_CONTROL_LEVEL2" {
+        modifiers= Control;
+        map[Control]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Control";
+    };
+    type "PC_LCONTROL_LEVEL2" {
+        modifiers= LControl;
+        map[LControl]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "LControl";
+    };
+    type "PC_RCONTROL_LEVEL2" {
+        modifiers= RControl;
+        map[RControl]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "RControl";
+    };
+    type "PC_ALT_LEVEL2" {
+        modifiers= Alt;
+        map[Alt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Alt";
+    };
+    type "PC_LALT_LEVEL2" {
+        modifiers= LAlt;
+        map[LAlt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "LAlt";
+    };
+    type "PC_RALT_LEVEL2" {
+        modifiers= RAlt;
+        map[RAlt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "RAlt";
+    };
+    type "CTRL+ALT" {
+        modifiers= Shift+Control+Alt+LevelThree;
+        map[Shift]= Level2;
+        preserve[Shift]= Shift;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        preserve[Shift+LevelThree]= Shift;
+        map[Control+Alt]= Level5;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "Ctrl+Alt";
+    };
+    type "LOCAL_EIGHT_LEVEL" {
+        modifiers= Shift+Lock+Control+LevelThree;
+        map[Shift+Lock]= Level1;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Control]= Level5;
+        map[Shift+Lock+Control]= Level5;
+        map[Shift+Control]= Level6;
+        map[Lock+Control]= Level6;
+        map[Control+LevelThree]= Level7;
+        map[Shift+Lock+Control+LevelThree]= Level7;
+        map[Shift+Control+LevelThree]= Level8;
+        map[Lock+Control+LevelThree]= Level8;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Level3";
+        level_name[Level4]= "Shift Level3";
+        level_name[Level5]= "Ctrl";
+        level_name[Level6]= "Shift Ctrl";
+        level_name[Level7]= "Level3 Ctrl";
+        level_name[Level8]= "Shift Level3 Ctrl";
+    };
+    type "THREE_LEVEL" {
+        modifiers= Shift+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Level3";
+    };
+    type "EIGHT_LEVEL" {
+        modifiers= Shift+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Shift+Lock+LevelThree]= Level3;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[Lock+LevelFive]= Level6;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[Lock+LevelThree+LevelFive]= Level8;
+        map[Shift+Lock+LevelThree+LevelFive]= Level7;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_LEVEL_FIVE_LOCK" {
+        modifiers= Shift+Lock+NumLock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        preserve[Shift+LevelFive]= Shift;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[NumLock]= Level5;
+        map[Shift+NumLock]= Level6;
+        preserve[Shift+NumLock]= Shift;
+        map[NumLock+LevelThree]= Level7;
+        map[Shift+NumLock+LevelThree]= Level8;
+        map[Shift+NumLock+LevelFive]= Level2;
+        map[NumLock+LevelThree+LevelFive]= Level3;
+        map[Shift+NumLock+LevelThree+LevelFive]= Level4;
+        map[Shift+Lock]= Level2;
+        map[Lock+LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level4;
+        map[Lock+LevelFive]= Level5;
+        map[Shift+Lock+LevelFive]= Level6;
+        preserve[Shift+Lock+LevelFive]= Shift;
+        map[Lock+LevelThree+LevelFive]= Level7;
+        map[Shift+Lock+LevelThree+LevelFive]= Level8;
+        map[Lock+NumLock]= Level5;
+        map[Shift+Lock+NumLock]= Level6;
+        preserve[Shift+Lock+NumLock]= Shift;
+        map[Lock+NumLock+LevelThree]= Level7;
+        map[Shift+Lock+NumLock+LevelThree]= Level8;
+        map[Shift+Lock+NumLock+LevelFive]= Level2;
+        map[Lock+NumLock+LevelThree+LevelFive]= Level3;
+        map[Shift+Lock+NumLock+LevelThree+LevelFive]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_ALPHABETIC_LEVEL_FIVE_LOCK" {
+        modifiers= Shift+Lock+NumLock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        preserve[Shift+LevelFive]= Shift;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[NumLock]= Level5;
+        map[Shift+NumLock]= Level6;
+        preserve[Shift+NumLock]= Shift;
+        map[NumLock+LevelThree]= Level7;
+        map[Shift+NumLock+LevelThree]= Level8;
+        map[Shift+NumLock+LevelFive]= Level2;
+        map[NumLock+LevelThree+LevelFive]= Level3;
+        map[Shift+NumLock+LevelThree+LevelFive]= Level4;
+        map[Lock]= Level2;
+        map[Lock+LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level4;
+        map[Lock+LevelFive]= Level5;
+        map[Shift+Lock+LevelFive]= Level6;
+        map[Lock+LevelThree+LevelFive]= Level7;
+        map[Shift+Lock+LevelThree+LevelFive]= Level8;
+        map[Lock+NumLock]= Level5;
+        map[Shift+Lock+NumLock]= Level6;
+        map[Lock+NumLock+LevelThree]= Level7;
+        map[Shift+Lock+NumLock+LevelThree]= Level8;
+        map[Lock+NumLock+LevelFive]= Level2;
+        map[Lock+NumLock+LevelThree+LevelFive]= Level4;
+        map[Shift+Lock+NumLock+LevelThree+LevelFive]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_SEMIALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level4;
+        preserve[Shift+Lock+LevelThree]= Lock;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[Lock+LevelFive]= Level6;
+        preserve[Lock+LevelFive]= Lock;
+        map[Shift+Lock+LevelFive]= Level6;
+        preserve[Shift+Lock+LevelFive]= Lock;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[Lock+LevelThree+LevelFive]= Level7;
+        preserve[Lock+LevelThree+LevelFive]= Lock;
+        map[Shift+Lock+LevelThree+LevelFive]= Level8;
+        preserve[Shift+Lock+LevelThree+LevelFive]= Lock;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "FOUR_LEVEL" {
+        modifiers= Shift+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Shift+Lock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_SEMIALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level4;
+        preserve[Shift+Lock+LevelThree]= Lock;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_MIXED_KEYPAD" {
+        modifiers= Shift+NumLock+LevelThree;
+        map[Shift+NumLock]= Level1;
+        map[NumLock]= Level2;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[NumLock+LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Shift+NumLock+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_X" {
+        modifiers= Shift+Control+Alt+LevelThree;
+        map[LevelThree]= Level2;
+        map[Shift+LevelThree]= Level3;
+        map[Control+Alt]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Alt Base";
+        level_name[Level3]= "Shift Alt";
+        level_name[Level4]= "Ctrl+Alt";
+    };
+    type "SEPARATE_CAPS_AND_SHIFT_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level4;
+        preserve[Lock]= Lock;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "AltGr Base";
+        level_name[Level4]= "Shift AltGr";
+    };
+    type "FOUR_LEVEL_PLUS_LOCK" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock]= Level5;
+        map[Shift+Lock]= Level2;
+        map[Lock+LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "Lock";
+    };
+    type "FOUR_LEVEL_KEYPAD" {
+        modifiers= Shift+NumLock+LevelThree;
+        map[Shift]= Level2;
+        map[NumLock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[NumLock+LevelThree]= Level4;
+        map[Shift+NumLock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Alt Number";
+    };
+};
+
+xkb_compatibility "complete" {
+
+    virtual_modifiers NumLock,Alt,LevelThree,LAlt,RAlt,RControl,LControl,ScrollLock,LevelFive,AltGr,Meta,Super,Hyper;
+
+    interpret.useModMapMods= AnyLevel;
+    interpret.repeat= False;
+    interpret.locking= False;
+    interpret ISO_Level2_Latch+Exactly(Shift) {
+        useModMapMods=level1;
+        action= LatchMods(modifiers=Shift,clearLocks,latchToLock);
+    };
+    interpret Shift_Lock+AnyOf(Shift+Lock) {
+        action= LockMods(modifiers=Shift);
+    };
+    interpret Num_Lock+AnyOf(all) {
+        virtualModifier= NumLock;
+        action= LockMods(modifiers=NumLock);
+    };
+    interpret ISO_Level3_Shift+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= SetMods(modifiers=LevelThree,clearLocks);
+    };
+    interpret ISO_Level3_Latch+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock);
+    };
+    interpret ISO_Level3_Lock+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= LockMods(modifiers=LevelThree);
+    };
+    interpret Alt_L+AnyOf(all) {
+        virtualModifier= Alt;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Alt_R+AnyOf(all) {
+        virtualModifier= Alt;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Meta_L+AnyOf(all) {
+        virtualModifier= Meta;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Meta_R+AnyOf(all) {
+        virtualModifier= Meta;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Super_L+AnyOf(all) {
+        virtualModifier= Super;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Super_R+AnyOf(all) {
+        virtualModifier= Super;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Hyper_L+AnyOf(all) {
+        virtualModifier= Hyper;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Hyper_R+AnyOf(all) {
+        virtualModifier= Hyper;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Scroll_Lock+AnyOf(all) {
+        virtualModifier= ScrollLock;
+        action= LockMods(modifiers=modMapMods);
+    };
+    interpret ISO_Level5_Shift+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= SetMods(modifiers=LevelFive,clearLocks);
+    };
+    interpret ISO_Level5_Latch+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock);
+    };
+    interpret ISO_Level5_Lock+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= LockMods(modifiers=LevelFive);
+    };
+    interpret Mode_switch+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= SetGroup(group=+1);
+    };
+    interpret ISO_Level3_Shift+AnyOfOrNone(all) {
+        action= SetMods(modifiers=LevelThree,clearLocks);
+    };
+    interpret ISO_Level3_Latch+AnyOfOrNone(all) {
+        action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock);
+    };
+    interpret ISO_Level3_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=LevelThree);
+    };
+    interpret ISO_Group_Latch+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LatchGroup(group=2);
+    };
+    interpret ISO_Next_Group+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LockGroup(group=+1);
+    };
+    interpret ISO_Prev_Group+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LockGroup(group=-1);
+    };
+    interpret ISO_First_Group+AnyOfOrNone(all) {
+        action= LockGroup(group=1);
+    };
+    interpret ISO_Last_Group+AnyOfOrNone(all) {
+        action= LockGroup(group=2);
+    };
+    interpret Pointer_Button_Dflt+AnyOfOrNone(all) {
+        action= PtrBtn(button=default);
+    };
+    interpret Pointer_Button1+AnyOfOrNone(all) {
+        action= PtrBtn(button=1);
+    };
+    interpret Pointer_Button2+AnyOfOrNone(all) {
+        action= PtrBtn(button=2);
+    };
+    interpret Pointer_Button3+AnyOfOrNone(all) {
+        action= PtrBtn(button=3);
+    };
+    interpret Pointer_DblClick_Dflt+AnyOfOrNone(all) {
+        action= PtrBtn(button=default,count=2);
+    };
+    interpret Pointer_DblClick1+AnyOfOrNone(all) {
+        action= PtrBtn(button=1,count=2);
+    };
+    interpret Pointer_DblClick2+AnyOfOrNone(all) {
+        action= PtrBtn(button=2,count=2);
+    };
+    interpret Pointer_DblClick3+AnyOfOrNone(all) {
+        action= PtrBtn(button=3,count=2);
+    };
+    interpret Pointer_Drag_Dflt+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=default,affect=both);
+    };
+    interpret Pointer_Drag1+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=1,affect=both);
+    };
+    interpret Pointer_Drag2+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=2,affect=both);
+    };
+    interpret Pointer_Drag3+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=3,affect=both);
+    };
+    interpret Pointer_EnableKeys+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeys);
+    };
+    interpret Pointer_Accelerate+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeysAccel);
+    };
+    interpret Pointer_DfltBtnNext+AnyOfOrNone(all) {
+        action= SetPtrDflt(affect=button,button=+1);
+    };
+    interpret Pointer_DfltBtnPrev+AnyOfOrNone(all) {
+        action= SetPtrDflt(affect=button,button=-1);
+    };
+    interpret AccessX_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AccessXKeys);
+    };
+    interpret AccessX_Feedback_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AccessXFeedback);
+    };
+    interpret RepeatKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=RepeatKeys);
+    };
+    interpret SlowKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=SlowKeys);
+    };
+    interpret BounceKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=BounceKeys);
+    };
+    interpret StickyKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=StickyKeys);
+    };
+    interpret MouseKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeys);
+    };
+    interpret MouseKeys_Accel_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeysAccel);
+    };
+    interpret Overlay1_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=Overlay1);
+    };
+    interpret Overlay2_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=Overlay2);
+    };
+    interpret AudibleBell_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AudibleBell);
+    };
+    interpret Terminate_Server+AnyOfOrNone(all) {
+        action= Terminate();
+    };
+    interpret Alt_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Alt,clearLocks);
+    };
+    interpret Alt_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Alt,clearLocks);
+    };
+    interpret Meta_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Meta,clearLocks);
+    };
+    interpret Meta_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Meta,clearLocks);
+    };
+    interpret Super_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Super,clearLocks);
+    };
+    interpret Super_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Super,clearLocks);
+    };
+    interpret Hyper_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Hyper,clearLocks);
+    };
+    interpret Hyper_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Hyper,clearLocks);
+    };
+    interpret Shift_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Shift,clearLocks);
+    };
+    interpret XF86Switch_VT_1+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=1,!same);
+    };
+    interpret XF86Switch_VT_2+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=2,!same);
+    };
+    interpret XF86Switch_VT_3+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=3,!same);
+    };
+    interpret XF86Switch_VT_4+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=4,!same);
+    };
+    interpret XF86Switch_VT_5+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=5,!same);
+    };
+    interpret XF86Switch_VT_6+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=6,!same);
+    };
+    interpret XF86Switch_VT_7+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=7,!same);
+    };
+    interpret XF86Switch_VT_8+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=8,!same);
+    };
+    interpret XF86Switch_VT_9+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=9,!same);
+    };
+    interpret XF86Switch_VT_10+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=10,!same);
+    };
+    interpret XF86Switch_VT_11+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=11,!same);
+    };
+    interpret XF86Switch_VT_12+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=12,!same);
+    };
+    interpret XF86LogGrabInfo+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x50,data[1]=0x72,data[2]=0x47,data[3]=0x72,data[4]=0x62,data[5]=0x73,data[6]=0x00);
+    };
+    interpret XF86LogWindowTree+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x50,data[1]=0x72,data[2]=0x57,data[3]=0x69,data[4]=0x6e,data[5]=0x73,data[6]=0x00);
+    };
+    interpret XF86Next_VMode+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x2b,data[1]=0x56,data[2]=0x4d,data[3]=0x6f,data[4]=0x64,data[5]=0x65,data[6]=0x00);
+    };
+    interpret XF86Prev_VMode+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x2d,data[1]=0x56,data[2]=0x4d,data[3]=0x6f,data[4]=0x64,data[5]=0x65,data[6]=0x00);
+    };
+    interpret ISO_Level5_Shift+AnyOfOrNone(all) {
+        action= SetMods(modifiers=LevelFive,clearLocks);
+    };
+    interpret ISO_Level5_Latch+AnyOfOrNone(all) {
+        action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock);
+    };
+    interpret ISO_Level5_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=LevelFive);
+    };
+    interpret Caps_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=Lock);
+    };
+    interpret Any+Exactly(Lock) {
+        action= LockMods(modifiers=Lock);
+    };
+    interpret Any+AnyOf(all) {
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    group 2 = AltGr;
+    group 3 = AltGr;
+    group 4 = AltGr;
+    indicator "Caps Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= Lock;
+    };
+    indicator "Num Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= NumLock;
+    };
+    indicator "Scroll Lock" {
+        whichModState= locked;
+        modifiers= ScrollLock;
+    };
+    indicator "Shift Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= Shift;
+    };
+    indicator "Group 2" {
+        !allowExplicit;
+        groups= 0xfe;
+    };
+    indicator "Mouse Keys" {
+        indicatorDrivesKeyboard;
+        controls= mouseKeys;
+    };
+};
+
+xkb_symbols "pc+us+inet(evdev)+ctrl(nocaps)+terminate(ctrl_alt_bksp)" {
+
+    name[group1]="English (US)";
+
+    key  <ESC> {         [          Escape ] };
+    key <AE01> {         [               1,          exclam ] };
+    key <AE02> {         [               2,              at ] };
+    key <AE03> {         [               3,      numbersign ] };
+    key <AE04> {         [               4,          dollar ] };
+    key <AE05> {         [               5,         percent ] };
+    key <AE06> {         [               6,     asciicircum ] };
+    key <AE07> {         [               7,       ampersand ] };
+    key <AE08> {         [               8,        asterisk ] };
+    key <AE09> {         [               9,       parenleft ] };
+    key <AE10> {         [               0,      parenright ] };
+    key <AE11> {         [           minus,      underscore ] };
+    key <AE12> {         [           equal,            plus ] };
+    key <BKSP> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [       BackSpace,       BackSpace,        NoSymbol,        NoSymbol, Terminate_Server ]
+    };
+    key  <TAB> {         [             Tab,    ISO_Left_Tab ] };
+    key <AD01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               q,               Q ]
+    };
+    key <AD02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               w,               W ]
+    };
+    key <AD03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               e,               E ]
+    };
+    key <AD04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               r,               R ]
+    };
+    key <AD05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               t,               T ]
+    };
+    key <AD06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               y,               Y ]
+    };
+    key <AD07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               u,               U ]
+    };
+    key <AD08> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               i,               I ]
+    };
+    key <AD09> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               o,               O ]
+    };
+    key <AD10> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               p,               P ]
+    };
+    key <AD11> {         [     bracketleft,       braceleft ] };
+    key <AD12> {         [    bracketright,      braceright ] };
+    key <RTRN> {         [          Return ] };
+    key <LCTL> {         [       Control_L ] };
+    key <AC01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               a,               A ]
+    };
+    key <AC02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               s,               S ]
+    };
+    key <AC03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               d,               D ]
+    };
+    key <AC04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               f,               F ]
+    };
+    key <AC05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               g,               G ]
+    };
+    key <AC06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               h,               H ]
+    };
+    key <AC07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               j,               J ]
+    };
+    key <AC08> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               k,               K ]
+    };
+    key <AC09> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               l,               L ]
+    };
+    key <AC10> {         [       semicolon,           colon ] };
+    key <AC11> {         [      apostrophe,        quotedbl ] };
+    key <TLDE> {         [           grave,      asciitilde ] };
+    key <LFSH> {         [         Shift_L ] };
+    key <BKSL> {         [       backslash,             bar ] };
+    key <AB01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               z,               Z ]
+    };
+    key <AB02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               x,               X ]
+    };
+    key <AB03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               c,               C ]
+    };
+    key <AB04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               v,               V ]
+    };
+    key <AB05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               b,               B ]
+    };
+    key <AB06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               n,               N ]
+    };
+    key <AB07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               m,               M ]
+    };
+    key <AB08> {         [           comma,            less ] };
+    key <AB09> {         [          period,         greater ] };
+    key <AB10> {         [           slash,        question ] };
+    key <RTSH> {         [         Shift_R ] };
+    key <KPMU> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [     underscore,      grave,      KP_Multiply,      KP_Multiply,   XF86ClearGrab ]
+    };
+    key <LALT> {         [           Alt_L,          Meta_L ] };
+    key <SPCE> {         [           space ] };
+    key <CAPS> {         [       Control_L,       Control_L ] };
+    key <FK01> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F1,              F1,              F1,              F1, XF86Switch_VT_1 ]
+    };
+    key <FK02> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F2,              F2,              F2,              F2, XF86Switch_VT_2 ]
+    };
+    key <FK03> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F3,              F3,              F3,              F3, XF86Switch_VT_3 ]
+    };
+    key <FK04> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F4,              F4,              F4,              F4, XF86Switch_VT_4 ]
+    };
+    key <FK05> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F5,              F5,              F5,              F5, XF86Switch_VT_5 ]
+    };
+    key <FK06> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F6,              F6,              F6,              F6, XF86Switch_VT_6 ]
+    };
+    key <FK07> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F7,              F7,              F7,              F7, XF86Switch_VT_7 ]
+    };
+    key <FK08> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F8,              F8,              F8,              F8, XF86Switch_VT_8 ]
+    };
+    key <FK09> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F9,              F9,              F9,              F9, XF86Switch_VT_9 ]
+    };
+    key <FK10> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             Pointer_Button1,             Pointer_Button1,             Pointer_Button1,             Pointer_Button1, XF86Switch_VT_10 ]
+    };
+    key <NMLK> {         [        Num_Lock ] };
+    key <SCLK> {         [     Scroll_Lock ] };
+    key  <KP7> {         [         KP_Home,            KP_7 ] };
+    key  <KP8> {         [           KP_Up,            KP_8 ] };
+    key  <KP9> {         [        KP_Prior,            KP_9 ] };
+    key <KPSU> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [     asterisk,     grave,     KP_Subtract,     KP_Subtract,  XF86Prev_VMode ]
+    };
+    key  <KP4> {         [         KP_Left,            KP_4 ] };
+    key  <KP5> {         [        KP_Begin,            KP_5 ] };
+    key  <KP6> {         [        KP_Right,            KP_6 ] };
+    key <KPAD> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [          KP_Add,          asciitilde,          KP_Add,          KP_Add,  XF86Next_VMode ]
+    };
+    key  <KP1> {         [          KP_End,            KP_1 ] };
+    key  <KP2> {         [         KP_Down,            KP_2 ] };
+    key  <KP3> {         [         KP_Next,            KP_3 ] };
+    key  <KP0> {         [       KP_Insert,            KP_0 ] };
+    key <KPDL> {         [       KP_Delete,      KP_Decimal ] };
+    key <LVL3> {         [ ISO_Level3_Shift ] };
+    key <LSGT> {
+        type= "FOUR_LEVEL",
+        symbols[Group1]= [            less,         greater,             bar,       brokenbar ]
+    };
+    key <FK11> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F11,             F11,             F11,             F11, XF86Switch_VT_11 ]
+    };
+    key <FK12> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F12,             F12,             F12,             F12, XF86Switch_VT_12 ]
+    };
+    key <KATA> {         [        Katakana ] };
+    key <HIRA> {         [        Hiragana ] };
+    key <HENK> {         [     Henkan_Mode ] };
+    key <HKTG> {         [ Hiragana_Katakana ] };
+    key <MUHE> {         [        Muhenkan ] };
+    key <KPEN> {         [        KP_Enter ] };
+    key <RCTL> {         [       Control_R ] };
+        key <KPEQ> {    type= "CTRL+ALT",
+        symbols[Group1]= [       parenleft,       KP_Equal,       KP_Equal,       KP_Equal,      XF86Ungrab ]
+    };
+    key <KPDV> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [       parenright,       KP_Divide,       KP_Divide,       KP_Divide,      XF86Ungrab ]
+    };
+    key <PRSC> {
+        type= "PC_ALT_LEVEL2",
+        symbols[Group1]= [           Print,         Sys_Req ]
+    };
+    key <RALT> {
+        type= "TWO_LEVEL",
+        symbols[Group1]= [           Alt_R,          Meta_R ]
+    };
+    key <LNFD> {         [        Linefeed ] };
+    key <HOME> {         [            Home ] };
+    key   <UP> {         [              Up ] };
+    key <PGUP> {         [           Pointer_Button2 ] };
+    key <LEFT> {         [            Left ] };
+    key <RGHT> {         [           Right ] };
+    key  <END> {         [             End ] };
+    key <DOWN> {         [            Down ] };
+    key <PGDN> {         [            Pointer_Button3 ] };
+    key  <INS> {         [          Insert ] };
+    key <DELE> {         [          Delete ] };
+    key <MUTE> {         [   XF86AudioMute ] };
+    key <VOL-> {         [ XF86AudioLowerVolume ] };
+    key <VOL+> {         [ XF86AudioRaiseVolume ] };
+    key <POWR> {         [    XF86PowerOff ] };
+    key <I126> {         [       plusminus ] };
+    key <PAUS> {
+        type= "PC_CONTROL_LEVEL2",
+        symbols[Group1]= [           Pause,           Break ]
+    };
+    key <I128> {         [     XF86LaunchA ] };
+    key <I129> {         [      KP_Decimal,      KP_Decimal ] };
+    key <HNGL> {         [          Hangul ] };
+    key <HJCV> {         [    Hangul_Hanja ] };
+    key <LWIN> {         [         Super_L ] };
+    key <RWIN> {         [         Super_R ] };
+    key <COMP> {         [            Menu ] };
+    key <STOP> {         [          Cancel ] };
+    key <AGAI> {         [            Redo ] };
+    key <PROP> {         [        SunProps ] };
+    key <UNDO> {         [            Undo ] };
+    key <FRNT> {         [        SunFront ] };
+    key <COPY> {         [        XF86Copy ] };
+    key <OPEN> {         [        XF86Open ] };
+    key <PAST> {         [       XF86Paste ] };
+    key <FIND> {         [            Find ] };
+    key  <CUT> {         [         XF86Cut ] };
+    key <HELP> {         [            Help ] };
+    key <I147> {         [      XF86MenuKB ] };
+    key <I148> {         [  XF86Calculator ] };
+    key <I150> {         [       XF86Sleep ] };
+    key <I151> {         [      XF86WakeUp ] };
+    key <I152> {         [    XF86Explorer ] };
+    key <I153> {         [        XF86Send ] };
+    key <I155> {         [        XF86Xfer ] };
+    key <I156> {         [     XF86Launch1 ] };
+    key <I157> {         [     XF86Launch2 ] };
+    key <I158> {         [         XF86WWW ] };
+    key <I159> {         [         XF86DOS ] };
+    key <I160> {         [ XF86ScreenSaver ] };
+    key <I161> {         [ XF86RotateWindows ] };
+    key <I162> {         [    XF86TaskPane ] };
+    key <I163> {         [        XF86Mail ] };
+    key <I164> {         [   XF86Favorites ] };
+    key <I165> {         [  XF86MyComputer ] };
+    key <I166> {         [        XF86Back ] };
+    key <I167> {         [     XF86Forward ] };
+    key <I169> {         [       XF86Eject ] };
+    key <I170> {         [       XF86Eject,       XF86Eject ] };
+    key <I171> {         [   XF86AudioNext ] };
+    key <I172> {         [   XF86AudioPlay,  XF86AudioPause ] };
+    key <I173> {         [   XF86AudioPrev ] };
+    key <I174> {         [   XF86AudioStop,       XF86Eject ] };
+    key <I175> {         [ XF86AudioRecord ] };
+    key <I176> {         [ XF86AudioRewind ] };
+    key <I177> {         [       XF86Phone ] };
+    key <I179> {         [       XF86Tools ] };
+    key <I180> {         [    XF86HomePage ] };
+    key <I181> {         [      XF86Reload ] };
+    key <I182> {         [       XF86Close ] };
+    key <I185> {         [    XF86ScrollUp ] };
+    key <I186> {         [  XF86ScrollDown ] };
+    key <I187> {         [       parenleft ] };
+    key <I188> {         [      parenright ] };
+    key <I189> {         [         XF86New ] };
+    key <I190> {         [            Redo ] };
+    key <FK13> {         [       XF86Tools ] };
+    key <FK14> {         [     XF86Launch5 ] };
+    key <FK15> {         [     XF86Launch6 ] };
+    key <FK16> {         [     XF86Launch7 ] };
+    key <FK17> {         [     XF86Launch8 ] };
+    key <FK18> {         [     XF86Launch9 ] };
+    key <FK20> {         [ XF86AudioMicMute ] };
+    key <FK21> {         [ XF86TouchpadToggle ] };
+    key <FK22> {         [  XF86TouchpadOn ] };
+    key <FK23> {         [ XF86TouchpadOff ] };
+    key <MDSW> {         [     Mode_switch ] };
+    key  <ALT> {         [        NoSymbol,           Alt_L ] };
+    key <META> {         [        NoSymbol,          Meta_L ] };
+    key <SUPR> {         [        NoSymbol,         Super_L ] };
+    key <HYPR> {         [        NoSymbol,         Hyper_L ] };
+    key <I208> {         [   XF86AudioPlay ] };
+    key <I209> {         [  XF86AudioPause ] };
+    key <I210> {         [     XF86Launch3 ] };
+    key <I211> {         [     XF86Launch4 ] };
+    key <I212> {         [     XF86LaunchB ] };
+    key <I213> {         [     XF86Suspend ] };
+    key <I214> {         [       XF86Close ] };
+    key <I215> {         [   XF86AudioPlay ] };
+    key <I216> {         [ XF86AudioForward ] };
+    key <I218> {         [           Print ] };
+    key <I220> {         [      XF86WebCam ] };
+    key <I223> {         [        XF86Mail ] };
+    key <I224> {         [   XF86Messenger ] };
+    key <I225> {         [      XF86Search ] };
+    key <I226> {         [          XF86Go ] };
+    key <I227> {         [     XF86Finance ] };
+    key <I228> {         [        XF86Game ] };
+    key <I229> {         [        XF86Shop ] };
+    key <I231> {         [          Cancel ] };
+    key <I232> {         [ XF86MonBrightnessDown ] };
+    key <I233> {         [ XF86MonBrightnessUp ] };
+    key <I234> {         [  XF86AudioMedia ] };
+    key <I235> {         [     XF86Display ] };
+    key <I236> {         [ XF86KbdLightOnOff ] };
+    key <I237> {         [ XF86KbdBrightnessDown ] };
+    key <I238> {         [ XF86KbdBrightnessUp ] };
+    key <I239> {         [        XF86Send ] };
+    key <I240> {         [       XF86Reply ] };
+    key <I241> {         [ XF86MailForward ] };
+    key <I242> {         [        XF86Save ] };
+    key <I243> {         [   XF86Documents ] };
+    key <I244> {         [     XF86Battery ] };
+    key <I245> {         [   XF86Bluetooth ] };
+    key <I246> {         [        XF86WLAN ] };
+    modifier_map Control { <LCTL> };
+    modifier_map Shift { <LFSH> };
+    modifier_map Shift { <RTSH> };
+    modifier_map Mod1 { <LALT> };
+    modifier_map Control { <CAPS> };
+    modifier_map Mod2 { <NMLK> };
+    modifier_map Mod5 { <LVL3> };
+    modifier_map Control { <RCTL> };
+    modifier_map Mod1 { <RALT> };
+    modifier_map Mod4 { <LWIN> };
+    modifier_map Mod4 { <RWIN> };
+    modifier_map Mod5 { <MDSW> };
+    modifier_map Mod1 { <META> };
+    modifier_map Mod4 { <SUPR> };
+    modifier_map Mod4 { <HYPR> };
+};
+
+xkb_geometry "pc(pc105)" {
+
+    width=       470;
+    height=      180;
+
+    alias <AC00> = <CAPS>;
+    alias <AA00> = <LCTL>;
+
+    baseColor=   "white";
+    labelColor=  "black";
+    xfont=       "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+    description= "Generic 105";
+
+    shape "NORM" {
+        corner= 1,
+        { [  18,  18 ] },
+        { [   2,   1 ], [  16,  16 ] }
+    };
+    shape "BKSP" {
+        corner= 1,
+        { [  38,  18 ] },
+        { [   2,   1 ], [  36,  16 ] }
+    };
+    shape "TABK" {
+        corner= 1,
+        { [  28,  18 ] },
+        { [   2,   1 ], [  26,  16 ] }
+    };
+    shape "BKSL" {
+        corner= 1,
+        { [  28,  18 ] },
+        { [   2,   1 ], [  26,  16 ] }
+    };
+    shape "RTRN" {
+        corner= 1,
+        { [   0,   0 ], [  28,   0 ], [  28,  37 ], [   5,  37 ],
+          [   5,  18 ], [   0,  18 ] },
+        { [   2,   1 ], [  26,   1 ], [  26,  35 ], [   7,  35 ],
+          [   7,  16 ], [   2,  16 ] },
+        approx= { [   5,   0 ], [  28,  37 ] }
+    };
+    shape "CAPS" {
+        corner= 1,
+        { [  33,  18 ] },
+        { [   2,   1 ], [  31,  16 ] }
+    };
+    shape "LFSH" {
+        corner= 1,
+        { [  25,  18 ] },
+        { [   2,   1 ], [  23,  16 ] }
+    };
+    shape "RTSH" {
+        corner= 1,
+        { [  50,  18 ] },
+        { [   2,   1 ], [  48,  16 ] }
+    };
+    shape "MODK" {
+        corner= 1,
+        { [  27,  18 ] },
+        { [   2,   1 ], [  25,  16 ] }
+    };
+    shape "SMOD" {
+        corner= 1,
+        { [  23,  18 ] },
+        { [   2,   1 ], [  21,  16 ] }
+    };
+    shape "SPCE" {
+        corner= 1,
+        { [ 113,  18 ] },
+        { [   2,   1 ], [ 111,  16 ] }
+    };
+    shape "KP0" {
+        corner= 1,
+        { [  37,  18 ] },
+        { [   2,   1 ], [  35,  16 ] }
+    };
+    shape "KPAD" {
+        corner= 1,
+        { [  18,  37 ] },
+        { [   2,   1 ], [  16,  35 ] }
+    };
+    shape "LEDS" { { [  75,  20 ] } };
+    shape "LED" { { [   5,   1 ] } };
+    section "Function" {
+        key.color= "grey20";
+        priority=  7;
+        top=       22;
+        left=      19;
+        width=     351;
+        height=    19;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                {  <ESC>, "NORM",   1 },
+                { <FK01>, "NORM",  20, color="white" },
+                { <FK02>, "NORM",   1, color="white" },
+                { <FK03>, "NORM",   1, color="white" },
+                { <FK04>, "NORM",   1, color="white" },
+                { <FK05>, "NORM",  11, color="white" },
+                { <FK06>, "NORM",   1, color="white" },
+                { <FK07>, "NORM",   1, color="white" },
+                { <FK08>, "NORM",   1, color="white" },
+                { <FK09>, "NORM",  11, color="white" },
+                { <FK10>, "NORM",   1, color="white" },
+                { <FK11>, "NORM",   1, color="white" },
+                { <FK12>, "NORM",   1, color="white" },
+                { <PRSC>, "NORM",   8, color="white" },
+                { <SCLK>, "NORM",   1, color="white" },
+                { <PAUS>, "NORM",   1, color="white" }
+            };
+        };
+    }; // End of "Function" section
+
+    section "Alpha" {
+        key.color= "white";
+        priority=  8;
+        top=       61;
+        left=      19;
+        width=     287;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                { <TLDE>, "NORM",   1 }, { <AE01>, "NORM",   1 },
+                { <AE02>, "NORM",   1 }, { <AE03>, "NORM",   1 },
+                { <AE04>, "NORM",   1 }, { <AE05>, "NORM",   1 },
+                { <AE06>, "NORM",   1 }, { <AE07>, "NORM",   1 },
+                { <AE08>, "NORM",   1 }, { <AE09>, "NORM",   1 },
+                { <AE10>, "NORM",   1 }, { <AE11>, "NORM",   1 },
+                { <AE12>, "NORM",   1 },
+                { <BKSP>, "BKSP",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                {  <TAB>, "TABK",   1, color="grey20" },
+                { <AD01>, "NORM",   1 }, { <AD02>, "NORM",   1 },
+                { <AD03>, "NORM",   1 }, { <AD04>, "NORM",   1 },
+                { <AD05>, "NORM",   1 }, { <AD06>, "NORM",   1 },
+                { <AD07>, "NORM",   1 }, { <AD08>, "NORM",   1 },
+                { <AD09>, "NORM",   1 }, { <AD10>, "NORM",   1 },
+                { <AD11>, "NORM",   1 }, { <AD12>, "NORM",   1 },
+                { <RTRN>, "RTRN",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  39;
+            left= 1;
+            keys {
+                { <CAPS>, "CAPS",   1, color="grey20" },
+                { <AC01>, "NORM",   1 }, { <AC02>, "NORM",   1 },
+                { <AC03>, "NORM",   1 }, { <AC04>, "NORM",   1 },
+                { <AC05>, "NORM",   1 }, { <AC06>, "NORM",   1 },
+                { <AC07>, "NORM",   1 }, { <AC08>, "NORM",   1 },
+                { <AC09>, "NORM",   1 }, { <AC10>, "NORM",   1 },
+                { <AC11>, "NORM",   1 }, { <BKSL>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  58;
+            left= 1;
+            keys {
+                { <LFSH>, "LFSH",   1, color="grey20" },
+                { <LSGT>, "NORM",   1 }, { <AB01>, "NORM",   1 },
+                { <AB02>, "NORM",   1 }, { <AB03>, "NORM",   1 },
+                { <AB04>, "NORM",   1 }, { <AB05>, "NORM",   1 },
+                { <AB06>, "NORM",   1 }, { <AB07>, "NORM",   1 },
+                { <AB08>, "NORM",   1 }, { <AB09>, "NORM",   1 },
+                { <AB10>, "NORM",   1 },
+                { <RTSH>, "RTSH",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                { <LCTL>, "MODK",   1, color="grey20" },
+                { <LWIN>, "SMOD",   1, color="grey20" },
+                { <LALT>, "SMOD",   1, color="grey20" },
+                { <SPCE>, "SPCE",   1 },
+                { <RALT>, "SMOD",   1, color="grey20" },
+                { <RWIN>, "SMOD",   1, color="grey20" },
+                { <MENU>, "SMOD",   1, color="grey20" },
+                { <RCTL>, "SMOD",   1, color="grey20" }
+            };
+        };
+    }; // End of "Alpha" section
+
+    section "Editing" {
+        key.color= "grey20";
+        priority=  9;
+        top=       61;
+        left=      312;
+        width=     58;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                {  <INS>, "NORM",   1 }, { <HOME>, "NORM",   1 },
+                { <PGUP>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                { <DELE>, "NORM",   1 }, {  <END>, "NORM",   1 },
+                { <PGDN>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  58;
+            left= 20;
+            keys {
+                {   <UP>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                { <LEFT>, "NORM",   1 }, { <DOWN>, "NORM",   1 },
+                { <RGHT>, "NORM",   1 }
+            };
+        };
+    }; // End of "Editing" section
+
+    section "Keypad" {
+        key.color= "grey20";
+        priority=  10;
+        top=       61;
+        left=      376;
+        width=     77;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                { <NMLK>, "NORM",   1 }, { <KPDV>, "NORM",   1 },
+                { <KPMU>, "NORM",   1 }, { <KPSU>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                {  <KP7>, "NORM",   1, color="white" },
+                {  <KP8>, "NORM",   1, color="white" },
+                {  <KP9>, "NORM",   1, color="white" },
+                { <KPAD>, "KPAD",   1 }
+            };
+        };
+        row {
+            top=  39;
+            left= 1;
+            keys {
+                {  <KP4>, "NORM",   1, color="white" },
+                {  <KP5>, "NORM",   1, color="white" },
+                {  <KP6>, "NORM",   1, color="white" }
+            };
+        };
+        row {
+            top=  58;
+            left= 1;
+            keys {
+                {  <KP1>, "NORM",   1, color="white" },
+                {  <KP2>, "NORM",   1, color="white" },
+                {  <KP3>, "NORM",   1, color="white" },
+                { <KPEN>, "KPAD",   1 }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                {  <KP0>, "KP0",   1, color="white" },
+                { <KPDL>, "NORM",   1, color="white" }
+            };
+        };
+    }; // End of "Keypad" section
+
+    solid "LedPanel" {
+        top=      22;
+        left=     377;
+        priority= 0;
+        color= "grey10";
+        shape= "LEDS";
+    };
+    indicator "Num Lock" {
+        top=      37;
+        left=     382;
+        priority= 1;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    indicator "Caps Lock" {
+        top=      37;
+        left=     407;
+        priority= 2;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    indicator "Scroll Lock" {
+        top=      37;
+        left=     433;
+        priority= 3;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    text "NumLockLabel" {
+        top=      25;
+        left=     378;
+        priority= 4;
+        width=  19.8;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Num\nLock";
+    };
+    text "CapsLockLabel" {
+        top=      25;
+        left=     403;
+        priority= 5;
+        width=  26.4;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Caps\nLock";
+    };
+    text "ScrollLockLabel" {
+        top=      25;
+        left=     428;
+        priority= 6;
+        width=  39.6;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Scroll\nLock";
+    };
+};
+
+};
diff --git a/.Xresources b/.Xresources
new file mode 100644 (file)
index 0000000..9cddbd9
--- /dev/null
@@ -0,0 +1,13 @@
+Xft.autohint: 0
+Xft.lcdfilter: lcddefault
+Xft.hintstyle: hintfull
+Xft.hinting: 1
+Xft.antialias: 1
+Xft.rgba: vrgb
+
+! commented because I am not using urxvt currently
+!URxvt*font: xft:DejaVu Sans Mono:size=11
+!URxvt*boldFont: xft:DejaVu Sans Mono:size=11
+!URxvt*foreground: White
+!URxvt*background: Black
+!URxvt.scrollstyle: plain
diff --git a/.bash_profile b/.bash_profile
new file mode 100644 (file)
index 0000000..d3a6a8f
--- /dev/null
@@ -0,0 +1,17 @@
+# man bash covers everything comprehensively of course.  i use ~/.bash_profile
+# to source bashrc, and .profile just echos that the normal bash startup process
+# is not happening.  I don't source bashrc in posix mode based on debian's
+# default, and posix mode is quirky, doesn't seem worth figuring it out This
+# setup ensures no distro can override anything, as they occasionally do things
+# I don't like. For example, debian's root .profile spamms with "mesg n"when
+# there is no tty. The mesg n ensures the terminal is not writable by other
+# users, but it is that way without the mesg n, and google search, and debian
+# wiki search, /usr/share/doc search gives no justification for it, and it's not
+# in fedora, so I'm pretty confident that it is useless redundant security, plus
+# it is purposefully in a user startup file, not a system one, so intended for
+# the user to change.
+
+[[ -f ~/.bashrc ]] && . ~/.bashrc
+
+# ensure no bad programs appending to this file will have an affect
+return 0
diff --git a/.bashrc b/.bashrc
new file mode 100644 (file)
index 0000000..d0c4b02
--- /dev/null
+++ b/.bashrc
@@ -0,0 +1,64 @@
+# Copyright (C) 2016 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# to debug
+#set -x
+# redirect output to log file. this doesn't work. todo figure out why
+#exec 1>>/a/tmp/bashlog
+#exec 2>>/a/tmp/bashlog
+
+
+# By default this file is sourced for ALL ssh commands. This is wonky.
+# Normally, this file is not sourced when a script is run, but we can
+# override that by having #!/bin/bash -l.  I want something similar for ssh
+# commands. when a local script runs an ssh command, this file should not be
+# sourced by default, but we should be able to override that.
+#
+# So here we test for conditions of a script under ssh and return if so.
+# And we don't keep the rest of the code in this file, because even
+# though we return, we already parsed the whole code, and as I develop
+# the code, the parsing can have errors, which can screw up cronjobs
+# etc. To test for an overriding condition, we have a few options. one
+# is to use an environment variable.  env variables sent across ssh are
+# strictly limited. ssh -t which sets $SSH_TTY, but within a script that
+# won't work because tty allocation will fail.  We could override an
+# obscure unused LC_var, like telephone, but I don't want to run into
+# some edge case where that messes things up. we could transfer a file
+# which we could test for, but I can't think of a way to make that
+# inherently limited to a single ssh command.  I choose to set SendEnv
+# and AcceptEnv ssh config vars to allow the environment variable
+# BASH_LOGIN_SHELL to propagate across ssh.
+
+# assume we want ssh commands to source this file if we are sourcing it,
+# and we haven't specified otherwise already
+[[ ! $BASH_LOGIN_SHELL ]] && export BASH_LOGIN_SHELL=true
+#BASH_LOGIN_SHELL=false # temporary override
+
+# first conditions show that we are an ssh command without an interactive shell
+if [[ $SSH_CONNECTION ]] \
+       && [[ $- == *c* ]] \
+       && [[ ! $SSH_TTY ]] \
+       && [[ $- != *i* ]] \
+       && [[ ! $BASH_LOGIN_SHELL == true ]]; then
+    return
+else
+    if [[ -r /etc/profile ]]; then
+        source /etc/profile
+    fi
+    _x=$(readlink -f $BASH_SOURCE)
+    _x=${_x%/*}/brc
+    if [[ -r $_x ]]; then
+        source $_x
+    fi
+fi
diff --git a/.dir_colors b/.dir_colors
new file mode 100644 (file)
index 0000000..518294f
--- /dev/null
@@ -0,0 +1,482 @@
+# LS_COLORS
+# Maintainer: Magnus Woldrich <m@japh.se>
+# URL: https://github.com/trapd00r/LS_COLORS
+# Version: master
+# Updated: undefined
+#
+# This is a collection of extension:color mappings, suitable to use as your
+# LS_COLORS environment variable. Most of them use the extended color map,
+# described in the ECMA-48 document; in other words, you'll need a terminal
+# with capabilities of displaying 256 colors.
+#
+# As of this writing, around 300 different filetypes/extensions is supported.
+# That's indeed a lot of extensions, but there's a lot more! Therefore I need
+# your help.
+#
+# Fork this project on github, add the extensions you are missing, and send me
+# a pull request.
+#
+# For files that usually ends up next to each other, like html, css and js,
+# try to pick colors that fit nicely together. Filetypes with multiple
+# possible extensions, like htm and html, should have the same color.
+#
+# To install and use this file, put something like this is your shell resource
+# file:
+#
+# eval $( dircolors -b $HOME/.LS_COLORS)
+#
+# where .LS_COLORS is this file.
+
+# {{{ Base
+BLK 38;5;68
+CAPABILITY 38;5;17
+CHR 38;5;113;1
+DIR 38;5;33
+DOOR 38;5;127
+EXEC 38;5;174;1
+FIFO 38;5;126
+FILE 1
+LINK target
+MULTIHARDLINK 38;5;220;1
+ORPHAN 38;5;196;38;5;232;1
+OTHER_WRITABLE 38;5;220;1
+SETGID 38;5;234;38;5;100;1
+SETUID 38;5;137
+SOCK 38;5;197
+STICKY 38;5;86;48;5;234
+STICKY_OTHER_WRITABLE 38;5;235;38;5;139
+# }}}
+# Code, text, documents {{{
+.BAT 38;5;108
+.PL 38;5;160
+.asm 38;5;240;1
+.awk 38;5;148;1
+.bash 38;5;173
+.bat 38;5;108
+.c 38;5;110
+.cfg 38;5;63
+.coffee 38;5;94;1
+.cow 38;5;94;1
+.conf 38;5;63
+.cpp 38;5;24;1
+.cs 38;5;74;1
+.css 38;5;91
+.csv 38;5;78
+.ctp 38;5;95
+.diff 38;5;197
+.enc 38;5;192;3
+.eps 38;5;192
+.etx 38;5;172
+.ex 38;5;148;1
+.example 38;5;225;1
+.git 38;5;197
+.gitignore 38;5;240
+.go 38;5;117;1
+.h 38;5;81
+.hs 38;5;155;1
+.htm 38;5;125;1
+.html 38;5;125;1
+.info 38;5;101
+.ini 38;5;122
+.java 38;5;142;1
+.jhtm 38;5;125;1
+.js 38;5;42
+.jsm 38;5;42
+.jsm 38;5;42
+.json 38;5;13
+.jsp 38;5;45
+.lisp 38;5;204;1
+.log 38;5;190
+.lua 38;5;34;1
+.map 38;5;58;3
+.markdown 38;5;184
+.md 38;5;184
+.mf 38;5;220;3
+.mfasl 38;5;73
+.mi 38;5;124
+.mkd 38;5;184
+.mtx 38;5;36;3
+.nfo 38;5;220
+.o 38;5;240
+.pacnew 38;5;33
+.patch 38;5;5;1
+.pc 38;5;100
+.pfa 38;5;43
+.php 38;5;93
+.pid 38;5;160
+.pl 38;5;214
+.pm 38;5;197;1
+.pod 38;5;172;1
+.py 38;5;41
+.rb 38;5;192
+.rdf 38;5;144
+.rst 38;5;67
+.ru 38;5;142
+.sed 38;5;130;1
+.sfv 38;5;197
+.sh 38;5;113
+.signature 38;5;206
+.sql 38;5;222
+.srt 38;5;116
+.sty 38;5;58
+.sug 38;5;44
+.t 38;5;28;1
+.tcl 38;5;64;1
+.tdy 38;5;214
+.tex 38;5;172
+.textile 38;5;106
+.tfm 38;5;64
+.tfnt 38;5;140
+.theme 38;5;109
+.txt 38;5;192
+.urlview 38;5;85
+.vim 38;5;28
+.viminfo 38;5;240;1
+.xml 38;5;13
+.yml 38;5;208
+.zsh 38;5;173
+
+# Build stuff
+.1 38;5;196;1
+.1p 38;5;160
+.3p 38;5;160
+.am 38;5;242
+.in 38;5;242
+.old 38;5;242
+.out 38;5;46;1
+
+
+
+
+#}}}
+# Pictures, icons {{{
+.bmp 38;5;62
+.cdr 38;5;59
+.gif 38;5;72
+.ico 38;5;73
+.jpeg 38;5;66
+.jpg 38;5;66
+.JPG 38;5;66
+.png 38;5;68;1
+.svg 38;5;24;1
+.xpm 38;5;36
+#}}}
+# Emulator roms, emulator savegames {{{
+.32x 38;5;137
+.A64 38;5;82
+.a00 38;5;11
+.a52 38;5;112
+.a64 38;5;82
+.a78 38;5;112
+.adf 38;5;35
+.atr 38;5;213
+.cdi 38;5;124
+.fm2 38;5;35
+.gb 38;5;203
+.gba 38;5;205
+.gbc 38;5;204
+.gel 38;5;83
+.gg 38;5;138
+.ggl 38;5;83
+.j64 38;5;102
+.nds 38;5;193
+.nes 38;5;160
+.rom 38;5;59;1
+.sav 38;5;220
+.sms 38;5;33
+.st 38;5;208;1
+#}}}
+# Images {{{
+.ipsw 38;5;146
+.iso 38;5;1
+.nrg 38;5;124
+.qcow 38;5;141
+#}}}
+# Video {{{
+.VOB 38;5;137
+.IFO 38;5;240
+.BUP 38;5;241
+.MOV 38;5;42
+.asf 38;5;25
+.avi 38;5;114
+.flv 38;5;131
+.m2v 38;5;166
+.mkv 38;5;173
+.mov 38;5;42
+.mp4 38;5;9
+.mpg 38;5;38
+.mpeg 38;5;38
+.ogm 38;5;97
+.ogv 38;5;94
+.rmvb 38;5;112
+.sample 38;5;130;1
+.ts 38;5;39
+.vob 38;5;137
+.webm 38;5;109
+.wmv 38;5;113
+#}}}
+# Music, audio {{{
+.S3M 38;5;71;1
+.aac 38;5;137
+.cue 38;5;112
+.dat 38;5;165
+.dts 38;5;100;1
+.fcm 38;5;41
+.flac 38;5;166;1
+.m3u 38;5;172
+.m4 38;5;196;3
+.m4a 38;5;137;1
+.mid 38;5;102
+.midi 38;5;102
+.mod 38;5;72
+.mp3 38;5;191
+.oga 38;5;95
+.ogg 38;5;96
+.s3m 38;5;71;1
+.sid 38;5;69;1
+.spl 38;5;173
+.wv 38;5;149
+.wvc 38;5;149
+#}}}
+# Fonts {{{
+.afm 38;5;58
+.pfb 38;5;58
+.pfm 38;5;58
+.ttf 38;5;1
+.pcf 38;5;65
+.psf 38;5;64
+#}}}
+# Backups, undo files, tempfiles {{{
+.bak 38;5;41;1
+.bin 38;5;249
+.swo 38;5;236
+.swp 38;5;241
+.tmp 38;5;244
+.un~ 38;5;240
+.zcompdump 38;5;240
+.zwc 38;5;240
+#}}}
+# Databases {{{
+.db 38;5;60
+.dump 38;5;119
+.sqlite 38;5;60
+.typelib 38;5;60
+
+#}}}
+# Archives {{{
+.7z 38;5;40
+.a 38;5;46
+.arj 38;5;41
+.bz2 38;5;44
+.ipk 38;5;117
+.jad 38;5;50
+.jar 38;5;51
+.part 38;5;240;1
+.r00 38;5;235
+.r01 38;5;235
+.r02 38;5;235
+.r03 38;5;236
+.r04 38;5;237
+.r05 38;5;238
+.r06 38;5;239
+.r07 38;5;240
+.r08 38;5;241
+.r09 38;5;242
+.r10 38;5;243
+.r100 38;5;244
+.r101 38;5;240
+.r102 38;5;241
+.r103 38;5;242
+.r104 38;5;243
+.r105 38;5;244
+.r106 38;5;245
+.r107 38;5;246
+.r108 38;5;247
+.r109 38;5;248
+.r11 38;5;244
+.r110 38;5;249
+.r111 38;5;250
+.r112 38;5;251
+.r113 38;5;252
+.r114 38;5;253
+.r115 38;5;254
+.r116 38;5;255
+.r12 38;5;245
+.r13 38;5;246
+.r14 38;5;247
+.r15 38;5;248
+.r16 38;5;249
+.r17 38;5;250
+.r18 38;5;251
+.r19 38;5;252
+.r20 38;5;253
+.r21 38;5;254
+.r22 38;5;255
+.r25 38;5;255
+.r26 38;5;254
+.r27 38;5;253
+.r28 38;5;252
+.r29 38;5;251
+.r30 38;5;250
+.r31 38;5;249
+.r32 38;5;248
+.r33 38;5;247
+.r34 38;5;246
+.r35 38;5;245
+.r36 38;5;244
+.r37 38;5;243
+.r38 38;5;242
+.r39 38;5;241
+.r40 38;5;240
+.r41 38;5;239
+.r42 38;5;238
+.r43 38;5;237
+.r44 38;5;236
+.r45 38;5;235
+.r46 38;5;235
+.r47 38;5;235
+.r48 38;5;235
+.r49 38;5;235
+.r50 38;5;236
+.r51 38;5;237
+.r52 38;5;238
+.r53 38;5;239
+.r54 38;5;240
+.r55 38;5;241
+.r56 38;5;242
+.r57 38;5;243
+.r58 38;5;244
+.r59 38;5;245
+.r60 38;5;246
+.r61 38;5;247
+.r62 38;5;248
+.r63 38;5;249
+.r64 38;5;250
+.r65 38;5;251
+.r66 38;5;252
+.r67 38;5;253
+.r68 38;5;254
+.r69 38;5;255
+.r69 38;5;255
+.r70 38;5;254
+.r71 38;5;253
+.r72 38;5;252
+.r73 38;5;251
+.r74 38;5;250
+.r75 38;5;249
+.r76 38;5;248
+.r77 38;5;247
+.r78 38;5;246
+.r79 38;5;245
+.r80 38;5;244
+.r81 38;5;243
+.r82 38;5;242
+.r83 38;5;241
+.r84 38;5;240
+.r85 38;5;239
+.r86 38;5;238
+.r87 38;5;237
+.r88 38;5;236
+.r89 38;5;235
+.r90 38;5;235
+.r91 38;5;235
+.r92 38;5;236
+.r93 38;5;237
+.r94 38;5;238
+.r95 38;5;239
+.r96 38;5;240
+.r97 38;5;241
+.r98 38;5;242
+.r99 38;5;243
+.rar 38;5;106;1
+.tar 38;5;118
+.tar.gz 38;5;34
+.tgz 38;5;35;1
+.xz 38;5;118
+.zip 38;5;41
+.xpi 38;5;106
+#}}}
+# {{{ Really compressed archives (documends, comics)
+.pdf 38;5;203
+.djvu 38;5;141
+.cbr 38;5;140
+.cbz 38;5;140
+.chm 38;5;144
+#}}}
+# Open document formats {{{
+.odt 38;5;111
+.ods 38;5;112
+.odp 38;5;166
+.odb 38;5;161
+#}}}
+# /etc/hosts.{deny,allow} {{{
+.allow 38;5;112
+.deny 38;5;196
+#}}}
+# other {{{
+.SKIP 38;5;244
+.def 38;5;136
+.directory 38;5;83
+.err 38;5;160;1
+.error 38;5;160;1
+.pi 38;5;126
+.properties 38;5;197;1
+.torrent 38;5;2
+.gp3 38;5;114
+.gp4 38;5;115
+.tg 38;5;99
+#}}}
+
+# {{{ TERM
+TERM Eterm
+TERM ansi
+TERM color-xterm
+TERM con132x25
+TERM con132x30
+TERM con132x43
+TERM con132x60
+TERM con80x25
+TERM con80x28
+TERM con80x30
+TERM con80x43
+TERM con80x50
+TERM con80x60
+TERM cons25
+TERM console
+TERM cygwin
+TERM dtterm
+TERM eterm-color
+TERM gnome
+TERM gnome-256color
+TERM jfbterm
+TERM konsole
+TERM kterm
+TERM linux
+TERM linux-c
+TERM mach-color
+TERM mlterm
+TERM putty
+TERM rxvt
+TERM rxvt-256color
+TERM rxvt-cygwin
+TERM rxvt-cygwin-native
+TERM rxvt-unicode
+TERM rxvt-unicode-256color
+TERM rxvt-unicode256
+TERM screen
+TERM screen-256color
+TERM screen-256color-bce
+TERM screen-bce
+TERM screen-w
+TERM screen.rxvt
+TERM screen.linux
+TERM terminator
+TERM vt100
+TERM xterm
+TERM xterm-16color
+TERM xterm-256color
+TERM xterm-88color
+TERM xterm-color
+TERM xterm-debian
+# }}}
+# vim: set ft=dircolors fdm=marker et sw=2:
diff --git a/.eclipse-dictionary b/.eclipse-dictionary
new file mode 100644 (file)
index 0000000..0e2693d
--- /dev/null
@@ -0,0 +1 @@
+downside
diff --git a/.gdbinit b/.gdbinit
new file mode 100644 (file)
index 0000000..5c6ecb8
--- /dev/null
+++ b/.gdbinit
@@ -0,0 +1,13 @@
+
+# based on this gdb output:
+# warning: File "/a/opt/emacs-debiantesting/src/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
+# To enable execution of this file add
+# add-auto-load-safe-path /a/opt/emacs-debiantesting/src/.gdbinit
+# line to your configuration file "/home/ian/.gdbinit".
+# To completely disable this security protection add
+# set auto-load safe-path /
+# line to your configuration file "/home/ian/.gdbinit".
+# For more information about this security protection see the
+# "Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
+# info "(gdb)Auto-loading safe path"
+set auto-load safe-path /
diff --git a/.gemrc b/.gemrc
new file mode 100644 (file)
index 0000000..d90ed0f
--- /dev/null
+++ b/.gemrc
@@ -0,0 +1,2 @@
+# if a gem fails on documentation, uncomment this.
+# gem: --no-document
diff --git a/.git_template/hooks/pre-commit b/.git_template/hooks/pre-commit
new file mode 100755 (executable)
index 0000000..182dcb9
--- /dev/null
@@ -0,0 +1,128 @@
+#!/bin/bash
+# Copyright (C) 2016 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# ian: This section is the git upstream example hook, but it's actually useful.
+
+# An example hook script to verify what is about to be committed.
+# Called by "git commit" with no arguments.  The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-commit".
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+    against=HEAD
+else
+    # Initial commit: diff against an empty tree object
+    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+fi
+
+# If you want to allow non-ASCII filenames set this variable to true.
+allownonascii=$(git config --bool hooks.allownonascii)
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Cross platform projects tend to avoid non-ASCII filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [[ $allownonascii != true ]] &&
+       # Note that the use of brackets around a tr range is ok here, (it's
+       # even required, for portability to Solaris 10's /usr/bin/tr), since
+       # the square bracket bytes happen to fall in the designated range.
+       test $(git diff --cached --name-only --diff-filter=A -z $against |
+                  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
+then
+    cat <<EOF
+Error: Attempt to add a non-ASCII file name.
+This can cause problems if you want to work with people on other platforms.
+To be portable it is advisable to rename the file.
+If you know what you are doing you can disable this check using:
+  git config hooks.allownonascii true
+EOF
+    exit 1
+fi
+
+
+
+# ian: inspired from:
+# https://github.com/ntc2/conf/blob/master/dot.gitconfig
+# last checked for any changes there on 7/2016
+# duplicated at,
+# http://stackoverflow.com/a/15398512/14456
+
+
+set -eE -o pipefail
+trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?"' ERR
+
+if [[ $(git config --bool hooks.allowwhitespace) == true ]] || \
+       [[ $GIT_ALLOWWHITESPACE == true ]]; then
+    exit 0
+fi
+
+# bail out when we call ourselves or there are no whitespace issues.
+if [[ $GIT_ALLOWWHITESPACE == ignore ]] ||
+       ws_issues=$(git diff-index --check --cached $against --); then
+    echo "$ws_issues"
+    exit 0
+fi
+if [[ -d "$(git rev-parse --git-dir)/rebase-merge" ]] ; then
+    cat <<'EOF'
+warning: whitespace issues, but rebase in progress, so I can't/won't fix them.
+If you want to fix them, do for example:
+git rebase --whitespace=fix HEAD~2
+EOF
+    echo "$ws_issues"
+    exit 0
+fi
+# can't do it on the first commit
+if ! git rev-list HEAD --count &>/dev/null; then
+    cat <<'EOF'
+Whitespace issues found. We can't fix in a pre-commit hook for the first commit.
+Either fix on your own. I suggest https://github.com/dlenski/wtf, from git root:
+find . -not -name .git -type f -exec bash -c \
+         'grep -Il "" "$1" &>/dev/null && wtf.py -i -E lf "$1"' _ {} \;
+or allow whitespace with:
+git config hooks.allowwhitespace true, or export GIT_ALLOWWHITESPACE=true
+EOF
+    echo "$ws_issues"
+    exit 1
+fi
+echo "Fixing whitespace lines. To avoid this, do:
+git config hooks.allowwhitespace true, or export GIT_ALLOWWHITESPACE=true"
+export GIT_ALLOWWHITESPACE=ignore
+if ! git diff-files --quiet . && \
+        ! git diff-index --quiet --cached HEAD; then
+    # - dirty tree and dirty index
+    git commit -m "temporary commit of index while fixing whitespace"
+    git add -u :/
+    git commit -m "temporary commit of non-index while fixing whitespace"
+    git rebase --whitespace=fix HEAD~2
+    git reset HEAD~ &&
+        git reset --soft HEAD~
+elif ! git diff-files --quiet .; then
+    # - dirty tree and clean index
+    git add -u :/
+    git commit -m "temporary commit of non-index while fixing whitespace"
+    git rebase --whitespace=fix HEAD~
+    git reset HEAD~
+elif ! git diff-index --quiet --cached HEAD; then
+    # - clean tree and dirty index
+    git commit -m "temporary commit of index while fixing whitespace"
+    git rebase --whitespace=fix HEAD~
+    git reset --soft HEAD~
+fi
diff --git a/.gitconfig b/.gitconfig
new file mode 100644 (file)
index 0000000..c4fd69d
--- /dev/null
@@ -0,0 +1,67 @@
+[user]
+name = Ian Kelling
+email = iank@fsf.org
+[alias]
+# Always use the git lg alias instead of git log.  It's too easy to get
+# confused by not seeing branches in git log output.
+lg = log --graph --decorate
+co = checkout
+s = status
+ci = commit
+lol = log --graph --decorate --pretty=oneline --abbrev-commit --all
+
+[core]
+excludesfile = ~/.gitignore_global
+[github]
+user = ian-kelling
+token = d2dc0c5c6fef5378cc896a617e53c1a9
+[credential]
+helper = cache
+
+# new option, but not available yet on debian wheezy
+# [push]
+#      default = simple
+
+# based on ghc advice,
+# https://ghc.haskell.org/trac/ghc/wiki/Building/GettingTheSources
+[diff]
+ignoreSubmodules = dirty
+tool = meld
+
+[gitreview]
+username = iank
+remote = origin
+# this is for newer git version. Doesn't work for ubuntu 12.04's version
+#[push]
+#      default = simple
+[color]
+ui = auto
+status = auto
+branch = auto
+interactive = auto
+diff = auto
+# for hooks. http://stackoverflow.com/questions/2293498/git-commit-hooks-global-settings
+[init]
+templatedir = ~/.git_template
+[difftool]
+tool = meld
+prompt = false
+[format]
+# This seemed like a good idea, but it broke interactive rebase, so disabled.
+# for format-patch, use --base=auto. This means we need to do
+# git branch --set-upstream-to, but that should be done automatically
+# based on other settings.
+#      useAutoBase = true
+[branch]
+# Disabled as this was to be used with useAutoBase, which didn't work as expected.
+# sets local branches to have the same remote tracking branch.
+# This allows the useAutoBase setting to work without having to
+# setup the branch specially with
+# git branch --set-upstream-to origin/my_branch
+# or git checkout -b my-branch origin/whatever
+#      autoSetupMerge = always
+
+# git pull always does rebase by default
+autoSetupRebase = always
+[push]
+default = simple
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d52d4c6
--- /dev/null
@@ -0,0 +1,3 @@
+/t.org
+/old-unused
+/.emacs.d
diff --git a/.gitignore_global b/.gitignore_global
new file mode 100644 (file)
index 0000000..5bc0376
--- /dev/null
@@ -0,0 +1,8 @@
+# file generated when emacs has a file open
+\#*\#
+# files generated from gnu global
+GPATH
+GRTAGS
+GTAGS
+# file generated when vi has a file open
+*.swp
diff --git a/.gvimrc b/.gvimrc
new file mode 100644 (file)
index 0000000..b676075
--- /dev/null
+++ b/.gvimrc
@@ -0,0 +1,50 @@
+set mousemodel=popup          " right mouse button pops up a menu in the GUI
+" set the X11 font to use
+"
+" this may need to be set for a newer host
+" set guifont=DejaVu\ Sans\ Mono\ 11
+if $OS ==# 'Windows_NT'
+       if has("xterm_clipboard")
+               set guifont=DejaVu\ Sans\ Mono\ 12
+       else
+               set guifont=DejaVu_Sans_Mono:h10:cANSI
+       endif
+       set winaltkeys=no
+endif
+
+if hostname() ==# 'gd'
+       set guifont=DejaVu\ Sans\ Mono\ 13
+endif
+if hostname() ==# 'jl'
+       set guifont=DejaVu\ Sans\ Mono\ 11
+endif
+
+set guicursor=a:blinkwait1000-blinkon10000-blinkoff100
+"here are the defaults:
+"set guicursor=n-v-c:block-Cursor/lCursor,ve:ver35-Cursor,o:hor50-Cursor,i-ci:ver25-Cursor/lCursor,r-cr:hor20-Cursor/lCursor,sm:block-Cursor-blinkwait175-blinkoff150-blinkon175
+
+"Crude way of maximizing when the console is small. optimized for my system.
+if &columns < 60 || &lines <20 || exists("$DMAXVIM")
+       set  columns=158
+       set  lines=51
+endif
+"remove menu and button stuff.
+set go-=m
+set go-=T
+" no right scrollbar
+set go-=r
+"use non gui tabline, stops maxmized kde bug.
+set go-=e
+"use console prompts
+set go+=c
+" no left scrollbar
+set go-=L
+
+" Use a sufficiently wide window automatically when started as "gvimdiff"
+if (&foldmethod == 'diff')
+       set columns=165
+endif
+
+" For bug 33327, add a mapping which allows shift-insert to function
+" as expected in insert and command-line modes
+map! <s-insert> <c-r>*
diff --git a/.hunspell_en_US b/.hunspell_en_US
new file mode 100644 (file)
index 0000000..d1aa599
--- /dev/null
@@ -0,0 +1,2 @@
+timestamp
+mediawiki
diff --git a/.hushlogin b/.hushlogin
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.inputrc b/.inputrc
new file mode 100644 (file)
index 0000000..76d3577
--- /dev/null
+++ b/.inputrc
@@ -0,0 +1,216 @@
+# case insensitive completion
+set completion-ignore-case on
+# - to _ insensitive completion
+set completion-map-case on
+
+# todo: explain this :)
+set menu-complete-display-prefix on
+
+# for readline-complete.el
+set bell-style none
+set completion-display-width 0
+
+# preserve cusror location in history items.
+# unfortunately, this does not work when using vi-mode and j or k
+# I would like to make a patch to fix this.
+set history-preserve-point on
+
+
+# show all completions all the time
+set show-all-if-ambiguous on
+# this does a partial complete if it can before showing completions
+set show-all-if-unmodified on
+
+
+# nice in bash, but messes up completion in emacs shell
+#set visible-stats on
+set visible-stats off
+
+# turn off pager for completion
+set page-completions off
+
+# to answer this question in bash-completion README:
+# Q. When completing on a symlink to a directory, bash does not append
+#   the trailing / and I have to hit  <Tab> again. I don't like this.
+set mark-symlinked-directories on
+
+# i had this set in the past to make c-u work, but it doesn't seem to be needed now.
+# not sure if it affected any other keybinds.
+#set bind-tty-special-chars off
+
+
+
+# key bindings:
+
+# note c-i/m/h are unusable, duplicating tab, enter, ctrl-del or something
+# C-x would be hard to rebind because emacs.
+
+"\C-q": exchange-point-and-mark
+"\C-w": kill-region
+"\C-e": yank-last-arg
+"\C-a": insert-comment
+"\C-s": yank-nth-arg
+
+"\C-f": menu-complete
+"\C-b": menu-complete-backward
+
+# arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash
+# this can be conditioned based on the term, see the term keyword in
+# bash's readline manual section
+#"\eOc": forward-word
+#"\eOd": backward-word
+"\e[1;5C": forward-word
+"\e[1;5D": backward-word
+
+Control-Space: set-mark
+
+# default is C-M-e which kinda sucks
+"\C-t": shell-expand-line
+
+# ctrl-delete
+"\e[3;5~": kill-word
+
+# default is C-x C-e
+"\C-g": edit-and-execute-command
+
+# default is C-y
+"\C-v": yank
+
+# default is C-M-y
+"\C-\M-v": yank-pop
+
+# by default, not bound
+"\C-p": dabbrev-expand # similar menu-complete, except for commands in history
+
+# default is C-q. this matches emacs.
+"\C-\M-q": quoted-insert
+
+# default is C-x C-u
+"\C-d": undo
+
+
+
+
+
+
+
+
+
+####### commented out stuff ###########
+
+# vi mode settings and observatoins
+# double pound indicates it is only commented because I am not using vi mode
+##set editing-mode vi
+
+# completion from history. how cool
+##Control-SPACE: dynamic-complete-history
+
+# this is just kinda cool to show off:
+##"\M-{": complete-into-braces
+
+
+# by default / and ? do a non-incremental search. This changes that.
+# the downside is that you have to type ctrl-v then / to get the actual char.
+##set keymap vi-command
+##"/":  reverse-search-history
+##"?":  forward-search-history
+# make g go to the end of history. emacs has some meta key for this,
+# but we are not using g, so why not.
+##"g": end-of-history
+# yank-pop from emacs mode, hacked into vim-mode.
+##"o": vi-yank-pop
+# vim uses m<letter> to set a mark and `<letter> to return to a mark specified
+# by letter. On a single line, this doesn't make much sense. So I'm using the
+# emacs way of a single mark. In vi-mode, you can do dm<letter> to delete a
+# mark. Emacs has its own delete and copy region commands. I'd like to hack the
+# source code to make d` delete to the mark and y` yank to the mark. I'm
+# leaving it for now, we will see how this works out in actual emacs when I get
+# my vi keybinds setup there.
+##"`": exchange-point-and-mark
+##"v": set-mark
+##"m": kill-region
+##"-": yank-last-arg
+##"_": yank-nth-arg
+
+
+
+# w and b are normally always tripping over punctuation as word separators, I
+# think having W and B act like that is a bit more efficient default. And this
+# "shell" functions see words as the shell does, which is cool.
+# These are also only included as bash commands.
+# this is enables in .bashrc, after testing if bash version is > 4
+# "b": shell-backward-word
+# "w": shell-forward-word
+# here we are with the unwrapped word, back and end functions, (that should get fixed).
+#"w": vi-fWord
+#"W": vi-fword
+#b: vi-bWord
+#B: vi-bword
+#"e": vi-eWord
+#"E": vi-eword
+#
+#unfortunately thre is another bug:  binding for example W to vi-fword makes it
+#show that its bound to that and also vi-fWord, and the upper case one is what
+# word.now
+#happens
+#
+# I added a hack to the readline sources to reverse the upper/lowercase check on these
+
+# I would like to make a patch to make the command 'shell-end-word'
+
+
+
+# for some reason, binding the '=' key doesn't work.
+
+# for some reason, the documentation of the same commands seems to be more
+# extensive in bash. There are extra commands listed in the bash documentation
+# too, but so far they seem to be shell specific.
+
+# "p": yank
+#set keymap vi-insert
+#
+#notes:
+#
+#normal mode has this, which doesn't make sense. 'i' still works fine. whatever.
+# vi-insertion-mode (not bound)
+#
+# normal mode default keybinds also have this:
+# vi-fword (not bound)
+# vi-fWord (not bound)
+#
+# vi-bword (not bound)
+# vi-bWord (not bound)
+#
+# Looking in the readline sources, those are the real forward and back
+# commands, but they are wrapped in this: and its corresponding back version.
+# "W": vi-next-word
+# "w": vi-next-word
+# What this wrapper does is check for upper/lower case, do terminal ding if its
+# at the eol, and handle negative counts prefixed to it.
+#
+# This command:
+# "_": vi-yank-arg
+# works like this emacs command:
+# yank-last-arg
+# except it puts you in insert mode afterwards, so you can't make successive
+# calls to go to earlier last arguments.
+#
+# Emacs has a kill ring. yank means paste. yank-pop means go down the kill
+# ring, this only has an effect after a yank, or a yank-pop that was after a
+# yank. Vim also has a kill ring plugin, that has a yank-pop, and a yank-push,
+# to browse forward in the kill ring. In emacs there is no forward function,
+# you simply undo the yank. In bash vi-mode, undoing the yank undoes it
+# entirely, not each item in the kill ring. This isn't a big deal. In both
+# emacs and vi-mode, the kill ring stays in the new position regardless of the
+# undo. Vi-mode does not allow you to yank-pop after the vi pastes 'p' and 'P',
+# 1 problem is the cursor is in a little different position than an emacs yank.
+# I think I can modify the readline sources fairly easily to make this
+# possible.
+# There is an interesting incompatibility between vi and emacs mode. Emacs mode
+# always has the block be 1 square after the cursor. Same in vi-insert, but in
+# vi-normal, its on top of the character and can't go beyond the last
+# character, so if you do a yank at the last character in vi-normal, it does
+# not put the cursor after the last character like a normal yank because it
+# cant, and then you can't yank-pop because the cursor is in the wrong spot.
+#
+#
diff --git a/.libao b/.libao
new file mode 100644 (file)
index 0000000..25ede45
--- /dev/null
+++ b/.libao
@@ -0,0 +1 @@
+default_driver=pulse
diff --git a/.mailcap b/.mailcap
new file mode 100644 (file)
index 0000000..e0e4a99
--- /dev/null
+++ b/.mailcap
@@ -0,0 +1,2 @@
+# google mailcap for more info.
+text/plain; g '%s'; edit=g '%s'; compose=g '%s'; needsterminal
diff --git a/.profile b/.profile
new file mode 100644 (file)
index 0000000..6596cb5
--- /dev/null
+++ b/.profile
@@ -0,0 +1,2 @@
+# see .bashrc_profile for commentary
+echo this is ~/.profile, either my .bashrc_profile is unavailable or this is posix mode or not bash
diff --git a/.redshift.conf b/.redshift.conf
new file mode 100644 (file)
index 0000000..cb1bf6b
--- /dev/null
@@ -0,0 +1,3 @@
+[redshift]
+temp-day=5900
+temp-night=5400
diff --git a/.screenrc b/.screenrc
new file mode 100644 (file)
index 0000000..7f584eb
--- /dev/null
+++ b/.screenrc
@@ -0,0 +1,43 @@
+# turn off visual bell
+vbell off
+
+# bufferfile /tmp/screen-exchange
+# replace ctrl-A by ctrl-O
+# escape ^Oo
+# set a big scrolling buffer
+defscrollback 20000
+# Set the caption on the bottom line
+# %-w all window names & numbers up to the current one. %+w all after the current.
+#
+#
+# modified from this
+#caption always "%{= kw}%-w%{= kw}%n %t%{-}%+w %-= %u@%H - %LD %d %LM - %C"
+caption always "%{= kw}%-w%{= kw}%n %t%{-}%+w %-= %h"
+# useless stuff taken away.
+# %u other users using this session or something
+# %H host, doesn't change after ssh
+#
+#
+
+# make vim go away when it exits
+altscreen on
+
+# syn with X paste buffer
+#bindkey -m > eval "stuff ' '" writebuf "exec sh -c 'xsel -n < /tmp/screen-exchange'"
+#screen -t shell1
+
+startup_message off
+
+# terminfo and termcap for nice 256 color terminal
+# allow bold colors - necessary for some reason
+attrcolor b ".I"
+# tell screen how to set colors. AB = background, AF=foreground
+termcapinfo xterm|xterm-256color 'Co#256:AB=\E[48;5;%dm:AF=\E[38;5;%dm'
+# erase background with current bg color. bce = background color erase
+defbce "on"
+
+# make shift-pgup/pgdn work
+termcapinfo xterm|xterm-256color ti@:te=\E[2J
+
+# can update the hardstatus (xterm title) with a command like this:
+# hardstatus string "SCREEN @ %H: %-n - %t"
diff --git a/.vim/.netrwhist b/.vim/.netrwhist
new file mode 100644 (file)
index 0000000..d68b384
--- /dev/null
@@ -0,0 +1,4 @@
+let g:netrw_dirhistmax  =10
+let g:netrw_dirhist_cnt =2
+let g:netrw_dirhist_1='/usr/share/X11/xkb/symbols'
+let g:netrw_dirhist_2='/a/.vim'
diff --git a/.vim/after/syntax/c.vim b/.vim/after/syntax/c.vim
new file mode 100644 (file)
index 0000000..5988183
--- /dev/null
@@ -0,0 +1,3 @@
+if exists("cpre")
+       syn region c_Preprocessor_Conditional_Fold fold transparent start="^\s*#\s*if\|^\s*#\s*else" end="^\s*#\s*else\>\&\|^\s*#\s*endif\>"
+endif
diff --git a/.vim/autoload/SyntaxAttr.vim b/.vim/autoload/SyntaxAttr.vim
new file mode 100644 (file)
index 0000000..81a8b0b
--- /dev/null
@@ -0,0 +1,65 @@
+" EXAMPLE SETUP
+"
+" Show the syntax group name of the item under cursor.
+"      map -a  :call SyntaxAttr()<CR>
+
+function! SyntaxAttr()
+     let synid = ""
+     let guifg = ""
+     let guibg = ""
+     let gui   = ""
+
+     let id1  = synID(line("."), col("."), 1)
+     let tid1 = synIDtrans(id1)
+
+     if synIDattr(id1, "name") != ""
+         let synid = "group: " . synIDattr(id1, "name")
+         if (tid1 != id1)
+              let synid = synid . '->' . synIDattr(tid1, "name")
+         endif
+         let id0 = synID(line("."), col("."), 0)
+         if (synIDattr(id1, "name") != synIDattr(id0, "name"))
+              let synid = synid .  " (" . synIDattr(id0, "name")
+              let tid0 = synIDtrans(id0)
+              if (tid0 != id0)
+                   let synid = synid . '->' . synIDattr(tid0, "name")
+              endif
+              let synid = synid . ")"
+         endif
+     endif
+
+     " Use the translated id for all the color & attribute lookups; the linked id yields blank values.
+     if (synIDattr(tid1, "fg") != "" )
+         let guifg = " guifg=" . synIDattr(tid1, "fg") . "(" . synIDattr(tid1, "fg#") . ")"
+     endif
+     if (synIDattr(tid1, "bg") != "" )
+         let guibg = " guibg=" . synIDattr(tid1, "bg") . "(" . synIDattr(tid1, "bg#") . ")"
+     endif
+     if (synIDattr(tid1, "bold"     ))
+         let gui   = gui . ",bold"
+     endif
+     if (synIDattr(tid1, "italic"   ))
+         let gui   = gui . ",italic"
+     endif
+     if (synIDattr(tid1, "reverse"  ))
+         let gui   = gui . ",reverse"
+     endif
+     if (synIDattr(tid1, "inverse"  ))
+         let gui   = gui . ",inverse"
+     endif
+     if (synIDattr(tid1, "underline"))
+         let gui   = gui . ",underline"
+     endif
+     if (gui != ""                  )
+         let gui   = substitute(gui, "^,", " gui=", "")
+     endif
+
+     echohl MoreMsg
+     let message = synid . guifg . guibg . gui
+     if message == ""
+         echohl WarningMsg
+         let message = "<no syntax group here>"
+     endif
+     echo message
+     echohl None
+endfunction
diff --git a/.vim/colors/ir_black.vim b/.vim/colors/ir_black.vim
new file mode 100644 (file)
index 0000000..6dc402d
--- /dev/null
@@ -0,0 +1,212 @@
+" ir_black color scheme
+" More at: http://blog.infinitered.com/entries/show/8
+
+
+" ********************************************************************************
+" Standard colors used in all ir_black themes:
+" Note, x:x:x are RGB values
+"
+"  normal: #f6f3e8
+"
+"  string: #A8FF60  168:255:96
+"    string inner (punc, code, etc): #00A0A0  0:160:160
+"  number: #FF73FD  255:115:253
+"  comments: #7C7C7C  124:124:124
+"  keywords: #96CBFE  150:203:254
+"  operators: white
+"  class: #FFFFB6  255:255:182
+"  method declaration name: #FFD2A7  255:210:167
+"  regular expression: #E9C062  233:192:98
+"    regexp alternate: #FF8000  255:128:0
+"    regexp alternate 2: #B18A3D  177:138:61
+"  variable: #C6C5FE  198:197:254
+"
+" Misc colors:
+"  red color (used for whatever): #FF6C60   255:108:96
+"     light red: #FFB6B0   255:182:176
+"
+"  brown: #E18964  good for special
+"
+"  lightpurpleish: #FFCCFF
+"
+" Interface colors:
+"  background color: black
+"  cursor (where underscore is used): #FFA560  255:165:96
+"  cursor (where block is used): white
+"  visual selection: #1D1E2C
+"  current line: #151515  21:21:21
+"  search selection: #07281C  7:40:28
+"  line number: #3D3D3D  61:61:61
+
+
+" ********************************************************************************
+" The following are the preferred 16 colors for your terminal
+"           Colors      Bright Colors
+" Black     #4E4E4E     #7C7C7C
+" Red       #FF6C60     #FFB6B0
+" Green     #A8FF60     #CEFFAB
+" Yellow    #FFFFB6     #FFFFCB
+" Blue      #96CBFE     #FFFFCB
+" Magenta   #FF73FD     #FF9CFE
+" Cyan      #C6C5FE     #DFDFFE
+" White     #EEEEEE     #FFFFFF
+
+
+" ********************************************************************************
+set background=dark
+hi clear
+
+if exists("syntax_on")
+  syntax reset
+endif
+
+let colors_name = "ir_black"
+
+
+"hi Example         guifg=NONE        guibg=NONE        gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE
+
+" General colors
+hi Normal           guifg=#f6f3e8     guibg=black       gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE
+hi NonText          guifg=#070707     guibg=black       gui=NONE      ctermfg=black       ctermbg=NONE        cterm=NONE
+
+hi Cursor           guifg=black       guibg=white       gui=NONE      ctermfg=black       ctermbg=white       cterm=reverse
+hi LineNr           guifg=#3D3D3D     guibg=black       gui=NONE      ctermfg=darkgray    ctermbg=NONE        cterm=NONE
+
+hi VertSplit        guifg=#202020     guibg=#202020     gui=NONE      ctermfg=darkgray    ctermbg=darkgray    cterm=NONE
+hi StatusLine       guifg=#CCCCCC     guibg=#202020     gui=italic    ctermfg=white       ctermbg=darkgray    cterm=NONE
+hi StatusLineNC     guifg=black       guibg=#202020     gui=NONE      ctermfg=blue        ctermbg=darkgray    cterm=NONE
+
+hi Folded           guifg=#a0a8b0     guibg=#384048     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE
+hi Title            guifg=#f6f3e8     guibg=NONE        gui=bold      ctermfg=NONE        ctermbg=NONE        cterm=NONE
+hi Visual           guifg=NONE        guibg=#262D51     gui=NONE      ctermfg=NONE        ctermbg=darkgray    cterm=NONE
+
+hi SpecialKey       guifg=#808080     guibg=#343434     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE
+
+hi WildMenu         guifg=green       guibg=yellow      gui=NONE      ctermfg=black       ctermbg=yellow      cterm=NONE
+hi PmenuSbar        guifg=black       guibg=white       gui=NONE      ctermfg=black       ctermbg=white       cterm=NONE
+"hi Ignore           guifg=gray        guibg=black       gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE
+
+hi Error            guifg=NONE        guibg=NONE        gui=undercurl ctermfg=white       ctermbg=red         cterm=NONE     guisp=#FF6C60 " undercurl color
+hi ErrorMsg         guifg=white       guibg=#FF6C60     gui=BOLD      ctermfg=white       ctermbg=red         cterm=NONE
+hi WarningMsg       guifg=white       guibg=#FF6C60     gui=BOLD      ctermfg=white       ctermbg=red         cterm=NONE
+
+" Message displayed in lower left, such as --INSERT--
+hi ModeMsg          guifg=black       guibg=#C6C5FE     gui=BOLD      ctermfg=black       ctermbg=cyan        cterm=BOLD
+
+if version >= 700 " Vim 7.x specific colors
+  hi CursorLine     guifg=NONE        guibg=#121212     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=BOLD
+  hi CursorColumn   guifg=NONE        guibg=#121212     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=BOLD
+  hi MatchParen     guifg=#f6f3e8     guibg=#857b6f     gui=BOLD      ctermfg=white       ctermbg=darkgray    cterm=NONE
+  hi Pmenu          guifg=#f6f3e8     guibg=#444444     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE
+  hi PmenuSel       guifg=#000000     guibg=#cae682     gui=NONE      ctermfg=NONE        ctermbg=NONE        cterm=NONE
+  hi Search         guifg=NONE        guibg=NONE        gui=underline ctermfg=NONE        ctermbg=NONE        cterm=underline
+endif
+
+" Syntax highlighting
+hi Comment          guifg=#7C7C7C     guibg=NONE        gui=NONE      ctermfg=darkgray    ctermbg=NONE        cterm=NONE
+hi String           guifg=#A8FF60     guibg=NONE        gui=NONE      ctermfg=green       ctermbg=NONE        cterm=NONE
+hi Number           guifg=#FF73FD     guibg=NONE        gui=NONE      ctermfg=magenta     ctermbg=NONE        cterm=NONE
+
+hi Keyword          guifg=#96CBFE     guibg=NONE        gui=NONE      ctermfg=blue        ctermbg=NONE        cterm=NONE
+hi PreProc          guifg=#96CBFE     guibg=NONE        gui=NONE      ctermfg=blue        ctermbg=NONE        cterm=NONE
+hi Conditional      guifg=#6699CC     guibg=NONE        gui=NONE      ctermfg=blue        ctermbg=NONE        cterm=NONE  " if else end
+
+hi Todo             guifg=#8f8f8f     guibg=NONE        gui=NONE      ctermfg=red         ctermbg=NONE        cterm=NONE
+hi Constant         guifg=#99CC99     guibg=NONE        gui=NONE      ctermfg=cyan        ctermbg=NONE        cterm=NONE
+
+hi Identifier       guifg=#C6C5FE     guibg=NONE        gui=NONE      ctermfg=cyan        ctermbg=NONE        cterm=NONE
+hi Function         guifg=#FFD2A7     guibg=NONE        gui=NONE      ctermfg=brown       ctermbg=NONE        cterm=NONE
+hi Type             guifg=#FFFFB6     guibg=NONE        gui=NONE      ctermfg=yellow      ctermbg=NONE        cterm=NONE
+hi Statement        guifg=#6699CC     guibg=NONE        gui=NONE      ctermfg=lightblue   ctermbg=NONE        cterm=NONE
+
+hi Special          guifg=#E18964     guibg=NONE        gui=NONE      ctermfg=white       ctermbg=NONE        cterm=NONE
+hi Delimiter        guifg=#00A0A0     guibg=NONE        gui=NONE      ctermfg=cyan        ctermbg=NONE        cterm=NONE
+hi Operator         guifg=white       guibg=NONE        gui=NONE      ctermfg=white       ctermbg=NONE        cterm=NONE
+
+hi link Character       Constant
+hi link Boolean         Constant
+hi link Float           Number
+hi link Repeat          Statement
+hi link Label           Statement
+hi link Exception       Statement
+hi link Include         PreProc
+hi link Define          PreProc
+hi link Macro           PreProc
+hi link PreCondit       PreProc
+hi link StorageClass    Type
+hi link Structure       Type
+hi link Typedef         Type
+hi link Tag             Special
+hi link SpecialChar     Special
+hi link SpecialComment  Special
+hi link Debug           Special
+
+
+" Special for Ruby
+hi rubyRegexp                  guifg=#B18A3D      guibg=NONE      gui=NONE      ctermfg=brown          ctermbg=NONE      cterm=NONE
+hi rubyRegexpDelimiter         guifg=#FF8000      guibg=NONE      gui=NONE      ctermfg=brown          ctermbg=NONE      cterm=NONE
+hi rubyEscape                  guifg=white        guibg=NONE      gui=NONE      ctermfg=cyan           ctermbg=NONE      cterm=NONE
+hi rubyInterpolationDelimiter  guifg=#00A0A0      guibg=NONE      gui=NONE      ctermfg=blue           ctermbg=NONE      cterm=NONE
+hi rubyControl                 guifg=#6699CC      guibg=NONE      gui=NONE      ctermfg=blue           ctermbg=NONE      cterm=NONE  "and break, etc
+"hi rubyGlobalVariable          guifg=#FFCCFF      guibg=NONE      gui=NONE      ctermfg=lightblue      ctermbg=NONE      cterm=NONE  "yield
+hi rubyStringDelimiter         guifg=#336633      guibg=NONE      gui=NONE      ctermfg=lightgreen     ctermbg=NONE      cterm=NONE
+"rubyInclude
+"rubySharpBang
+"rubyAccess
+"rubyPredefinedVariable
+"rubyBoolean
+"rubyClassVariable
+"rubyBeginEnd
+"rubyRepeatModifier
+"hi link rubyArrayDelimiter    Special  " [ , , ]
+"rubyCurlyBlock  { , , }
+
+hi link rubyClass             Keyword
+hi link rubyModule            Keyword
+hi link rubyKeyword           Keyword
+hi link rubyOperator          Operator
+hi link rubyIdentifier        Identifier
+hi link rubyInstanceVariable  Identifier
+hi link rubyGlobalVariable    Identifier
+hi link rubyClassVariable     Identifier
+hi link rubyConstant          Type
+
+
+" Special for Java
+" hi link javaClassDecl    Type
+hi link javaScopeDecl         Identifier
+hi link javaCommentTitle      javaDocSeeTag
+hi link javaDocTags           javaDocSeeTag
+hi link javaDocParam          javaDocSeeTag
+hi link javaDocSeeTagParam    javaDocSeeTag
+
+hi javaDocSeeTag              guifg=#CCCCCC     guibg=NONE        gui=NONE      ctermfg=darkgray    ctermbg=NONE        cterm=NONE
+hi javaDocSeeTag              guifg=#CCCCCC     guibg=NONE        gui=NONE      ctermfg=darkgray    ctermbg=NONE        cterm=NONE
+"hi javaClassDecl              guifg=#CCFFCC     guibg=NONE        gui=NONE      ctermfg=white       ctermbg=NONE        cterm=NONE
+
+
+" Special for XML
+hi link xmlTag          Keyword
+hi link xmlTagName      Conditional
+hi link xmlEndTag       Identifier
+
+
+" Special for HTML
+hi link htmlTag         Keyword
+hi link htmlTagName     Conditional
+hi link htmlEndTag      Identifier
+
+
+" Special for Javascript
+hi link javaScriptNumber      Number
+
+
+" Special for Python
+"hi  link pythonEscape         Keyword
+
+
+" Special for CSharp
+hi  link csXmlTag             Keyword
+
+
+" Special for PHP
diff --git a/.vim/colors/leo.vim b/.vim/colors/leo.vim
new file mode 100644 (file)
index 0000000..7d47ce1
--- /dev/null
@@ -0,0 +1,115 @@
+" Vim color file
+" Maintainer:       Lorenzo Leonini <vim-theme[a]leonini[.]net>
+" Last Change:  2008 Aug 13
+" URL:                  http://www.leonini.net
+
+" Description:
+" A colored, contrasted theme for long programming sessions.
+" For 256-colors term (xterm, Eterm, konsole, gnome-terminal, ...)
+" Very good with Ruby, C, Lua, PHP, ... (but no language specific settings)
+
+" Note:
+" If your term report 8 colors (but is 256 capable), put 'set t_Co=256'
+" in your .vimrc
+
+" Tips:
+" :verbose hi StatusLine
+" Color numbers (0-255) see:
+"       http://www.calmar.ws/vim/256-xterm-24bit-rgb-color-chart.html
+
+if !has("gui_running")
+    if &t_Co != 256
+        echomsg "err: Please use a 256-colors terminal (so that t_Co=256 could be set)."
+        echomsg ""
+        finish
+    end
+endif
+
+let g:colors_name = "leo256"
+
+set background=dark
+if v:version > 580
+    highlight clear
+    if exists("syntax_on")
+        syntax reset
+    endif
+endif
+
+hi Normal               cterm=none      ctermfg=255     ctermbg=16 guibg=#000000 guifg=#ffffff
+hi CursorLine       cterm=none      ctermbg=233     guibg=#121212
+hi DiffAdd          cterm=none      ctermbg=235
+hi DiffChange       cterm=none      ctermbg=235
+hi DiffDelete       cterm=none      ctermfg=238     ctermbg=244
+hi DiffText         cterm=bold      ctermfg=255     ctermbg=196
+hi Directory        cterm=none      ctermfg=196
+hi ErrorMsg         cterm=none      ctermfg=255     ctermbg=160
+hi FoldColumn       cterm=none      ctermfg=110     ctermbg=16  guibg=#000000
+hi SignColumn       cterm=none      ctermfg=none        ctermbg=16  guibg=#000000
+hi Folded               cterm=none      ctermfg=16      ctermbg=110 guifg=#000000   guibg=#87afd7
+hi IncSearch        cterm=reverse
+hi LineNr               cterm=none      ctermfg=124     guifg=#af0000
+hi ModeMsg          cterm=bold
+hi MoreMsg          cterm=none      ctermfg=40
+hi NonText          cterm=none      ctermfg=27
+hi Question         cterm=none      ctermfg=40
+hi Search               cterm=none      ctermfg=16  ctermbg=248 guifg=#000000   guibg=#a8a8a8
+hi SpecialKey       cterm=none      ctermfg=124 guifg=#af0000
+" grey style
+"hi StatusLine      cterm=none      ctermfg=16          ctermbg=252
+"hi StatusLineNC    cterm=none      ctermfg=246     ctermbg=235
+" blue style
+hi StatusLine       cterm=none      ctermfg=255         ctermbg=21 guibg=#FFFFFF guifg=#0000FF
+hi StatusLineNC cterm=none      ctermfg=252     ctermbg=17 guibg=#dddddd guifg=#0000aa
+hi Title                cterm=none      ctermfg=33
+hi VertSplit        cterm=none      ctermfg=254     ctermbg=16 guibg=#EEEEEE guifg=#000000
+hi Visual               cterm=reverse                               ctermbg=none
+hi VisualNOS        cterm=underline,bold
+hi WarningMsg       cterm=none      ctermfg=255
+hi WildMenu         cterm=none      ctermfg=16          ctermbg=11
+
+if v:version >= 700
+    " light
+    "hi Pmenu               cterm=none  ctermfg=16      ctermbg=252
+    "hi PmenuSel            cterm=none  ctermfg=255     ctermbg=21
+    "hi PmenuSbar       cterm=none  ctermfg=240     ctermbg=240
+  "hi PmenuThumb        cterm=none  ctermfg=255     ctermbg=255
+
+    "dark
+    hi Pmenu                cterm=none  ctermfg=255     ctermbg=235     guibg=#222222       guifg=#eeeeee
+    hi PmenuSel         cterm=none  ctermfg=255     ctermbg=21          guibg=#3333ff
+    hi PmenuSbar        cterm=none  ctermfg=240     ctermbg=240     guibg=#444444
+  hi PmenuThumb     cterm=none  ctermfg=255     ctermbg=255
+
+    hi SpellBad         cterm=none    ctermfg=16        ctermbg=196
+    hi SpellCap         cterm=none    ctermfg=16        ctermbg=196
+    hi SpellLocal           cterm=none    ctermfg=16        ctermbg=196
+    hi SpellRare            cterm=none    ctermfg=16        ctermbg=196
+    hi TabLine          cterm=none  ctermfg=252     ctermbg=17
+    hi TabLineSel       cterm=none  ctermfg=255     ctermbg=21
+    hi TabLineFill  cterm=none  ctermfg=17      ctermbg=17
+endif
+
+hi Boolean          cterm=none      ctermfg=135     guifg=#af5fff
+hi Character        cterm=none      ctermfg=184
+hi Comment          cterm=none      ctermfg=247     guifg=#A5A5A5
+hi Constant         cterm=none      ctermfg=226 guifg=#ffff00
+hi Conditional  cterm=none      ctermfg=154     guifg=#afff00
+hi Define               cterm=bold      ctermfg=27      guifg=#005fff
+hi Delimiter        cterm=none      ctermfg=196     guifg=#af0000
+hi Exception        cterm=bold      ctermfg=226     guifg=#ffff00
+hi Error                cterm=none      ctermfg=255     ctermbg=9
+hi Keyword          cterm=none      ctermfg=159     guifg=#d7af00
+hi Function         cterm=none      ctermfg=red     guifg=#ff0000
+hi Identifier       cterm=none      ctermfg=27      guifg=#005fff
+hi Number               cterm=none      ctermfg=135     guifg=#af5fff
+hi Operator         cterm=none      ctermfg=11
+hi PreProc          cterm=none      ctermfg=202     guifg=#ff5f00
+hi Special          cterm=none      ctermfg=206     ctermbg=234     guifg=#ff5fd7
+hi Statement        cterm=none      ctermfg=2           guifg=#00cd00
+hi String               cterm=none      ctermfg=224     ctermbg=234     guifg=#ffd7d7   guibg=#1c1c1c
+hi Todo         cterm=none      ctermfg=0           ctermbg=11      guifg=#000000 guibg=#ffff00
+hi Type                 cterm=none      ctermfg=75      guifg=#5fafff
+
+" ADDITIONNAL
+
+hi Repeat       cterm=none      ctermfg=142     guifg=#878700
diff --git a/.vim/colors/metacosm.vim b/.vim/colors/metacosm.vim
new file mode 100644 (file)
index 0000000..c3f8848
Binary files /dev/null and b/.vim/colors/metacosm.vim differ
diff --git a/.vim/colors/nightsky.vim b/.vim/colors/nightsky.vim
new file mode 100644 (file)
index 0000000..4d0e247
--- /dev/null
@@ -0,0 +1,147 @@
+" Vim color file based on nightsky
+" Maintainer:   Ian Kelling
+" Last Change:
+
+
+" cool help screens
+" :he group-name
+" :he highlight-groups
+" :he cterm-colors
+
+set background=dark
+hi clear
+if exists("syntax_on")
+    syntax reset
+endif
+let g:colors_name="northsky"
+
+
+" Search & normal
+hi Normal              guifg=#eeeeee guibg=black
+hi Search              guibg=#3D5B8C guifg=yellow gui=bold
+hi IncSearch   guifg=bg guibg=cyan gui=bold
+if &t_Co == "256"
+       hi Normal               ctermfg=255 ctermbg=16
+       hi Search               ctermfg=226 ctermbg=60
+       hi IncSearch    ctermfg=16 ctermbg=51
+else
+       hi Normal               ctermfg=lightgray ctermbg=black
+       hi Search               ctermbg=darkblue ctermfg=yellow
+       hi IncSearch    ctermfg=bg ctermbg=cyan
+endif
+
+" highlight groups
+hi Cursor      guifg=bg guibg=fg
+hi VertSplit   guibg=#C0FFFF guifg=#075554 gui=none
+hi Folded      guifg=plum1 guibg=#061A3E
+hi FoldColumn  guibg=#800080 guifg=tan
+hi ModeMsg     guifg=#404040 guibg=#C0C0C0
+hi MoreMsg     guifg=darkturquoise guibg=#188F90
+hi NonText     guibg=#191919 guifg=#9FADC5
+hi Question    guifg=#F4BB7E
+hi SpecialKey  guifg=#BF9261
+hi StatusLine  guibg=#222222 guifg=#b0b0b0 gui=none
+hi StatusLineNC        guibg=#001111 guifg=DimGrey gui=none
+hi Title       guifg=#8DB8C3
+hi Visual gui=bold guifg=black guibg=#84AF84
+hi WarningMsg  guifg=#F60000 gui=underline
+if &t_Co == "256"
+       hi Cursor               ctermfg=254 ctermbg=166
+       hi VertSplit    ctermfg=23 ctermbg=159
+       hi Folded               ctermfg=219 ctermbg=17
+       hi FoldColumn   ctermfg=180 ctermbg=90
+       hi ModeMsg              ctermfg=238 ctermbg=250
+       hi MoreMsg              ctermfg=44 ctermbg=30
+       hi NonText              ctermfg=146 ctermbg=234
+       hi Question             ctermfg=216
+       hi SpecialKey   ctermfg=137
+       hi StatusLine   ctermfg=145 ctermbg=235
+       hi StatusLineNC ctermfg=242 ctermbg=16
+       hi Title                ctermfg=109
+       hi Visual               ctermfg=16 ctermbg=108
+       hi WarningMsg   ctermfg=196
+else
+       hi Cursor               ctermfg=white ctermbg=red
+       hi VertSplit    ctermfg=darkblue ctermbg=cyan
+       hi Folded               ctermfg=lightmagenta ctermbg=bg
+       hi FoldColumn   ctermfg=lightgray ctermbg=darkmagenta
+       hi ModeMsg              ctermfg=black ctermbg=gray
+       hi MoreMsg              ctermfg=cyan ctermbg=darkcyan
+       hi NonText              ctermfg=gray ctermbg=darkgrey
+       hi Question             ctermfg=yellow
+       hi SpecialKey   ctermfg=brown
+       hi StatusLine   ctermfg=lightgray ctermbg=darkgray
+       hi StatusLineNC ctermfg=darkgray ctermbg=black
+       hi Title                ctermfg=blue
+       hi Visual               ctermfg=black ctermbg=darkgreen
+       hi WarningMsg   ctermfg=red
+endif
+
+" syntax highlighting groups
+hi Comment             guifg=DarkGray
+hi Constant            guifg=#72A5E4   gui=bold
+hi Number              guifg=chartreuse2 gui=bold
+hi Identifier  guifg=#ADCBF1
+hi Statement   guifg=yellow
+hi PreProc             guifg=#14967C
+hi Type                        guifg=#FFAE66
+hi Special             guifg=#EEBABA
+hi Ignore              guifg=grey60
+hi Todo                        guibg=#9C8C84 guifg=#244C0A
+hi Label               guifg=#ffc0c0
+if &t_Co == "256"
+       hi Comment              ctermfg=248
+       hi Constant             ctermfg=74
+       hi Number               ctermfg=118
+       hi Identifier   ctermfg=153
+       hi Statement    ctermfg=226
+       hi PreProc              ctermfg=30
+       hi Type                 ctermfg=215
+       hi Special              ctermfg=217
+       hi Ignore               ctermfg=246
+       hi Todo                 ctermfg=22 ctermbg=138
+       hi Label                ctermfg=217
+else
+       hi Comment              ctermfg=darkgray
+       hi Constant             ctermfg=lightcyan
+       hi Number               ctermfg=green
+       hi Identifier   ctermfg=gray
+       hi Statement    ctermfg=yellow
+       hi PreProc              ctermfg=darkgreen
+       hi Type                 ctermfg=white "brown
+       hi Special              ctermfg=brown "darkmagenta
+       hi Ignore               ctermfg=gray
+       hi Todo                 ctermfg=darkblue ctermbg=darkgray
+       hi Label                ctermfg=darkmagenta
+endif
+
+" Vim defaults
+hi ErrorMsg            guifg=White guibg=Red
+hi DiffAdd             guibg=DarkBlue
+hi DiffChange  guibg=aquamarine4
+hi DiffDelete  gui=bold guifg=Yellow guibg=DarkBlue
+hi DiffText            gui=bold guibg=#940303
+hi Directory   guifg=Cyan
+hi LineNr              guifg=DarkGreen
+hi WildMenu            guifg=Black guibg=Yellow
+hi lCursor             guibg=SeaGreen1 guifg=NONE
+hi Underlined  gui=underline guifg=#80a0ff
+hi Error               guifg=White guibg=Red
+if &t_Co == "256"
+       hi ErrorMsg             ctermfg=231 ctermbg=196
+       hi DiffAdd              ctermbg=18
+       hi DiffChange   ctermbg=66
+       hi DiffDelete   ctermfg=226 ctermbg=18
+       hi DiffText             ctermbg=88
+       hi Directory    ctermfg=51
+       hi LineNr               ctermfg=22
+       hi WildMenu             ctermfg=16 ctermbg=226
+       hi lCursor              ctermbg=85
+       hi Underlined   ctermfg=111
+       hi Error                ctermfg=231 ctermbg=196
+else
+       hi lCursor              ctermbg=lightgreen ctermfg=NONE
+endif
+
+
+" vim:sw=4 ts=4 sts=4
diff --git a/.vim/colors/refactor.vim b/.vim/colors/refactor.vim
new file mode 100644 (file)
index 0000000..c01b060
--- /dev/null
@@ -0,0 +1,84 @@
+set background=dark
+hi clear
+if exists("syntax_on")
+  syntax reset
+endif
+let g:colors_name = "RefractorMyCode"
+
+hi Comment guifg=#80a0ff
+hi Constant guifg=#527023
+hi Cursor gui=bold guifg=black guibg=green
+hi CursorColumn guibg=gray40
+hi CursorIM gui=None
+hi CursorLine guibg=gray40
+hi DiffAdd guibg=darkblue
+hi DiffChange guibg=darkmagenta
+hi DiffDelete gui=bold guifg=blue guibg=darkcyan
+hi DiffText gui=bold guibg=red
+hi Directory guifg=cyan
+hi Error guifg=white guibg=red
+hi ErrorMsg guifg=white guibg=red
+hi FoldColumn guifg=#3387CC guibg=grey
+hi Folded guifg=#0a1926 guibg=darkgray
+hi Identifier guifg=#40ffff
+hi Ignore guifg=black
+hi IncSearch gui=reverse
+hi LineNr guifg=gray40
+hi MatchParen guibg=darkcyan
+hi ModeMsg gui=bold
+hi MoreMsg gui=bold guifg=seagreen
+hi NonText gui=bold guifg=#cc0099
+hi Normal guifg=gray80 guibg=black
+hi Pmenu guibg=magenta
+hi PmenuSbar guibg=grey
+hi PmenuSel guibg=darkgray
+hi PmenuThumb gui=reverse
+hi PreProc guifg=#AFC4DB
+hi Question gui=bold guifg=green
+hi Search gui=bold guifg=black guibg=red
+hi SignColumn guifg=cyan guibg=grey
+hi Special guifg=orange
+hi SpecialKey guifg=cyan
+hi SpellBad gui=undercurl
+hi SpellCap gui=undercurl
+hi SpellLocal gui=undercurl
+hi SpellRare gui=undercurl
+hi Statement guifg=#E28964
+hi StatusLine gui=bold,reverse guifg=#3387CC guibg=white
+hi StatusLineNC gui=reverse
+hi TabLine guifg=black guibg=darkgray
+hi TabLineFill gui=reverse
+hi TabLineSel gui=bold
+hi Title gui=bold guifg=magenta
+hi Todo gui=italic guifg=black guibg=yellow
+hi Type guifg=#99CF50
+hi Underlined gui=underline guifg=#80a0ff
+hi VertSplit gui=reverse
+hi Visual gui=bold guifg=gray25 guibg=darkgray
+hi VisualNOS gui=bold,underline
+hi WarningMsg guifg=red
+hi WildMenu guifg=black guibg=yellow
+hi link Boolean Constant
+hi link Character Constant
+hi link Conditional Statement
+hi link Debug Special
+hi link Define PreProc
+hi link Delimiter Special
+hi link Exception Statement
+hi link Float Constant
+hi link Function Identifier
+hi link Include PreProc
+hi link Keyword Statement
+hi link Label Statement
+hi link Macro PreProc
+hi link Number Constant
+hi link Operator Statement
+hi link PreCondit PreProc
+hi link Repeat Statement
+hi link SpecialChar Special
+hi link SpecialComment Special
+hi link StorageClass Type
+hi link String Constant
+hi link Structure Type
+hi link Tag Special
+hi link Typedef Type
diff --git a/.vim/colors/vividchalk.vim b/.vim/colors/vividchalk.vim
new file mode 100644 (file)
index 0000000..d628807
Binary files /dev/null and b/.vim/colors/vividchalk.vim differ
diff --git a/.vim/doc/bufexplorer.txt b/.vim/doc/bufexplorer.txt
new file mode 100644 (file)
index 0000000..1a909a4
--- /dev/null
@@ -0,0 +1,407 @@
+*bufexplorer.txt*              Buffer Explorer       Last Change: 21 Dec 2007
+
+Buffer Explorer                                *buffer-explorer* *bufexplorer*
+                                Version 7.1.7
+
+Plugin for easily exploring (or browsing) Vim |:buffers|.
+
+|bufexplorer-usage|          Usage
+|bufexplorer-installation|   Installation
+|bufexplorer-customization|  Customization
+|bufexplorer-changelog|      Change Log
+|bufexplorer-todo|           Todo
+|bufexplorer-credits|        Credits
+
+For Vim version 7.0 and above.
+This plugin is only available if 'compatible' is not set.
+
+{Vi does not have any of this}
+
+==============================================================================
+INSTALLATION                                        *bufexplorer-installation*
+
+To install:
+  - Download the bufexplorer.zip.
+  - Extract the zip archive into your runtime directory.
+    The archive contains plugin/bufexplorer.vim, and doc/bufexplorer.txt.
+  - Start Vim or goto an existing instance of Vim.
+  - Execute the following command:
+>
+      :helptag <your runtime directory/doc
+<
+    This will generate all the help tags for any file located in the doc
+    directory.
+
+==============================================================================
+USAGE                                                      *bufexplorer-usage*
+
+To start exploring in the current window, use: >
+
+ \be   OR  :BufExplorer
+
+If you would like to use something other than '\', you may simply change the
+leader (see |mapleader|).
+
+Note: If the current buffer is modified when bufexplorer started, the current
+      window is always split and the new bufexplorer is displayed in that new
+      window.
+
+Commands to use once exploring:
+
+ <enter>       Opens the buffer that is under the cursor into the current
+               window.
+ <F1>          Toggle help information.
+ <leftmouse>   Opens the buffer that is under the cursor into the current
+               window.
+ <shift-enter> Opens the buffer that is under the cursor in another tab.
+ d             |:wipeout| the buffer under the cursor from the list.
+               When a buffers is wiped, it will not be shown when unlisted
+               buffer are displayed.
+ D             |:delete| the buffer under the cursor from the list.
+               The buffer's 'buflisted' is cleared. This allows for the buffer
+               to be displayed again using the 'show unlisted' command.
+ f             Toggles whether you are taken to the active window when
+               selecting a buffer or not.
+ p             Toggles the showing of a split filename/pathname.
+ q             Quit exploring.
+ r             Reverses the order the buffers are listed in.
+ R             Toggles relative path/absolute path.
+ s             Selects the order the buffers are listed in. Either by buffer
+               number, file name, file extension, most recently used (MRU), or
+               full path.
+ t             Opens the buffer that is under the cursor in another tab.
+ u             Toggles the showing of "unlisted" buffers.
+
+Once invoked, Buffer Explorer displays a sorted list (MRU is the default
+sort method) of all the buffers that are currently opened. You are then
+able to move the cursor to the line containing the buffer's name you are
+wanting to act upon. Once you have selected the buffer you would like,
+you can then either open it, close it(delete), resort the list, reverse
+the sort, quit exploring and so on...
+
+===============================================================================
+CUSTOMIZATION                                       *bufexplorer-customization*
+
+                                                     *g:bufExplorerDefaultHelp*
+To control whether the default help is displayed or not, use: >
+  let g:bufExplorerDefaultHelp=0       " Do not show default help.
+  let g:bufExplorerDefaultHelp=1       " Show default help.
+The default is to show the default help.
+
+                                                    *g:bufExplorerDetailedHelp*
+To control whether detailed help is display by, use: >
+  let g:bufExplorerDetailedHelp=0      " Do not show detailed help.
+  let g:bufExplorerDetailedHelp=1      " Show detailed help.
+The default is NOT to show detailed help.
+
+                                                      *g:bufExplorerFindActive*
+To control whether you are taken to the active window when selecting a buffer,
+use: >
+  let g:bufExplorerFindActive=0        " Do not go to active window.
+  let g:bufExplorerFindActive=1        " Go to active window.
+The default is to be taken to the active window.
+
+                                                     *g:bufExplorerReverseSort*
+To control whether to sort the buffer in reverse order or not, use: >
+  let g:bufExplorerReverseSort=0       " Do not sort in reverse order.
+  let g:bufExplorerReverseSort=1       " Sort in reverse order.
+The default is NOT to sort in reverse order.
+
+                                                 *g:bufExplorerShowDirectories*
+Directories usually show up in the list from using a command like ":e .".
+To control whether to show directories in the buffer list or not, use: >
+  let g:bufExplorerShowDirectories=1   " Show directories.
+  let g:bufExplorerShowDirectories=0   " Don't show directories.
+The default is to show directories.
+
+                                                *g:bufExplorerShowRelativePath*
+To control whether to show absolute paths or relative to the current
+directory, use: >
+  let g:bufExplorerShowRelativePath=0  " Show absolute paths.
+  let g:bufExplorerShowRelativePath=1  " Show relative paths.
+The default is to show absolute paths.
+
+                                                    *g:bufExplorerShowUnlisted*
+To control whether to show unlisted buffer or not, use: >
+  let g:bufExplorerShowUnlisted=0      " Do not show unlisted buffers.
+  let g:bufExplorerShowUnlisted=1      " Show unlisted buffers.
+The default is to NOT show unlisted buffers.
+
+                                                          *g:bufExplorerSortBy*
+To control what field the buffers are sorted by, use: >
+  let g:bufExplorerSortBy='extension'  " Sort by file extension.
+  let g:bufExplorerSortBy='fullpath'   " Sort by full file path name.
+  let g:bufExplorerSortBy='mru'        " Sort by most recently used.
+  let g:bufExplorerSortBy='name'       " Sort by the buffer's name.
+  let g:bufExplorerSortBy='number'     " Sort by the buffer's number.
+The default is to sort by mru.
+
+                                                *g:bufExplorerSplitOutPathName*
+To control whether to split out the path and file name or not, use: >
+  let g:bufExplorerSplitOutPathName=1  " Split the path and file name.
+  let g:bufExplorerSplitOutPathName=0  " Don't split the path and file
+                                       " name.
+The default is to split the path and file name.
+
+===============================================================================
+CHANGE LOG                                              *bufexplorer-changelog*
+
+7.1.7  - Fixes:
+         * TaCahiroy fixed several issues related to opening a buffer in a
+           tab.
+7.1.6  - Fixes:
+         * Removed ff=unix from modeline in bufexplorer.txt. Found by Bill
+           McCarthy.
+7.1.5  - Fixes:
+         * Could not open unnamed buffers. Fixed by TaCahiroy.
+7.1.4  - Fixes:
+         * Sometimes when a file's path has 'white space' in it, extra buffers
+           would be created containing each piece of the path. i.e:
+           opening c:\document and settings\test.txt would create a buffer
+           named "and" and a buffer named "Documents". This was reported and
+           fixed by TaCa Yoss.
+7.1.3  - Fixes:
+         * Added code to allow only one instance of the plugin to run at a
+           time. Thanks Dennis Hostetler.
+7.1.2  - Fixes:
+         * Fixed a jumplist issue spotted by JiangJun. I overlooked the
+           'jumplist' and with a couple calls to 'keepjumps', everything is
+           fine again.
+         * Went back to just having a plugin file, no autoload file. By having
+           the autoload, WinManager was no longer working and without really
+           digging into the cause, it was easier to go back to using just a
+           plugin file.
+7.1.1  - Fixes:
+         * A problem spotted by Thomas Arendsen Hein.
+           When running Vim (7.1.94), error E493 was being thrown.
+         Enhancements:
+         * Added 'D' for 'delete' buffer as the 'd' command was a 'wipe'
+           buffer.
+7.1.0  - Another 'major' update, some by Dave Larson, some by me.
+         * Making use of 'autoload' now to make the plugin load quicker.
+         * Removed '\bs' and '\bv'. These are now controlled by the user. The
+           user can issue a ':sp' or ':vs' to create a horizontal or vertical
+           split window and then issue a '\be'
+         * Added handling of tabs.
+7.0.17 - Fixed issue with 'drop' command.
+         Various enhancements and improvements.
+7.0.16 - Fixed issue reported by Liu Jiaping on non Windows systems, which was
+         ...
+         Open file1, open file2, modify file1, open bufexplorer, you get the
+         following error:
+
+         --------8<--------
+         Error detected while processing function
+         <SNR>14_StartBufExplorer..<SNR>14_SplitOpen:
+         line    4:
+         E37: No write since last change (add ! to override)
+
+         But the worse thing is, when I want to save the current buffer and
+         type ':w', I get another error message:
+         E382: Cannot write, 'buftype' option is set
+         --------8<--------
+
+7.0.15 - Thanks to Mark Smithfield for suggesting bufexplorer needed to handle
+         the ':args' command.
+7.0.14 - Thanks to Randall Hansen for removing the requirement of terminal
+         versions to be recompiled with 'gui' support so the 'drop' command
+         would work. The 'drop' command is really not needed in terminal
+         versions.
+7.0.13 - Fixed integration with WinManager.
+         Thanks to Dave Eggum for another update.
+           - Fix: The detailed help didn't display the mapping for toggling
+                  the split type, even though the split type is displayed.
+           - Fixed incorrect description in the detailed help for toggling
+             relative or full paths.
+           - Deprecated s:ExtractBufferNbr(). Vim's str2nr() does the same
+             thing.
+           - Created a s:Set() function that sets a variable only if it hasn't
+             already been defined. It's useful for initializing all those
+             default settings.
+           - Removed checks for repetitive command definitions. They were
+             unnecessary.
+           - Made the help highlighting a little more fancy.
+           - Minor reverse compatibility issue: Changed ambiguous setting
+             names to be more descriptive of what they do (also makes the code
+             easier to follow):
+               Changed bufExplorerSortDirection to bufExplorerReverseSort
+               Changed bufExplorerSplitType to bufExplorerSplitVertical
+               Changed bufExplorerOpenMode to bufExplorerUseCurrentWindow
+           - When the BufExplorer window closes, all the file-local marks are
+             now deleted. This may have the benefit of cleaning up some of the
+             jumplist.
+           - Changed the name of the parameter for StartBufExplorer from
+             "split" to "open". The parameter is a string which specifies how
+             the buffer will be open, not if it is split or not.
+           - Deprecated DoAnyMoreBuffersExist() - it is a one line function
+             only used in one spot.
+           - Created four functions (SplitOpen(), RebuildBufferList(),
+             UpdateHelpStatus() and ReSortListing()) all with one purpose - to
+             reduce repeated code.
+           - Changed the name of AddHeader() to CreateHelp() to be more
+             descriptive of what it does. It now returns an array instead of
+             updating the window directly. This has the benefit of making the
+             code more efficient since the text the function returns is used a
+             little differently in the two places the function is called.
+         - Other minor simplifications.
+7.0.12 - MAJOR Update.
+         This version will ONLY run with Vim version 7.0 or greater.
+         Dave Eggum has made some 'significant' updates to this latest
+         version:
+           - Added BufExplorerGetAltBuf() global function to be used in the
+             user\92s rulerformat.
+           - Added g:bufExplorerSplitRight option.
+           - Added g:bufExplorerShowRelativePath option with mapping.
+           - Added current line highlighting.
+           - The split type can now be changed whether bufexplorer is opened
+             in split mode or not.
+           - Various major and minor bug fixes and speed improvements.
+           - Sort by extension.
+         Other improvements/changes:
+           - Changed the help key from '?' to <F1> to be more 'standard'.
+           - Fixed splitting of vertical bufexplorer window.
+         Hopefully I have not forgot something :)
+7.0.11 - Fixed a couple of highlighting bugs, reported by David Eggum. He also
+         changed passive voice to active on a couple of warning messages.
+7.0.10 - Fixed bug report by Xiangjiang Ma. If the 'ssl' option is set,
+         the slash character used when displaying the path was incorrect.
+7.0.9  - Martin Grenfell found and eliminated an annoying bug in the
+         bufexplorer/winmanager integration. The bug was were an
+         annoying message would be displayed when a window was split or
+         a new file was opened in a new window. Thanks Martin!
+7.0.8  - Thanks to Mike Li for catching a bug in the WinManager integration.
+         The bug was related to the incorrect displaying of the buffer
+         explorer's window title.
+7.0.7  - Thanks to Jeremy Cowgar for adding a new enhancement. This
+         enhancement allows the user to press 'S', that is capital S, which
+         will open the buffer under the cursor in a newly created split
+         window.
+7.0.6  - Thanks to Larry Zhang for finding a bug in the "split" buffer code.
+         If you force set g:bufExplorerSplitType='v' in your vimrc, and if you
+         tried to do a \bs to split the bufexplorer window, it would always
+         split horizontal, not vertical. He also found that I had a typeo in
+         that the variable g:bufExplorerSplitVertSize was all lower case in
+         the documentation which was incorrect.
+7.0.5  - Thanks to Mun Johl for pointing out a bug that if a buffer was
+         modified, the '+' was not showing up correctly.
+7.0.4  - Fixed a problem discovered first by Xiangjiang Ma. Well since I've
+         been using vim 7.0 and not 6.3, I started using a function (getftype)
+         that is not in 6.3. So for backward compatibility, I conditionaly use
+         this function now.  Thus, the g:bufExplorerShowDirectories feature is
+         only available when using vim 7.0 and above.
+7.0.3  - Thanks to Erwin Waterlander for finding a problem when the last
+         buffer was deleted. This issue got me to rewrite the buffer display
+         logic (which I've wanted to do for sometime now).
+         Also great thanks to Dave Eggum for coming up with idea for
+         g:bufExplorerShowDirectories. Read the above information about this
+         feature.
+7.0.2  - Thanks to Thomas Arendsen Hein for finding a problem when a user
+         has the default help turned off and then brought up the explorer. An
+         E493 would be displayed.
+7.0.1  - Thanks to Erwin Waterlander for finding a couple problems.
+         The first problem allowed a modified buffer to be deleted.  Opps! The
+         second problem occurred when several files were opened, BufExplorer
+         was started, the current buffer was deleted using the 'd' option, and
+         then BufExplorer was exited. The deleted buffer was still visible
+         while it is not in the buffers list. Opps again!
+7.0.0  - Thanks to Shankar R. for suggesting to add the ability to set
+         the fixed width (g:bufExplorerSplitVertSize) of a new window
+         when opening bufexplorer vertically and fixed height
+         (g:bufExplorerSplitHorzSize) of a new window when opening
+         bufexplorer horizontally. By default, the windows are normally
+         split to use half the existing width or height.
+6.3.0  - Added keepjumps so that the jumps list would not get cluttered with
+         bufexplorer related stuff.
+6.2.3  - Thanks to Jay Logan for finding a bug in the vertical split position
+         of the code. When selecting that the window was to be split
+         vertically by doing a '\bv', from then on, all splits, i.e. '\bs',
+         were split vertically, even though g:bufExplorerSplitType was not set
+         to 'v'.
+6.2.2  - Thanks to Patrik Modesto for adding a small improvement. For some
+         reason his bufexplorer window was always showing up folded. He added
+         'setlocal nofoldenable' and it was fixed.
+6.2.1  - Thanks goes out to Takashi Matsuo for added the 'fullPath' sorting
+         logic and option.
+6.2.0  - Thanks goes out to Simon Johann-Ganter for spotting and fixing a
+         problem in that the last search pattern is overridden by the search
+         pattern for blank lines.
+6.1.6  - Thanks to Artem Chuprina for finding a pesky bug that has been around
+         for sometime now. The <esc> key mapping was causing the buffer
+         explored to close prematurely when vim was run in an xterm. The <esc>
+         key mapping is now removed.
+6.1.5  - Thanks to Khorev Sergey. Added option to show default help or not.
+6.1.4  - Thanks goes out to Valery Kondakoff for suggesting the addition of
+         setlocal nonumber and foldcolumn=0. This allows for line numbering
+         and folding to be turned off temporarily while in the explorer.
+6.1.3  - Added folding. Did some code cleanup. Added the ability to force the
+         newly split window to be temporarily vertical, which was suggested by
+         Thomas Glanzmann.
+6.1.2  - Now pressing the <esc> key will quit, just like 'q'.
+         Added folds to hide winmanager configuration.
+         If anyone had the 'C' option in their cpoptions they would receive
+         a E10 error on startup of BufExplorer. cpo is now saved, updated and
+         restored. Thanks to Charles E Campbell, Jr.
+         Attempted to make sure there can only be one BufExplorer window open
+         at a time.
+6.1.1  - Thanks to Brian D. Goodwin for adding toupper to FileNameCmp. This
+         way buffers sorted by name will be in the correct order regardless of
+         case.
+6.0.16 - Thanks to Andre Pang for the original patch/idea to get bufexplorer
+         to work in insertmode/modeless mode (evim). Added Initialize
+         and Cleanup autocommands to handle commands that need to be
+         performed when starting or leaving bufexplorer.
+6.0.15 - Srinath Avadhanulax added a patch for winmanager.vim.
+6.0.14 - Fix a few more bug that I thought I already had fixed. Thanks
+         to Eric Bloodworth for adding 'Open Mode/Edit in Place'. Added
+         vertical splitting.
+6.0.13 - Thanks to Charles E Campbell, Jr. for pointing out some embarrassing
+         typos that I had in the documentation. I guess I need to run
+         the spell checker more :o)
+6.0.12 - Thanks to Madoka Machitani, for the tip on adding the augroup command
+         around the MRUList autocommands.
+6.0.11 - Fixed bug report by Xiangjiang Ma. '"=' was being added to the
+         search history which messed up hlsearch.
+6.0.10 - Added the necessary hooks so that the Srinath Avadhanula's
+         winmanager.vim script could more easily integrate with this script.
+         Tried to improve performance.
+6.0.9  - Added MRU (Most Recently Used) sort ordering.
+6.0.8  - Was not resetting the showcmd command correctly.
+         Added nifty help file.
+6.0.7  - Thanks to Brett Carlane for some great enhancements. Some are added,
+         some are not, yet. Added highlighting of current and alternate
+         filenames. Added splitting of path/filename toggle. Reworked
+         ShowBuffers().
+         Changed my email address.
+6.0.6  - Copyright notice added. Needed this so that it could be distributed
+         with Debian Linux. Fixed problem with the SortListing() function
+         failing when there was only one buffer to display.
+6.0.5  - Fixed problems reported by David Pascoe, in that you where unable to
+         hit 'd' on a buffer that belonged to a files that no longer existed
+         and that the 'yank' buffer was being overridden by the help text when
+         the bufexplorer was opened.
+6.0.4  - Thanks to Charles Campbell, Jr. for making this plugin more plugin
+         *compliant*, adding default keymappings of <Leader>be and <Leader>bs
+         as well as fixing the 'w:sortDirLabel not being defined' bug.
+6.0.3  - Added sorting capabilities. Sort taken from explorer.vim.
+6.0.2  - Can't remember.
+6.0.1  - Initial release.
+
+===============================================================================
+TODO                                                         *bufexplorer-todo*
+
+- The issuing of a ':bd' command does not always remove the buffer number from
+  the MRU list.
+- Look into adding '\bs' (buffer split) command back into the code.
+
+===============================================================================
+CREDITS                                                   *bufexplorer-credits*
+
+Author: Jeff Lanzarotta <delux256-vim at yahoo dot com>
+
+Credit must go out to Bram Moolenaar and all the Vim developers for
+making the world's best editor (IMHO). I also want to thank everyone who
+helped and gave me suggestions. I wouldn't want to leave anyone out so I
+won't list names.
+
+===============================================================================
+vim:tw=78:noet:wrap:ts=8:ft=help:norl:
diff --git a/.vim/doc/tags b/.vim/doc/tags
new file mode 100644 (file)
index 0000000..6c0188f
--- /dev/null
@@ -0,0 +1,95 @@
+:CVSEdit       vcscommand.txt  /*:CVSEdit*
+:CVSEditors    vcscommand.txt  /*:CVSEditors*
+:CVSUnedit     vcscommand.txt  /*:CVSUnedit*
+:CVSWatch      vcscommand.txt  /*:CVSWatch*
+:CVSWatchAdd   vcscommand.txt  /*:CVSWatchAdd*
+:CVSWatchOff   vcscommand.txt  /*:CVSWatchOff*
+:CVSWatchOn    vcscommand.txt  /*:CVSWatchOn*
+:CVSWatchRemove        vcscommand.txt  /*:CVSWatchRemove*
+:CVSWatchers   vcscommand.txt  /*:CVSWatchers*
+:VCSAdd        vcscommand.txt  /*:VCSAdd*
+:VCSAnnotate   vcscommand.txt  /*:VCSAnnotate*
+:VCSBlame      vcscommand.txt  /*:VCSBlame*
+:VCSCommit     vcscommand.txt  /*:VCSCommit*
+:VCSDelete     vcscommand.txt  /*:VCSDelete*
+:VCSDiff       vcscommand.txt  /*:VCSDiff*
+:VCSGotoOriginal       vcscommand.txt  /*:VCSGotoOriginal*
+:VCSInfo       vcscommand.txt  /*:VCSInfo*
+:VCSLock       vcscommand.txt  /*:VCSLock*
+:VCSLog        vcscommand.txt  /*:VCSLog*
+:VCSRemove     vcscommand.txt  /*:VCSRemove*
+:VCSRevert     vcscommand.txt  /*:VCSRevert*
+:VCSReview     vcscommand.txt  /*:VCSReview*
+:VCSStatus     vcscommand.txt  /*:VCSStatus*
+:VCSUnlock     vcscommand.txt  /*:VCSUnlock*
+:VCSUpdate     vcscommand.txt  /*:VCSUpdate*
+:VCSVimDiff    vcscommand.txt  /*:VCSVimDiff*
+VCSCommandCVSDiffOpt   vcscommand.txt  /*VCSCommandCVSDiffOpt*
+VCSCommandCVSExec      vcscommand.txt  /*VCSCommandCVSExec*
+VCSCommandCommitOnWrite        vcscommand.txt  /*VCSCommandCommitOnWrite*
+VCSCommandDeleteOnHide vcscommand.txt  /*VCSCommandDeleteOnHide*
+VCSCommandDiffSplit    vcscommand.txt  /*VCSCommandDiffSplit*
+VCSCommandDisableAll   vcscommand.txt  /*VCSCommandDisableAll*
+VCSCommandDisableExtensionMappings     vcscommand.txt  /*VCSCommandDisableExtensionMappings*
+VCSCommandDisableMappings      vcscommand.txt  /*VCSCommandDisableMappings*
+VCSCommandDisableMenu  vcscommand.txt  /*VCSCommandDisableMenu*
+VCSCommandEdit vcscommand.txt  /*VCSCommandEdit*
+VCSCommandEnableBufferSetup    vcscommand.txt  /*VCSCommandEnableBufferSetup*
+VCSCommandMapPrefix    vcscommand.txt  /*VCSCommandMapPrefix*
+VCSCommandMappings     vcscommand.txt  /*VCSCommandMappings*
+VCSCommandMenuPriority vcscommand.txt  /*VCSCommandMenuPriority*
+VCSCommandMenuRoot     vcscommand.txt  /*VCSCommandMenuRoot*
+VCSCommandResultBufferNameExtension    vcscommand.txt  /*VCSCommandResultBufferNameExtension*
+VCSCommandResultBufferNameFunction     vcscommand.txt  /*VCSCommandResultBufferNameFunction*
+VCSCommandSVKExec      vcscommand.txt  /*VCSCommandSVKExec*
+VCSCommandSVNDiffExt   vcscommand.txt  /*VCSCommandSVNDiffExt*
+VCSCommandSVNDiffOpt   vcscommand.txt  /*VCSCommandSVNDiffOpt*
+VCSCommandSVNExec      vcscommand.txt  /*VCSCommandSVNExec*
+VCSCommandSplit        vcscommand.txt  /*VCSCommandSplit*
+VCSCommandVCSTypeOverride      vcscommand.txt  /*VCSCommandVCSTypeOverride*
+b:VCSCommandCommand    vcscommand.txt  /*b:VCSCommandCommand*
+b:VCSCommandOriginalBuffer     vcscommand.txt  /*b:VCSCommandOriginalBuffer*
+b:VCSCommandSourceFile vcscommand.txt  /*b:VCSCommandSourceFile*
+b:VCSCommandVCSType    vcscommand.txt  /*b:VCSCommandVCSType*
+bufexplorer    bufexplorer.txt /*bufexplorer*
+bufexplorer-changelog  bufexplorer.txt /*bufexplorer-changelog*
+bufexplorer-credits    bufexplorer.txt /*bufexplorer-credits*
+bufexplorer-customization      bufexplorer.txt /*bufexplorer-customization*
+bufexplorer-installation       bufexplorer.txt /*bufexplorer-installation*
+bufexplorer-todo       bufexplorer.txt /*bufexplorer-todo*
+bufexplorer-usage      bufexplorer.txt /*bufexplorer-usage*
+bufexplorer.txt        bufexplorer.txt /*bufexplorer.txt*
+buffer-explorer        bufexplorer.txt /*buffer-explorer*
+cvscommand-changes     vcscommand.txt  /*cvscommand-changes*
+g:bufExplorerDefaultHelp       bufexplorer.txt /*g:bufExplorerDefaultHelp*
+g:bufExplorerDetailedHelp      bufexplorer.txt /*g:bufExplorerDetailedHelp*
+g:bufExplorerFindActive        bufexplorer.txt /*g:bufExplorerFindActive*
+g:bufExplorerReverseSort       bufexplorer.txt /*g:bufExplorerReverseSort*
+g:bufExplorerShowDirectories   bufexplorer.txt /*g:bufExplorerShowDirectories*
+g:bufExplorerShowRelativePath  bufexplorer.txt /*g:bufExplorerShowRelativePath*
+g:bufExplorerShowUnlisted      bufexplorer.txt /*g:bufExplorerShowUnlisted*
+g:bufExplorerSortBy    bufexplorer.txt /*g:bufExplorerSortBy*
+g:bufExplorerSplitOutPathName  bufexplorer.txt /*g:bufExplorerSplitOutPathName*
+vcscommand     vcscommand.txt  /*vcscommand*
+vcscommand-buffer-management   vcscommand.txt  /*vcscommand-buffer-management*
+vcscommand-buffer-variables    vcscommand.txt  /*vcscommand-buffer-variables*
+vcscommand-bugs        vcscommand.txt  /*vcscommand-bugs*
+vcscommand-commands    vcscommand.txt  /*vcscommand-commands*
+vcscommand-config      vcscommand.txt  /*vcscommand-config*
+vcscommand-contents    vcscommand.txt  /*vcscommand-contents*
+vcscommand-customize   vcscommand.txt  /*vcscommand-customize*
+vcscommand-events      vcscommand.txt  /*vcscommand-events*
+vcscommand-install     vcscommand.txt  /*vcscommand-install*
+vcscommand-intro       vcscommand.txt  /*vcscommand-intro*
+vcscommand-manual      vcscommand.txt  /*vcscommand-manual*
+vcscommand-mappings    vcscommand.txt  /*vcscommand-mappings*
+vcscommand-mappings-override   vcscommand.txt  /*vcscommand-mappings-override*
+vcscommand-naming      vcscommand.txt  /*vcscommand-naming*
+vcscommand-options     vcscommand.txt  /*vcscommand-options*
+vcscommand-ssh vcscommand.txt  /*vcscommand-ssh*
+vcscommand-ssh-config  vcscommand.txt  /*vcscommand-ssh-config*
+vcscommand-ssh-env     vcscommand.txt  /*vcscommand-ssh-env*
+vcscommand-ssh-other   vcscommand.txt  /*vcscommand-ssh-other*
+vcscommand-ssh-wrapper vcscommand.txt  /*vcscommand-ssh-wrapper*
+vcscommand-statusline  vcscommand.txt  /*vcscommand-statusline*
+vcscommand.txt vcscommand.txt  /*vcscommand.txt*
diff --git a/.vim/doc/vcscommand.txt b/.vim/doc/vcscommand.txt
new file mode 100644 (file)
index 0000000..4f38ab1
--- /dev/null
@@ -0,0 +1,835 @@
+*vcscommand.txt*       vcscommand
+Copyright (c) Bob Hiestand
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
+
+For instructions on installing this file, type
+       :help add-local-help
+inside Vim.
+
+Author:  Bob Hiestand <bob.hiestand@gmail.com>
+Credits:  Benji Fisher's excellent MatchIt documentation
+
+==============================================================================
+1. Contents                                            *vcscommand-contents*
+
+       Installation            : |vcscommand-install|
+       vcscommand Intro        : |vcscommand|
+       vcscommand Manual       : |vcscommand-manual|
+       Customization           : |vcscommand-customize|
+       SSH "integration"       : |vcscommand-ssh|
+       Changes from cvscommand : |cvscommand-changes|
+       Bugs                    : |vcscommand-bugs|
+
+==============================================================================
+
+2. vcscommand Installation                             *vcscommand-install*
+
+The vcscommand plugin comprises five files: vcscommand.vim, vcssvn.vim,
+vcscvs.vim, vcssvk.vim and vcscommand.txt (this file).  In order to install
+the plugin, place the vcscommand.vim, vcssvn.vim, vcssvk.vim, and vcscvs.vim
+files into a plugin directory in your runtime path (please see
+|add-global-plugin| and |'runtimepath'|.
+
+This help file can be included in the VIM help system by copying it into a
+'doc' directory in your runtime path and then executing the |:helptags|
+command, specifying the full path of the 'doc' directory.  Please see
+|add-local-help| for more details.
+
+vcscommand may be customized by setting variables, creating maps, and
+specifying event handlers.  Please see |vcscommand-customize| for more
+details.
+
+==============================================================================
+
+3. vcscommand Intro                                    *vcscommand*
+                                                       *vcscommand-intro*
+
+The vcscommand plugin provides global ex commands for manipulating
+version-controlled source files, currently those controlled either by CVS or
+Subversion.  In general, each command operates on the current buffer and
+accomplishes a separate source control function, such as update, commit, log,
+and others (please see |vcscommand-commands| for a list of all available
+commands).  The results of each operation are displayed in a scratch buffer.
+Several buffer variables are defined for those scratch buffers (please see
+|vcscommand-buffer-variables|).
+
+The notion of "current file" means either the current buffer, or, in the case
+of a directory buffer (such as Explorer or netrw buffers), the directory (and
+all subdirectories) represented by the the buffer.
+
+For convenience, any vcscommand invoked on a vcscommand scratch buffer acts as
+though it was invoked on the original file and splits the screen so that the
+output appears in a new window.
+
+Many of the commands accept revisions as arguments.  By default, most operate
+on the most recent revision on the current branch if no revision is specified.
+
+Each vcscommand is mapped to a key sequence starting with the |<Leader>|
+keystroke.  The default mappings may be overridden by supplying different
+mappings before the plugin is loaded, such as in the vimrc, in the standard
+fashion for plugin mappings.  For examples, please see
+|vcscommand-mappings-override|.
+
+The vcscommand plugin may be configured in several ways.  For more details,
+please see |vcscommand-customize|.
+
+==============================================================================
+
+4. vcscommand Manual                                   *vcscommand-manual*
+
+4.1 vcscommand commands                                        *vcscommand-commands*
+
+vcscommand defines the following commands:
+
+|:VCSAdd|
+|:VCSAnnotate|
+|:VCSBlame|
+|:VCSCommit|
+|:VCSDelete|
+|:VCSDiff|
+|:VCSGotoOriginal|
+|:VCSLog|
+|:VCSRemove|
+|:VCSRevert|
+|:VCSReview|
+|:VCSStatus|
+|:VCSUpdate|
+|:VCSVimDiff|
+
+The following commands are specific to CVS files:
+
+|:CVSEdit|
+|:CVSEditors|
+|:CVSUnedit|
+|:CVSWatch|
+|:CVSWatchAdd|
+|:CVSWatchOn|
+|:CVSWatchOff|
+|:CVSWatchRemove|
+|:CVSWatchers|
+
+:VCSAdd                                                        *:VCSAdd*
+
+This command adds the current file to source control.  Please note, this does
+not commit the newly-added file.  All parameters to the command are passed to
+the underlying VCS.
+
+:VCSAnnotate[!]                                                *:VCSAnnotate*
+
+This command displays the current file with each line annotated with the
+version in which it was most recently changed.  If an argument is given, the
+argument is used as a revision number to display.  If not given an argument,
+it uses the most recent version of the file (on the current branch, if under
+CVS control).  Additionally, if the current buffer is a VCSAnnotate buffer
+already, the version number on the current line is used.
+
+If '!' is used, the view of the annotated buffer is split so that the
+annotation is in a separate window from the content, and each is highlighted
+separately.
+
+For CVS buffers, the 'VCSCommandCVSAnnotateParent' option, if set to non-zero,
+will cause the above behavior to change.  Instead of annotating the version on
+the current line, the parent revision is used instead, crossing branches if
+necessary.
+
+With no arguments the cursor will jump to the line in the annotated buffer
+corresponding to the current line in the source buffer.
+
+:VCSBlame[!]                                           *:VCSBlame*
+
+Alias for |:VCSAnnotate|.
+
+:VCSCommit[!]                                          *:VCSCommit*
+
+This command commits changes to the current file to source control.
+
+If called with arguments, the arguments are the log message.
+
+If '!' is used, an empty log message is committed.
+
+If called with no arguments, this is a two-step command.  The first step opens
+a buffer to accept a log message.  When that buffer is written, it is
+automatically closed and the file is committed using the information from that
+log message.  The commit can be abandoned if the log message buffer is deleted
+or wiped before being written.
+
+Alternatively, the mapping that is used to invoke :VCSCommit (by default
+|<Leader>|cc, please see |vcscommand-mappings|) can be used in the log message
+buffer in Normal mode to immediately commit.  This is useful if the
+|VCSCommandCommitOnWrite| variable is set to 0 to disable the normal
+commit-on-write behavior.
+
+:VCSDelete                                             *:VCSDelete*
+
+Deletes the current file and removes it from source control.  All parameters
+to the command are passed to the underlying VCS.
+
+:VCSDiff                                               *:VCSDiff*
+
+With no arguments, this displays the differences between the current file and
+its parent version under source control in a new scratch buffer.
+
+With one argument, the diff is performed on the current file against the
+specified revision.
+
+With two arguments, the diff is performed between the specified revisions of
+the current file.
+
+For CVS, this command uses the |VCSCommandCVSDiffOpt| variable to specify diff
+options.  If that variable does not exist, a plugin-specific default is used.
+If you wish to have no options, then set it to the empty string.
+
+For SVN, this command uses the |VCSCommandSVNDiffOpt| variable to specify diff
+options.  If that variable does not exist, the SVN default is used.
+Additionally, |VCSCommandSVNDiffExt| can be used to select an external diff
+application.
+
+:VCSGotoOriginal                                       *:VCSGotoOriginal*
+
+This command jumps to the source buffer if the current buffer is a VCS scratch
+buffer.
+
+:VCSGotoOriginal!
+
+Like ":VCSGotoOriginal" but also executes :bufwipeout on all VCS scrach
+buffers associated with the original file.
+
+:VCSInfo                                               *:VCSInfo*
+
+This command displays extended information about the current file in a new
+scratch buffer.
+
+:VCSLock                                               *:VCSLock*
+
+This command locks the current file in order to prevent other users from
+concurrently modifying it.  The exact semantics of this command depend on the
+underlying VCS.  This does nothing in CVS.  All parameters are passed to the
+underlying VCS.
+
+:VCSLog                                                        *:VCSLog*
+
+Displays the version history of the current file in a new scratch buffer.  If
+there is one parameter supplied, it is taken as as a revision parameters to be
+passed through to the underlying VCS.  Otherwise, all parameters are passed to
+the underlying VCS.
+
+:VCSRemove                                             *:VCSRemove*
+
+Alias for |:VCSDelete|.
+
+:VCSRevert                                             *:VCSRevert*
+
+This command replaces the current file with the most recent version from the
+repository in order to wipe out any undesired changes.
+
+:VCSReview                                             *:VCSReview*
+
+Displays a particular version of the current file in a new scratch buffer.  If
+no argument is given, the most recent version of the file on the current
+branch is retrieved.
+
+:VCSStatus                                             *:VCSStatus*
+
+Displays versioning information about the current file in a new scratch
+buffer.  All parameters are passed to the underlying VCS.
+
+
+:VCSUnlock                                             *:VCSUnlock*
+
+Unlocks the current file in order to allow other users from concurrently
+modifying it.  The exact semantics of this command depend on the underlying
+VCS.  All parameters are passed to the underlying VCS.
+
+:VCSUpdate                                             *:VCSUpdate*
+
+Updates the current file with any relevant changes from the repository.  This
+intentionally does not automatically reload the current buffer, though vim
+should prompt the user to do so if the underlying file is altered by this
+command.
+
+:VCSVimDiff                                            *:VCSVimDiff*
+
+Uses vimdiff to display differences between versions of the current file.
+
+If no revision is specified, the most recent version of the file on the
+current branch is used.  With one argument, that argument is used as the
+revision as above.  With two arguments, the differences between the two
+revisions is displayed using vimdiff.
+
+With either zero or one argument, the original buffer is used to perform the
+vimdiff.  When the scratch buffer is closed, the original buffer will be
+returned to normal mode.
+
+Once vimdiff mode is started using the above methods, additional vimdiff
+buffers may be added by passing a single version argument to the command.
+There may be up to 4 vimdiff buffers total.
+
+Using the 2-argument form of the command resets the vimdiff to only those 2
+versions.  Additionally, invoking the command on a different file will close
+the previous vimdiff buffers.
+
+:CVSEdit                                               *:CVSEdit*
+
+This command performs "cvs edit" on the current file.  Yes, the output buffer
+in this case is almost completely useless.
+
+:CVSEditors                                            *:CVSEditors*
+
+This command performs "cvs edit" on the current file.
+
+:CVSUnedit                                             *:CVSUnedit*
+
+Performs "cvs unedit" on the current file.  Again, yes, the output buffer here
+is basically useless.
+
+:CVSWatch                                              *:CVSWatch*
+
+This command takes an argument which must be one of [on|off|add|remove].  The
+command performs "cvs watch" with the given argument on the current file.
+
+:CVSWatchAdd                                           *:CVSWatchAdd*
+
+This command is an alias for ":CVSWatch add"
+
+:CVSWatchOn                                            *:CVSWatchOn*
+
+This command is an alias for ":CVSWatch on"
+
+:CVSWatchOff                                           *:CVSWatchOff*
+
+This command is an alias for ":CVSWatch off"
+
+:CVSWatchRemove                                                *:CVSWatchRemove*
+
+This command is an alias for ":CVSWatch remove"
+
+:CVSWatchers                                           *:CVSWatchers*
+
+This command performs "cvs watchers" on the current file.
+
+4.2 Mappings                                           *vcscommand-mappings*
+
+By default, a mapping is defined for each command.  These mappings execute the
+default (no-argument) form of each command.
+
+|<Leader>|ca VCSAdd
+|<Leader>|cn VCSAnnotate
+|<Leader>|cN VCSAnnotate!
+|<Leader>|cc VCSCommit
+|<Leader>|cD VCSDelete
+|<Leader>|cd VCSDiff
+|<Leader>|cg VCSGotoOriginal
+|<Leader>|cG VCSGotoOriginal!
+|<Leader>|ci VCSInfo
+|<Leader>|cl VCSLog
+|<Leader>|cL VCSLock
+|<Leader>|cr VCSReview
+|<Leader>|cs VCSStatus
+|<Leader>|cu VCSUpdate
+|<Leader>|cU VCSUnlock
+|<Leader>|cv VCSVimDiff
+
+Only for CVS buffers:
+
+|<Leader>|ce CVSEdit
+|<Leader>|cE CVSEditors
+|<Leader>|ct CVSUnedit
+|<Leader>|cwv CVSWatchers
+|<Leader>|cwa CVSWatchAdd
+|<Leader>|cwn CVSWatchOn
+|<Leader>|cwf CVSWatchOff
+|<Leader>|cwf CVSWatchRemove
+
+                                               *vcscommand-mappings-override*
+
+The default mappings can be overridden by user-provided instead by mapping to
+<Plug>CommandName.  This is especially useful when these mappings collide with
+other existing mappings (vim will warn of this during plugin initialization,
+but will not clobber the existing mappings).
+
+There are three methods for controlling mapping:
+
+First, maps can be overriden for individual commands.  For instance, to
+override the default mapping for :VCSAdd to set it to '\add', add the
+following to the vimrc:
+
+nmap \add <Plug>VCSAdd
+
+Second, the default map prefix ('<Leader>c') can be overridden by defining the
+|VCSCommandMapPrefix| variable.
+
+Third, the entire set of default maps can be overridden by defining the
+|VCSCommandMappings| variable.
+
+
+4.3 Automatic buffer variables                 *vcscommand-buffer-variables*
+
+Several buffer variables are defined in each vcscommand result buffer. These
+may be useful for additional customization in callbacks defined in the event
+handlers (please see |vcscommand-events|).
+
+The following variables are automatically defined:
+
+b:VCSCommandOriginalBuffer                     *b:VCSCommandOriginalBuffer*
+
+This variable is set to the buffer number of the source file.
+
+b:VCSCommandCommand                            *b:VCSCommandCommand*
+
+This variable is set to the name of the vcscommand that created the result
+buffer.
+
+b:VCSCommandSourceFile                         *b:VCSCommandSourceFile*
+
+This variable is set to the name of the original file under source control.
+
+b:VCSCommandVCSType                            *b:VCSCommandVCSType*
+
+This variable is set to the type of the source control.  This variable is also
+set on the original file itself.
+==============================================================================
+
+5. Configuration and customization                     *vcscommand-customize*
+                                                       *vcscommand-config*
+
+The vcscommand plugin can be configured in several ways:  by setting
+configuration variables (see |vcscommand-options|) or by defining vcscommand
+event handlers (see |vcscommand-events|).  Additionally, the vcscommand plugin
+supports a customized status line (see |vcscommand-statusline| and
+|vcscommand-buffer-management|).
+
+5.1 vcscommand configuration variables                 *vcscommand-options*
+
+Several variables affect the plugin's behavior.  These variables are checked
+at time of execution, and may be defined at the window, buffer, or global
+level and are checked in that order of precedence.
+
+
+The following variables are available:
+
+|VCSCommandCommitOnWrite|
+|VCSCommandCVSDiffOpt|
+|VCSCommandCVSExec|
+|VCSCommandDeleteOnHide|
+|VCSCommandDiffSplit|
+|VCSCommandDisableAll|
+|VCSCommandDisableMappings|
+|VCSCommandDisableExtensionMappings|
+|VCSCommandDisableMenu|
+|VCSCommandEdit|
+|VCSCommandEnableBufferSetup|
+|VCSCommandMappings|
+|VCSCommandMapPrefix|
+|VCSCommandMenuPriority|
+|VCSCommandMenuRoot|
+|VCSCommandResultBufferNameExtension|
+|VCSCommandResultBufferNameFunction|
+|VCSCommandSplit|
+|VCSCommandSVKExec|
+|VCSCommandSVNDiffExt|
+|VCSCommandSVNDiffOpt|
+|VCSCommandSVNExec|
+|VCSCommandVCSTypeOverride|
+
+VCSCommandCommitOnWrite                                *VCSCommandCommitOnWrite*
+
+This variable, if set to a non-zero value, causes the pending commit
+to take place immediately as soon as the log message buffer is written.
+If set to zero, only the VCSCommit mapping will cause the pending commit to
+occur. If not set, it defaults to 1.
+
+VCSCommandCVSExec                              *VCSCommandCVSExec*
+
+This variable controls the executable used for all CVS commands  If not set,
+it defaults to "cvs".
+
+VCSCommandDeleteOnHide                         *VCSCommandDeleteOnHide*
+
+This variable, if set to a non-zero value, causes the temporary result buffers
+to automatically delete themselves when hidden.
+
+VCSCommandCVSDiffOpt                           *VCSCommandCVSDiffOpt*
+
+This variable, if set, determines the options passed to the diff command of
+CVS.  If not set, it defaults to 'u'.
+
+VCSCommandDiffSplit                            *VCSCommandDiffSplit*
+
+This variable overrides the |VCSCommandSplit| variable, but only for buffers
+created with |:VCSVimDiff|.
+
+VCSCommandDisableAll                           *VCSCommandDisableAll*
+
+This variable, if set, prevents the plugin or any extensions from loading at
+all.  This is useful when a single runtime distribution is used on multiple
+systems with varying versions.
+
+VCSCommandDisableMappings                      *VCSCommandDisableMappings*
+
+This variable, if set to a non-zero value, prevents the default command
+mappings from being set.  This supercedes
+|VCSCommandDisableExtensionMappings|.
+
+VCSCommandDisableExtensionMappings     *VCSCommandDisableExtensionMappings*
+
+This variable, if set to a non-zero value, prevents the default command
+mappings from being set for commands specific to an individual VCS.
+
+VCSCommandEdit                                 *VCSCommandEdit*
+
+This variable controls whether the original buffer is replaced ('edit') or
+split ('split').  If not set, it defaults to 'split'.
+
+VCSCommandDisableMenu                          *VCSCommandDisableMenu*
+
+This variable, if set to a non-zero value, prevents the default command menu
+from being set.
+
+VCSCommandEnableBufferSetup                    *VCSCommandEnableBufferSetup*
+
+This variable, if set to a non-zero value, activates VCS buffer management
+mode see (|vcscommand-buffer-management|).  This mode means that the
+'VCSCommandBufferInfo' variable is filled with version information if the file
+is VCS-controlled.  This is useful for displaying version information in the
+status bar.
+
+VCSCommandMappings                             *VCSCommandMappings*
+
+This variable, if set, overrides the default mappings used for shortcuts.  It
+should be a List of 2-element Lists, each containing a shortcut and function
+name pair.  The value of the '|VCSCommandMapPrefix|' variable will be added to
+each shortcut.
+
+VCSCommandMapPrefix                            *VCSCommandMapPrefix*
+
+This variable, if set, overrides the default mapping prefix ('<Leader>c').
+This allows customization of the mapping space used by the vcscommand
+shortcuts.
+
+VCSCommandMenuPriority                         *VCSCommandMenuPriority*
+
+This variable, if set, overrides the default menu priority '' (empty)
+
+VCSCommandMenuRoot                             *VCSCommandMenuRoot*
+
+This variable, if set, overrides the default menu root 'Plugin.VCS'
+
+VCSCommandResultBufferNameExtension    *VCSCommandResultBufferNameExtension*
+
+This variable, if set to a non-blank value, is appended to the name of the VCS
+command output buffers.  For example, '.vcs'.  Using this option may help
+avoid problems caused by autocommands dependent on file extension.
+
+VCSCommandResultBufferNameFunction     *VCSCommandResultBufferNameFunction*
+
+This variable, if set, specifies a custom function for naming VCS command
+output buffers.  This function is expected to return the new buffer name, and
+will be passed the following arguments:
+
+  command - name of the VCS command being executed (such as 'Log' or
+  'Diff').
+
+  originalBuffer - buffer number of the source file.
+
+  vcsType - type of VCS controlling this file (such as 'CVS' or 'SVN').
+
+  statusText - extra text associated with the VCS action (such as version
+  numbers).
+
+VCSCommandSplit                                        *VCSCommandSplit*
+
+This variable controls the orientation of the various window splits that
+may occur.
+
+If set to 'horizontal', the resulting windows will be on stacked on top of
+one another.  If set to 'vertical', the resulting windows will be
+side-by-side.  If not set, it defaults to 'horizontal' for all but
+VCSVimDiff windows.  VCSVimDiff windows default to the user's 'diffopt'
+setting, if set, otherwise 'vertical'.
+
+VCSCommandSVKExec                              *VCSCommandSVKExec*
+
+This variable controls the executable used for all SVK commands  If not set,
+it defaults to "svk".
+
+VCSCommandSVNDiffExt                           *VCSCommandSVNDiffExt*
+
+This variable, if set, is passed to SVN via the --diff-cmd command to select
+an external application for performing the diff.
+
+VCSCommandSVNDiffOpt                           *VCSCommandSVNDiffOpt*
+
+This variable, if set, determines the options passed with the '-x' parameter
+to the SVN diff command.  If not set, no options are passed.
+
+VCSCommandSVNExec                              *VCSCommandSVNExec*
+
+This variable controls the executable used for all SVN commands  If not set,
+it defaults to "svn".
+
+VCSCommandVCSTypeOverride                      *VCSCommandVCSTypeOverride*
+
+This variable allows the VCS type detection to be overridden on a path-by-path
+basis.  The value of this variable is expected to be a List of Lists.  Each
+item in the high-level List is a List containing two elements.  The first
+element is a regular expression that will be matched against the full file
+name of a given buffer.  If it matches, the second element will be used as the
+VCS type.
+
+5.2 VCSCommand events                          *vcscommand-events*
+
+For additional customization, vcscommand can trigger user-defined events.
+Event handlers are provided by defining User event autocommands (see
+|autocommand|, |User|) in the vcscommand group with patterns matching the
+event name.
+
+For instance, the following could be added to the vimrc to provide a 'q'
+mapping to quit a vcscommand scratch buffer:
+
+augroup VCSCommand
+  au User VCSBufferCreated silent! nmap <unique> <buffer> q :bwipeout<cr>
+augroup END
+
+The following hooks are available:
+
+VCSBufferCreated               This event is fired just after a vcscommand
+                                result buffer is created and populated.  It is
+                                executed within the context of the vcscommand
+                                buffer.  The vcscommand buffer variables may
+                                be useful for handlers of this event (please
+                                see |vcscommand-buffer-variables|).
+
+VCSBufferSetup                 This event is fired just after vcscommand buffer
+                                setup occurs, if enabled.
+
+VCSPluginInit                  This event is fired when the vcscommand plugin
+                               first loads.
+
+VCSPluginFinish                        This event is fired just after the vcscommand
+                               plugin loads.
+
+VCSVimDiffFinish               This event is fired just after the VCSVimDiff
+                               command executes to allow customization of,
+                               for instance, window placement and focus.
+
+Additionally, there is another hook which is used internally to handle loading
+the multiple scripts in order.  This hook should probably not be used by an
+end user without a good idea of how it works.  Among other things, any events
+associated with this hook are cleared after they are executed (during
+vcscommand.vim script initialization).
+
+VCSLoadExtensions              This event is fired just before the
+                                VCSPluginFinish.  It is used internally to
+                                execute any commands from the VCS
+                                implementation plugins that needs to be
+                                deferred until the primary plugin is
+                                initialized.
+
+5.3 vcscommand buffer naming                           *vcscommand-naming*
+
+vcscommand result buffers use the following naming convention:
+[{VCS type} {VCS command} {Source file name}]
+
+If additional buffers are created that would otherwise conflict, a
+distinguishing number is added:
+
+[{VCS type} {VCS command} {Source file name}] (1,2, etc)
+
+5.4 vcscommand status line support                     *vcscommand-statusline*
+
+It is intended that the user will customize the |'statusline'| option to
+include vcscommand result buffer attributes.  A sample function that may be
+used in the |'statusline'| option is provided by the plugin,
+VCSCommandGetStatusLine().  In order to use that function in the status line, do
+something like the following:
+
+set statusline=%<%f\ %{VCSCommandGetStatusLine()}\ %h%m%r%=%l,%c%V\ %P
+
+of which %{VCSCommandGetStatusLine()} is the relevant portion.
+
+The sample VCSCommandGetStatusLine() function handles both vcscommand result
+buffers and VCS-managed files if vcscommand buffer management is enabled
+(please see |vcscommand-buffer-management|).
+
+5.5 vcscommand buffer management               *vcscommand-buffer-management*
+
+The vcscommand plugin can operate in buffer management mode, which means that
+it attempts to set a buffer variable ('VCSCommandBufferInfo') upon entry into
+a buffer.  This is rather slow because it means that the VCS will be invoked
+at each entry into a buffer (during the |BufEnter| autocommand).
+
+This mode is disabled by default.  In order to enable it, set the
+|VCSCommandEnableBufferSetup| variable to a true (non-zero) value.  Enabling
+this mode simply provides the buffer variable mentioned above.  The user must
+explicitly include information from the variable in the |'statusline'| option
+if they are to appear in the status line (but see |vcscommand-statusline| for
+a simple way to do that).
+
+The 'VCSCommandBufferInfo' variable is a list which contains, in order, the
+revision of the current file, the latest revision of the file in the
+repository, and (for CVS) the name of the branch.  If those values cannot be
+determined, the list is a single element:  'Unknown'.
+
+==============================================================================
+
+6. SSH "integration"                                   *vcscommand-ssh*
+
+The following instructions are intended for use in integrating the
+vcscommand.vim plugin with an SSH-based CVS environment.
+
+Familiarity with SSH and CVS are assumed.
+
+These instructions assume that the intent is to have a message box pop up in
+order to allow the user to enter a passphrase.  If, instead, the user is
+comfortable using certificate-based authentication, then only instructions
+6.1.1 and 6.1.2 (and optionally 6.1.4) need to be followed; ssh should then
+work transparently.
+
+6.1 Environment settings                               *vcscommand-ssh-env*
+
+6.1.1 CVSROOT should be set to something like:
+
+       :ext:user@host:/path_to_repository
+
+6.1.2 CVS_RSH should be set to:
+
+       ssh
+
+       Together, those settings tell CVS to use ssh as the transport when
+       performing CVS calls.
+
+6.1.3 SSH_ASKPASS should be set to the password-dialog program.  In my case,
+       running gnome, it's set to:
+
+       /usr/libexec/openssh/gnome-ssh-askpass
+
+       This tells SSH how to get passwords if no input is available.
+
+6.1.4 OPTIONAL.  You may need to set SSH_SERVER to the location of the cvs
+       executable on the remote (server) machine.
+
+6.2 CVS wrapper program                                *vcscommand-ssh-wrapper*
+
+Now you need to convince SSH to use the password-dialog program.  This means
+you need to execute SSH (and therefore CVS) without standard input.  The
+following script is a simple perl wrapper that dissasociates the CVS command
+from the current terminal.  Specific steps to do this may vary from system to
+system; the following example works for me on linux.
+
+#!/usr/bin/perl -w
+use strict;
+use POSIX qw(setsid);
+open STDIN, '/dev/null';
+fork and do {wait; exit;};
+setsid;
+exec('cvs', @ARGV);
+
+6.3 Configuring vcscommand.vim                 *vcscommand-ssh-config*
+
+At this point, you should be able to use your wrapper script to invoke CVS with
+various commands, and get the password dialog.  All that's left is to make CVS
+use your newly-created wrapper script.
+
+6.3.1 Tell vcscommand.vim what CVS executable to use.  The easiest way to do this
+       is globally, by putting the following in your .vimrc:
+
+       let VCSCommandCVSExec=/path/to/cvs/wrapper/script
+
+6.4 Where to go from here                      *vcscommand-ssh-other*
+
+The script given above works even when non-SSH CVS connections are used,
+except possibly when interactively entering the message for CVS commit log
+(depending on the editor you use... VIM works fine).  Since the vcscommand.vim
+plugin handles that message without a terminal, the wrapper script can be used
+all the time.
+
+This allows mixed-mode operation, where some work is done with SSH-based CVS
+repositories, and others with pserver or local access.
+
+It is possible, though beyond the scope of the plugin, to dynamically set the
+CVS executable based on the CVSROOT for the file being edited.  The user
+events provided (such as VCSBufferCreated and VCSBufferSetup) can be used to
+set a buffer-local value (b:VCSCommandCVSExec) to override the CVS executable
+on a file-by-file basis.  Alternatively, much the same can be done (less
+automatically) by the various project-oriented plugins out there.
+
+It is highly recommended for ease-of-use that certificates with no passphrase
+or ssh-agent are employed so that the user is not given the password prompt
+too often.
+
+==============================================================================
+
+7. Changes from cvscommand                             *cvscommand-changes*
+
+1.  Require Vim 7 in order to leverage several convenient features; also
+because I wanted to play with Vim 7.
+
+2.  Renamed commands to start with 'VCS' instead of 'CVS'.  The exceptions are
+the 'CVSEdit' and 'CVSWatch' family of commands, which are specific to CVS.
+
+3.  Renamed options, events to start with 'VCSCommand'.
+
+4.  Removed option to jump to the parent version of the current line in an
+annotated buffer, as opposed to the version on the current line.  This made
+little sense in the branching scheme used by subversion, where jumping to a
+parent branch required finding a different location in the repository.  It
+didn't work consistently in CVS anyway.
+
+5.  Removed option to have nameless scratch buffers.
+
+6.  Changed default behavior of scratch buffers to split the window instead of
+displaying in the current window.  This may still be overridden using the
+'VCSCommandEdit' option.
+
+7.  Split plugin into multiple plugins.
+
+8.  Added 'VCSLock' and 'VCSUnlock' commands.  These are implemented for
+subversion but not for CVS.  These were not kept specific to subversion as they
+seemed more general in nature and more likely to be supported by any future VCS
+supported by this plugin.
+
+9.  Changed name of buffer variables set by commands.
+
+'b:cvsOrigBuffNR' became 'b:VCSCommandOriginalBuffer'
+'b:cvscmd' became 'b:VCSCommandCommand'
+
+10.  Added new automatic variables to command result buffers.
+
+'b:VCSCommandSourceFile'
+'b:VCSCommandVCSType'
+
+==============================================================================
+
+8. Known bugs                                          *vcscommand-bugs*
+
+Please let me know if you run across any.
+
+CVSUnedit may, if a file is changed from the repository, provide prompt text
+to determine whether the changes should be thrown away.  Currently, that text
+shows up in the CVS result buffer as information; there is no way for the user
+to actually respond to the prompt and the CVS unedit command does nothing.  If
+this really bothers anyone, please let me know.
+
+VCSVimDiff, when using the original (real) source buffer as one of the diff
+buffers, uses some hacks to try to restore the state of the original buffer
+when the scratch buffer containing the other version is destroyed.  There may
+still be bugs in here, depending on many configuration details.
+
+vim:tw=78:ts=8:ft=help
diff --git a/.vim/ftplugin/c.vim b/.vim/ftplugin/c.vim
new file mode 100644 (file)
index 0000000..16ffc97
--- /dev/null
@@ -0,0 +1 @@
+setlocal foldmethod=syntax
diff --git a/.vim/ftplugin/imath.vim b/.vim/ftplugin/imath.vim
new file mode 100644 (file)
index 0000000..7e7f4b2
--- /dev/null
@@ -0,0 +1,13 @@
+" my math filetype that extends txt for unicode/ascii math symbol abbreviations
+ab pi π
+ab sqrt √
+ab intg ∫
+ab ^2 ²
+ab sigma σ
+ab mew µ
+ab half ½
+ab epsilon ε 03b5
+ab _0 ₀
+ab _1 ₁
+ab beta β
+ab && ∩
diff --git a/.vim/ftplugin/java.vim b/.vim/ftplugin/java.vim
new file mode 100644 (file)
index 0000000..7883de5
--- /dev/null
@@ -0,0 +1,16 @@
+" Javadoc comments (/** and */ pairs) and code sections (marked by {} pairs) mark the start and end of folds. All other
+" lines simply take the fold level that is going so far.
+function! MyFoldLevel( lineNumber )
+  let thisLine = getline( a:lineNumber )
+  " If the entire Javadoc comment or the {} pair is on one line, then don't create a fold for it.
+  if ( thisLine =~ '\%(\%(/\*\*\).*\%(\*/\)\)\|\%({.*}\)' )
+    return '='
+  elseif ( thisLine =~ '\%(^\s*/\*\*\s*$\)\|{' )
+    return "a1"
+  elseif ( thisLine =~ '\%(^\s*\*/\s*$\)\|}' )
+    return "s1"
+  endif
+  return '='
+endfunction
+setlocal foldexpr=MyFoldLevel(v:lnum)
+setlocal foldmethod=expr
diff --git a/.vim/ftplugin/txt.vim b/.vim/ftplugin/txt.vim
new file mode 100644 (file)
index 0000000..9b40fa7
--- /dev/null
@@ -0,0 +1,2 @@
+setlocal tw=0
+setlocal foldmethod=indent
diff --git a/.vim/misc/filetype.vim b/.vim/misc/filetype.vim
new file mode 100644 (file)
index 0000000..d5d51de
--- /dev/null
@@ -0,0 +1,5 @@
+augroup filetypedetec
+au BufNewFile,BufRead *.txt     setf txt
+au BufNewFile,BufRead *.R     setf r
+au BufNewFile,BufRead *.R     set syntax=r
+augroup END
diff --git a/.vim/misc/sasvimrc b/.vim/misc/sasvimrc
new file mode 100644 (file)
index 0000000..0ff2632
--- /dev/null
@@ -0,0 +1,1106 @@
+" vim: set ff=unix ai si noet ts=4 sw=4 fdls=1 fdm=marker wrap tw=0 sm mat=3 spell tags=$HOME/.vim/vimrc.tags :
+"
+" Terry M P<SNIP>, personal vimrc file
+"
+"      $Id: .vimrc,v 1.11 2008/11/14 07:42:37 Terry Exp $:
+"
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+"    WARNING !!!
+" This file must have UNIX end of lines to avoid errors when starting
+" under unix. Use the pdos2unix and punix2dos sh scripts to convert or
+"
+" perl -p -e 's/\n/\r\n/' inputfile > outputfile  # UNIX to DOS
+" perl -p -e 's/\r\n/\n/' inputfile > outputfile  # DOS to UNIX
+"
+" on the command line or vim it self.
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+" Table of Contents
+" |SETTINGS|                   ---     Settings
+"      |GENERAL|                       ---     General options
+"      |FORMATS|                       ---     Configuration for various file formats
+"              |AUTOCMDS|              ---     Auto commands for calling format handler functions
+"              |HANDLERS|              --- Handler functions for various file formats
+"      |INTERFACE|                     ---     Interface specific settings
+" |BINDS|                              ---     Common Bindings
+"      |FUNCTIONS|                     ---     Global helper functions
+"      |COMMANDS|                      --- Normal mode commands
+"      |MAPS|                          --- Key mappings
+" |PLUGINS|                            --- Plugin specific configuration
+"      |Taglist|
+"      |NERDTree|
+" |play|                               --- Misc stuff, usually notes / code snippets
+"
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+" Preferences
+"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+
+" *SETTINGS* {{{
+
+" *GENERAL* {{{
+
+       " Don't be VI compatible - Keep this at top of file !
+       " Also skip loading .exrc and friend sin the CWD.
+       set nocompatible
+       set noexrc
+
+       " highlight matched text when searching
+       "if has('extra_search')
+       "       set hlsearch
+       "endif
+
+       " Ignore case when searching unless it starts with a Capitol
+       set ignorecase
+       set smartcase
+
+       " Use the system clipboard as the default register, '*'
+       if has("clipboard")
+               set clipboard=unnamed,exclude:cons\|linux
+       endif
+
+       " Make join commands smarter
+       set joinspaces
+
+       " Kill visual bell
+       set visualbell t_vb=
+
+       " Allow names with some spaces in path, like when using gf
+       set isfname+=32
+
+       " Show matching symbols Vi style
+       set showmatch
+       set matchtime=3
+       set matchpairs=(:),{:},[:],<:>
+
+       " How to display tabs when list is on and expand tab is off
+       set listchars=tab:\|\
+       set nolist
+
+       " Physical word wrapping
+       set textwidth=78
+
+       " Allow backspacing over everything in insert mode
+       set backspace=indent,eol,start
+
+       " Allow keys to move to next logical line marker at EOL
+       set whichwrap=b,s,h,l,<,>,[,],~
+
+       " Set folding
+       if has("folding")
+               set foldlevelstart=99
+               set foldmethod=indent
+       endif
+
+       " Indentation settings (explictly)
+       set tabstop=8
+       set shiftwidth=8
+       set noexpandtab
+
+       " Set minimal length of line numbering
+       set numberwidth=2
+
+       " Set tab menu 0=never, 1=when more then one, 2=always
+       set showtabline=1
+
+       " Preferred file formats, ffs
+       set fileformat=unix
+       set fileformats=unix,dos,mac
+
+
+       " Show status bar with file name e.t.c.
+       " 0 = never, 1 = if >= 2 window, 2 = always
+       set laststatus=2
+
+       " Show % of file in status bar
+       if has("cmdline_info")
+               set ruler
+       endif
+
+       " Vim command line completion lists possible matches
+       set wildmode=list:full
+
+       " Start with mouse support in all modes
+       set mouse=a
+       " Hide mouse pointer when typing in text
+       set mousehide
+
+       " Allow split windows to shrink to this size
+       set winminheight=0
+
+       " Set spell checker language
+       if exists("+spell")
+               set spelllang=en_us,en_gb,de
+               set spell
+               " disable highlighting
+               " XXX see |MAPS| for a mapping to toggle this highlighting
+               set hl+=B:none,P:none,R:none,L:none
+       endif
+
+       " Show function prototypes when doing ins-completion
+       set showfulltag
+
+       " Settings for omni completion
+       if exists("+omnifunc")
+               set completeopt=menu
+               set infercase
+               "Enable syntax file based omni completion when no other method is set
+               if has("autocmd")
+                       autocmd Filetype *
+                       \       if &omnifunc == "" |
+                       \               setlocal omnifunc=syntaxcomplete#Complete |
+                       \       endif
+               endif
+       endif
+
+       " Allow cursor to move virtually in =mode or =onemore past the EOL
+       "set virtualedit=onemore
+
+       " UNIX: Include system tags file
+       if has("unix")
+               set tags+=~/.vim/systags
+       endif
+
+       " Enable filetypes and plugins
+       filetype plugin indent on
+
+       "Enable syntax related features
+       if has("syntax")
+               syntax on
+       endif
+
+       " Automatically change to directories if possible.
+       if has("netbeans_intg") || has("sun_workshop")
+               set autochdir
+       elseif has("autocmd")
+               " Mimic the autochdir option with a function and an autocmd
+               function! My_Autochdir()
+                       exec "cd %:p:h"
+               endfunction
+               autocmd BufEnter * call My_Autochdir()
+       endif
+
+" !GENERAL }}}
+
+" *FORMATS* {{{
+
+" *AUTOCMDS* {{{
+
+
+       if has("autocmd")
+"      XXX Document based formats
+               " make *.txt dos/win friendly
+               autocmd BufNewFile *.txt set ff=dos
+               autocmd BufNewFile,BufRead *.txt call TextFileHandler()
+               autocmd BufNewFile *.rtf call RtfFileHandler()
+               autocmd BufRead *.rtf call RtfFileHandler()
+               autocmd BufRead *.doc call DocFileHandler()
+               autocmd BufRead *.pdf call PdfFileHandler()
+               autocmd BufRead *.ps call PsFileHandler()
+               autocmd BufRead *.dvi call DviFileHandler()
+               autocmd BufNewFile,BufRead *.outline call My_OutlineMode()
+"      XXX Image file based formats
+               autocmd BufRead *.jpg,*.jpeg,*.png,*.tiff,*.bmp,*.gif call ImgFileHandler()
+               autocmd BufRead *.xpm call XImgFileHandler()
+"      XXX Code based formats
+               autocmd filetype ada call AdaFileHandler()
+               autocmd filetype asm call AsmFileHandler()
+               autocmd BufNewFile,BufRead *.c,*.h call CFileHandler()
+               autocmd filetype css call CSSFileHandler()
+               autocmd BufNewFile,BufRead *.cc,*.cxx,*.cpp,*.hh,*.hpp,*.hxx call CXXFileHandler()
+               autocmd BufNewFile,BufRead *.d call DFileHandler()
+               autocmd BufNewFile,BufRead *.el call ELispFileHandler()
+               autocmd BufNewFile,BufRead *.html,*.xhtml call HtmlFileHandler()
+               autocmd filetype java call JavaFileHandler()
+               autocmd filetype javascript call JavaScriptFileHandler()
+               autocmd BufNewFile,BufRead *.lua call LuaFileHandler()
+               autocmd filetype make call MakefileFileHandler()
+               autocmd filetype perl call PerlFileHandler()
+               autocmd filetype php call PhpFileHandler()
+               autocmd filetype python call PythonFileHandler()
+               autocmd filetype ruby call RubyFileHandler()
+               autocmd filetype scheme call SchemeFileHandler()
+               autocmd filetype sh call ShellFileHandler()
+               autocmd filetype sql call SqlFileHandler()
+               autocmd filetype tex call TexFileHanlder()
+               autocmd filetype nroff call TroffFileHandler()
+               autocmd filetype vim call VimFileHanlder()
+               autocmd filetype vb  call VisualBasicFileHandler()
+"      XXX for use with other programs
+               autocmd BufNewFIle,BUfRead *.tmp call My_KNodeConfig()
+       endif
+
+" !AUTOCMDS }}}
+
+" *HANDLERS* {{{
+
+       function! PreHandlerHook()
+               setl autoindent
+               if exists("+spell")
+                       setl spell
+               endif
+       endfunction
+       function! PostHandlerHook()
+       endfunction
+
+"      XXX Document based formats
+       function! TextFileHandler()
+               call PreHandlerHook()
+
+               " turn off some stuff when viewing Vim help files
+               if &ft == "help"
+                       if exists("+spell")
+                               setl nospell
+                       endif
+               endif
+               setl fdm=expr
+               set fo+=tqn
+               set ai
+               " fixes gq from using c-keyword based indentation
+               set cinwords=""
+
+               call PostHandlerHook()
+       endfunction
+
+       function! RtfFileHandler()
+               call PreHandlerHook()
+
+               let l:prompt = input("View or edit % [yes|no]?: ")
+               if l:prompt=="y" || l:prompt=="yes" || l:prompt=="Y" || l:prompt=="YES"
+                       !{abiword "%" || wordpad.exe "%" || kword "%" || swriter "%"}  &
+                       q
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       function! DocFileHandler()
+               call PreHandlerHook()
+
+               if has("gui_running") ||  has("win32")
+                       !{abiword "%" || kword "%" || swriter "%"}  &
+                       q
+               else
+                       r!antiword -tf  "%s"
+               endif
+               call PostHandlerHook()
+       endfunction
+
+       function! PdfFileHandler()
+               call PreHandlerHook()
+
+               if has("x11")
+                       !{evince "%" || kpdf "%" || xpdf "%"} &
+                       q
+               elseif has("win32")
+                       !"Foxit Reader.exe" "%" &
+                       q
+               else
+                       echo "No text dump program setup available...."
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       function! PsFileHandler()
+               call PreHandlerHook()
+
+               let l:prompt = input("View or edit % [yes|no]?: ")
+               if l:prompt=="y" || l:prompt=="yes" || l:prompt=="Y" || l:prompt=="YES"
+                       if has("x11")
+                               !{evince "%" || kpdf "%" || xpdf "%"} &
+                               q
+                       elseif has("win32")
+                               echo "Whats the ps viewer?"
+                       else
+                               echo "No text dump program installed...."
+                       endif
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       function! DviFileHandler()
+               call PreHandlerHook()
+
+               let l:prompt = input("View % in Vim [yes|no]?: ")
+               if l:prompt=="y" || l:prompt=="yes" || l:prompt=="Y" || l:prompt=="YES"
+                       " depends on the dvi2tty program
+                       !dvi2tty -F'vim -c "setl ro fdl=99"  -' "%"
+                       q
+               else
+                       if has("x11")
+                               !{xdvi "%" || kdvi "%" ||  "%"} &
+                               q
+                       elseif has("win32")
+                               !echo "Go look up how to open DVI on TexLive Win"
+                       endif
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+"      XXX Image file based formats
+       function! ImgFileHandler()
+               call PreHandlerHook()
+
+               if has("x11")
+                       !xv "%" &
+                       q
+               elseif has("win32")
+                       !\%systemroot\%\system32\rundll32.exe \%systemroot\%\system32\shimgvw.dll,imageview_fullscreen  "%" || \%systemroot\%\system32\mspaint.exe "%"
+                       q
+               else
+                       echo "no external viewer for text mode set...."
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       function! XImgFileHandler()
+               call PreHandlerHook()
+
+               let l:prompt = input("View or edit % [yes|no]?: ")
+               if l:prompt=="y" || l:prompt=="yes" || l:prompt=="Y" || l:prompt=="YES"
+                       if has("x11")
+                               !xv "%" &
+                               q
+                       endif
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+"      XXX Code based formats
+       function! AdaFileHandler()
+               call PreHandlerHook()
+
+               call PostHandlerHook()
+       endfunction
+
+       function! AsmFileHandler()
+               call PreHandlerHook()
+
+               if exists("+spell")
+                       setl nospell
+               endif
+               call PostHandlerHook()
+       endfunction
+
+       function! CFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=8 shiftwidth=8 noexpandtab
+               if has("folding")
+                       setl foldmethod=syntax foldcolumn=1
+               endif
+               setl matchpairs-=<:>
+
+               if has("cindent")
+                       " modify cindent to understand my switch...case and paren style
+                       setl cinoptions+=:2,=2,g2,(0
+                       setl cinkeys-=0#
+               endif
+
+               " highlight GNU Compiler stuff
+               let c_gnu=1
+               " highlight preceding spaces before a tab as an error
+               let c_space_errors=1
+               let c_no_trail_space_error=1
+               " use C syntax in *.h rather then C++ syntax
+               if has("syntax")
+                       let c_syntax_for_h=1
+
+                       " allow doxygen highlighting
+                       set syntax=c.doxygen
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       function! CSSFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=5 shiftwidth=5 expandtab
+               if has("folding")
+                       setl foldmethod=indent
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       function! CXXFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=4 shiftwidth=4 expandtab
+               if has("folding")
+                       setl foldmethod=syntax foldcolumn=1
+               endif
+               setl matchpairs-=<:>
+
+               if has("cindent")
+                       " modify cindent to understand my switch...case / class member
+                       " access specifier / paren styles and not force #directives into
+                       " col 1
+                       setl cinoptions+=:2,=2,g2,h2,(0,#1
+                       setl cinkeys-=0#
+               endif
+
+               if has("syntax")
+                       " allow doxygen highlighting
+                       set syntax=cpp.doxygen
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       function! DFileHandler()
+               call PreHandlerHook()
+               call PostHandlerHook()
+       endfunction
+
+       function! ELispFileHandler()
+               call PreHandlerHook()
+               call PostHandlerHook()
+       endfunction
+
+       function! HtmlFileHandler()
+               call PreHandlerHook()
+
+               if has("syntax") && &t_Co > 2
+                       syntax on
+                       colo elflord
+               endif
+               setl tabstop=2 shiftwidth=2 expandtab
+               if has("folding")
+                       setl foldmethod=indent
+               endif
+               filetype indent on
+               if exists("+omnifunc")  " think twice about using this with other langs
+                       imap </ </<c-x><c-o>
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       function! JavaFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=4 shiftwidth=4 expandtab
+               if has("folding")
+                       setl foldmethod=indent
+               endif
+               setl matchpairs-=<:>
+               if exists("+spell")
+                       setl nospell " FUBAR in Java
+               endif
+               " Highlight all identifiers in java.lang.*
+               let java_highlight_java_lang_ids=1
+
+               call PostHandlerHook()
+       endfunction
+
+       function! JavaScriptFileHandler()
+               call PreHandlerHook()
+
+               call PostHandlerHook()
+       endfunction
+
+       function! LuaFileHandler()
+               call PreHandlerHook()
+               call PostHandlerHook()
+       endfunction
+
+       function! MakefileFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=4 shiftwidth=4 noexpandtab
+               if has("folding")
+                       setl foldmethod=indent
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       function! PerlFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=4 shiftwidth=4 noexpandtab
+               if has("folding")
+                       setl foldmethod=indent
+                       let perl_fold=1
+                       let perl_fold_blocks=1
+               endif
+               setl matchpairs-=<:>
+
+               call PostHandlerHook()
+       endfunction
+
+       function! PhpFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=4 shiftwidth=4 expandtab
+               if has("folding")
+                       setl foldmethod=indent
+                       " allow code folding for classes and functions!
+                       let php_folding=1
+               endif
+               setl matchpairs-=<:>
+               " highlight HTML tags within strings
+               let php_htmlInStrings=1
+               " disable short tags
+               let php_noShortTags=1
+
+               call PostHandlerHook()
+       endfunction
+
+       function! PythonFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=4 shiftwidth=4 expandtab
+               if has("folding")
+                       setl foldmethod=marker
+               endif
+               setl matchpairs-=<:>
+               setl keywordprg=pydoc
+
+               let python_highlight_all=1
+               " slightly smarter indenting for (code\n morecode) situations
+               let g:pyindent_open_paren = '&sw + 1'
+
+               call PostHandlerHook()
+       endfunction
+
+       function! RubyFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=2 shiftwidth=2 expandtab
+               if has("folding")
+                       setl foldmethod=syntax
+                       let ruby_fold=1
+               endif
+               setl matchpairs-=<:>
+               filetype indent on
+               setl keywordprg=ri
+
+               call PostHandlerHook()
+       endfunction
+
+       function! SchemeFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=4 shiftwidth=4 expandtab
+               if has("folding")
+                       setl foldmethod=syntax
+               endif
+               setl matchpairs-=<:>
+               if exists("+spell")
+                       setl nospell " FUBAR in Scheme
+               endif
+               call PostHandlerHook()
+       endfunction
+
+       function! ShellFileHandler()
+               call PreHandlerHook()
+
+               setl tabstop=4 shiftwidth=4 expandtab
+               if has("folding")
+                       setl foldmethod=indent
+                       let g:sh_fold_enabled=8
+               endif
+               setl matchpairs-=<:>
+
+               call PostHandlerHook()
+       endfunction
+
+       function! SqlFileHandler()
+               call PreHandlerHook()
+               " We almost always work with MySQL, so default to it.
+               SQLSetType mysql
+               setl tabstop=2 shiftwidth=2 expandtab
+               setl matchpairs-=<:>
+
+               call PostHandlerHook()
+       endfunction
+
+       function! TexFileHanlder()
+               call PreHandlerHook()
+               if has("syntax") && &t_Co > 2
+                       syntax on
+                       colo elflord
+               endif
+               setl fileformat=dos
+               setl tabstop=4 shiftwidth=4 softtabstop=4 noexpandtab
+               if has("folding")
+                       setl foldmethod=indent
+                       " syntax-based folding of parts, chapters, sections, etc
+                       let g:tex_fold_enabled=1
+               endif
+               setl matchpairs-=<:>
+               " fixes gq from using c-keyword based indentation
+               set cinwords=""
+
+               call PostHandlerHook()
+       endfunction
+
+       function! TroffFileHandler()
+               call PreHandlerHook()
+
+               setl matchpairs-=<:>
+               if exists("+spell")
+                       setl nospell " FUBAR in Java
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       function! VimFileHanlder()
+               call PreHandlerHook()
+
+               if has("syntax") && &t_Co > 2
+                       syntax on
+                       colo elflord
+               endif
+               setl fileformat=unix
+               setl tabstop=4 shiftwidth=4 noexpandtab
+               if has("folding")
+                       setl foldmethod=marker
+               endif
+               setl matchpairs-=<:>
+
+               call PostHandlerHook()
+       endfunction
+
+       function! VisualBasicFileHandler()
+               call PreHandlerHook()
+
+               "setl fileformat=unix
+               setl matchpairs-=<:>
+
+               call PostHandlerHook()
+       endfunction
+
+"      XXX for use with other programs
+       " Settings for outlining ideas
+       function! My_OutlineMode()
+               call PreHandlerHook()
+
+               setl tabstop=8 shiftwidth=8 noexpandtab
+               setl listchars =tab:\|\ " Mark \t's with |'s
+               setl list
+               if exists("+spell")
+                       setl spell
+               endif
+               setl autoindent
+               if has("smartindent")
+                       setl smartindent
+               endif
+               setl showmatch matchtime=3
+               setl matchpairs+=(:),{:},[:],<:>
+               if has("folding")
+                       " Fold by tabs
+                       "setl foldmethod=expr
+                       "setl foldexpr=getline(v:lnum)[0]==\"\\t\"
+                       " Fold by braces
+                       setl foldmethod=marker
+                       setl foldmarker={,}
+               endif
+
+               call PostHandlerHook()
+       endfunction
+
+       " For the .tmp files created by knode
+       function! My_KNodeConfig()
+               call PreHandlerHook()
+
+               setl textwidth=0 wrap
+               if exists("+spell")
+                       setl spell
+               endif
+               setl autoindent
+               if has('smartindent')
+                       smartindent
+               endif
+               if has("folding")
+                       setl foldmethod=indent
+               endif
+               set ft=mail
+
+               call PostHandlerHook()
+       endfunction
+
+" !HANDLERS }}}
+
+" !FORMATS }}}
+
+" *INTERFACE* {{{
+
+
+       "set :command line height in lines
+       if has("gui_running")
+               set cmdheight=2
+       else
+               set cmdheight=1
+       endif
+
+
+       " GUI: Set options mainly used for for GVim
+
+       " autoselect, console dialogs, tabpages, icon, menubar, show grey menu
+       " items, tearoff menu items, Toolbar, always right scrollbar.
+       set guioptions=aceimgtTr
+
+       " Right click menu lists correction options when spell is set
+       set mousemodel=popup_setpos
+
+       " Make mouse pointer an I-Beam in insert mode like most other programs
+       set mouseshape=i:beam
+
+
+       " X11: Specifics for GVim
+       if has("x11")
+               "I love this font!!
+               set guifont=Terminus\ 14
+               "set lines=40
+               "set columns=80
+
+       " WINDOWS: Specifics for GVim
+       elseif has("win32")
+               set guifont=Fixedsys:h11:cANSI
+               set lines=25
+               set columns=80
+
+       endif
+
+       function! SetGuiColo() " {{{            XXX DEPRECIATED
+
+               if exists("*strftime")
+                       let s:random = strftime("%S")
+               else " fall through to the else
+                       let s:random = 61
+               endif
+
+               if s:random < 2
+                       colo ron
+
+               elseif s:random < 4
+                       colo candycode
+
+               elseif s:random < 6
+                       colo silent
+
+               elseif s:random < 8
+                       colo default
+
+               elseif s:random < 12
+                       colo denim
+
+               elseif s:random < 14
+                       colo midnight2
+
+               elseif s:random < 16
+                       colo hhazure
+
+               elseif s:random < 18
+                       colo rootwater
+
+               elseif s:random < 20
+                       colo nightshimmer
+
+               elseif s:random < 22
+                       colo murphy
+
+               elseif s:random < 24
+                       colo coffee
+
+               elseif s:random < 26
+                       colo professional
+
+               elseif s:random < 28
+                       colo sf
+
+               elseif s:random < 30
+                       colo my
+
+               elseif s:random < 32
+                       colo vcbc
+
+               elseif s:random < 34
+                       colo gobo
+
+               elseif s:random < 36
+                       colo metacosm
+
+               elseif s:random < 38
+                       colo blackboard
+
+               elseif s:random < 40
+                       colo billw
+
+               elseif s:random < 42
+                       colo dusk
+
+               elseif s:random < 44
+                       colo desert
+
+               elseif s:random < 46
+                       colo evening
+
+               elseif s:random < 48
+                       colo golden
+
+               elseif s:random < 50
+                       colo chela_light
+
+               elseif s:random < 52
+                       colo camo
+
+               elseif s:random < 54
+                       colo developer
+
+               elseif s:random < 56
+                       colo neon
+
+               elseif s:random < 58
+                       colo chlordane
+
+               elseif s:random < 60
+                       colo shine
+
+               else
+                       colo default
+
+               endif
+       endfunction " }}}
+
+       function! SetConsoleColo() "{{{         XXX DEPRECIATED
+
+
+               " XXX override this for now
+               colo none
+
+               return
+               if exists("*strftime")
+                       let s:random = strftime("%S")
+               else " fall through to the else
+                       let s:random = 61
+               endif
+
+               if s:random < 2
+                       colo astronaut
+
+               elseif s:random < 4
+                       colo redblack
+
+               elseif s:random < 6
+                       colo default
+
+               elseif s:random < 8
+                       colo golden
+
+               elseif s:random < 12
+                       colo candycode
+
+               elseif s:random < 14
+                       colo murphy
+
+               elseif s:random < 16
+                       colo my
+
+               elseif s:random < 18
+                       colo c
+
+               elseif s:random < 20
+                       colo elflord
+
+               elseif s:random < 22
+                       colo relaxedgreen
+
+               elseif s:random < 24
+                       colo astronaut
+
+               elseif s:random < 26
+                       colo redblack
+
+               elseif s:random < 28
+                       colo default
+
+               elseif s:random < 30
+                       colo golden
+
+               elseif s:random < 32
+                       colo candycode
+
+               elseif s:random < 34
+                       colo elflord
+
+               elseif s:random < 36
+                       colo c
+
+               elseif s:random < 38
+               elseif s:random < 40
+               elseif s:random < 42
+                       colo relaxedgreen
+
+               elseif s:random < 44
+                       colo redblack
+
+               elseif s:random < 46
+                       colo default
+
+               elseif s:random < 48
+                       colo candycode
+
+               elseif s:random < 50
+                       colo golden
+
+               elseif s:random < 52
+                       colo relaxedgreen
+
+               elseif s:random < 54
+                       colo astronaut
+
+               elseif s:random < 56
+                       colo my
+
+               elseif s:random < 58
+                       colo astronaut
+
+               elseif s:random < 60
+                       colo murphy
+
+               else
+                       colo default
+
+               endif
+
+       endfunction " }}}
+
+       if !has("gui_running")
+               call SetConsoleColo()
+       endif
+
+
+" !INTERFACE }}}
+
+" !SETTINGS }}}
+
+" |BINDS| {{{
+
+" *FUNCTIONS* {{{
+
+" XXX: Be sure to add ! to functions and commands in order to overwrite
+" the definitions if this file is resourced after startup.
+
+
+" Make current file executable
+function! ChmodScript()
+       silent! execute ":!chmod +x %"
+endfunction
+command! Chmod call ChmodScript()
+
+" ROT13 encode/decode the current file
+function! ROT13()
+       <esc>ggvGg?
+endfunction
+
+" !FUNCTIONS }}}
+
+" *MAPS* {{{
+
+"""" WARNING !!!
+"""" NEVER MAP THE BACKSPACE KEY OR A MODIFIER
+
+       " Use ";" as an alias for ":" -> it makes life so much easier
+       nnoremap ; :
+
+       " show more data when we ask for a line count
+       nnoremap <C-g> <C-g>2<C-g>
+
+       " clear search highlighting when redrawing the screen
+       nnoremap <silent> <C-l> :nohl<CR><C-l>
+
+       " Make Escape toggle normal/insert mode -> I don't like this!
+       "nnoremap <silent> <esc> :startinsert<CR>
+
+       " Combine [s and ]s with z=
+       " (e.g. move to miss spelled word in direction and get suggestions)
+       nnoremap [z [sz=
+       nnoremap ]z ]sz=
+
+       " toggle spelling highlights on/off
+       nnoremap <leader>Z :set hl&<cr>
+       nnoremap <leader>z :set hl+=B:none,P:none,R:none,L:none<cr>
+
+       " swap words under cursor with \xp or \gw (where \ = <leader>
+       noremap <silent> <leader>xp "_yiw:s/\(\%#\w\+\)\(\W\+\)\(\w\+\)/\3\2\1/<cr><c-o><c-l>
+       noremap <silent> <leader>gw <leader>xp
+
+       " Use emacs style movement commands in insert mode
+       " XXX: these redine Vim maps, ctrl-a => useless, ctrl-e => useful
+       "inoremap <c-a> <esc>I
+       "inoremap <c-e> <esc>A
+
+       " F1 toggles spell checking for this buffer
+       if exists("+spell")
+               inoremap <F1> <esc>:set spell! <bar> set spell?<CR>i
+               nnoremap <F1> <esc>:set spell! <bar> set spell?<CR>
+       endif
+
+       " F2 toggles  line numbers
+       nnoremap <F2> :set number! <bar> set number?<CR>
+       inoremap <F2> <esc>:set number! <bar> set number?<CR>i
+
+       " F3 turns on  auto indentations
+       nnoremap <F3> :set smartindent autoindent<CR>
+       inoremap <F3> <esc>:set smartindent autoindent<CR>i
+
+       " XXX make this toggle the nerd tree plugin
+       " F5 launches file manager in the current directory
+       nnoremap <F5> :OPEN %:p:h<CR>
+       inoremap <F5> <esc>:OPEN %:p:h<CR>i
+
+" !KEYS }}}
+
+" !BINDS }}}
+
+" *PLUGINS* {{{
+
+" *Taglist* {{{
+
+       map <silent><leader>tl :TlistToggle<CR>
+       let g:Tlist_Auto_Open=0
+       let g:Tlist_GainFocus_On_ToggleOpen=1
+       let g:Tlist_Enable_Fold_Column=0
+       let g:Tlist_Exit_OnlyWindow=1
+       let g:Tlist_Display_Prototype=1
+       let g:Tlist_Show_Menu=1
+
+" !Taglist }}}
+
+" *NERDTree* {{{
+
+" !NERDTree }}}
+
+" !PLUGINS }}}
+
+
+
+""""" *play* {{{
+" Beautify omnicompletion popup
+"highlight Pmenu guifg=white guibg=blue ctermfg=white ctermbg=blue
+
+" Fold paragraphs
+"if has("folding")
+"      set foldexpr=getline(v:lnum)=~'^\\s*$'&&getline(v:lnum+1)=~'\\S'?'<1':1
+"endif
+
+       if exists("*strftime")
+               iabbrev insert_date <c-r>=strftime("%Y-%m-%d %H:%M:%S")<cr>
+       endif
+
+       iabbrev teh the
+       iabbrev swtich switch
+
+""""" !play }}}
diff --git a/.vim/misc/unix.vim b/.vim/misc/unix.vim
new file mode 100644 (file)
index 0000000..f766e74
--- /dev/null
@@ -0,0 +1,3 @@
+" Settings for test script execution
+" Always use "sh", don't use the value of "$SHELL".
+set shell=sh
diff --git a/.vim/plugin/matrix.vim b/.vim/plugin/matrix.vim
new file mode 100644 (file)
index 0000000..9ed838d
--- /dev/null
@@ -0,0 +1,325 @@
+" matrix.vim - Don Yang (uguu.org)
+"
+" Matrix screensaver for VIM.
+"
+"Usage:
+" After loading the script, use :Matrix to start.
+" Press any key a few times to exit.
+"
+" You will need to edit s:mindelay and s:maxdelay below to match your
+" machine speed and window size.
+"
+"Known Issues:
+" Sometimes you need to press keys a few times to exit instead of just
+" once.  Press and hold is another way to go... feels like getchar is
+" checking for keypress state instead of keystroke availability.
+"
+" If the window is too small, script will not run.  If the window is
+" resized and become too small (less than 8 rows or 10 columns) after
+" the script started, script will abort and *buffers may be lost*, so
+" don't do that.  Resizing the window to most other sizes will be fine.
+"
+" Doesn't work if multiple windows exist before script started.  In
+" that case the script will abort with error message.
+"
+" If the current buffer is modified, some error messages will appear
+" before the script starts, and an extra window is left behind after
+" the script exits.  Workaround: save your buffers first.
+"
+"Other Info:
+" Inspired by cmatrix...
+" Didn't feel inspired enough to start using pico/nano, of course ^_^;
+"
+" 05/13/08 - disable cursorline, cursorcolumn and spell
+"            (thanks to Diederick Niehorster for the suggestion).
+" 12/21/06 - multiwindow support by S. Lockwood-Childs.
+" 10/03/05 - added silent! to cursor positioning code to stop drawing
+"            numbers during animation (thanks to David Eggum for the
+"            suggestion).
+" 10/02/05 - disable showmatch
+" 03/16/05 - make new buffer modifiable before running
+" 01/27/05 - added sleep to consume less CPU
+"            removed frame counter
+" 01/26/05 - initial version
+
+
+" Speed range, must be positive.  Lower delay = faster.
+let s:mindelay = 1
+let s:maxdelay = 5
+
+" Session file for preserving original window layout
+let s:session_file = tempname()
+
+
+function! s:Rand()
+   let b:seed = b:seed * 22695477 + 1
+   if b:seed < 0
+      return -b:seed
+   endif
+   return b:seed
+endfunction
+
+function! s:CreateObject(i)
+   while 1
+      let b:x{a:i} = s:Rand() % b:columns
+      if b:reserve{b:x{a:i}} > 4
+         break
+      endif
+   endwhile
+   let b:y{a:i} = 1
+   let b:t{a:i} = s:Rand() % b:s{b:x{a:i}}
+   let b:head{a:i} = s:Rand() % 4
+   let b:len{a:i} = s:Rand() % b:h + 3
+   let b:reserve{b:x{a:i}} = 1 - b:len{a:i}
+endfunction
+
+function! s:DrawObject(i)
+   let x = b:x{a:i} * 2 + 1
+   let y = b:y{a:i}
+
+   " Draw head
+   if y <= b:h
+      if b:head{a:i}
+         silent! exec 'norm! :' . y . nr2char(13) . x . '|R' . b:d[s:Rand()%b:dl] . '_' . nr2char(27)
+         if y > 1
+            silent! exec 'norm! kR' . ((s:Rand() % 2) ? '`' : ' ') . nr2char(27)
+         endif
+      else
+         let a = ((s:Rand() % 2) ? '`' : ' ') . nr2char(27)
+         silent! exec 'norm! :'. y . nr2char(13) . x . '|R' . b:d[s:Rand() % b:dl] . a
+      endif
+   else
+      if b:head{a:i} && y == b:h + 1
+         silent! exec 'norm! :' . b:h . nr2char(13) . (x + 1) . '|R' . ((s:Rand() % 2) ? '`' : ' ') . nr2char(27)
+      endif
+   endif
+
+   " Draw tail
+   let y = y - b:len{a:i}
+   if 1 <= y && y <= b:h
+      silent! exec 'norm! :'. y . nr2char(13) . x . '|R  ' . nr2char(27)
+   endif
+   let b:reserve{b:x{a:i}} = y
+endfunction
+
+function! s:Animate()
+   let i = 0
+
+   while i < b:objcount
+      " Animate object
+      if b:t{i} <= 0
+         if b:y{i} - b:len{i} <= b:h
+            " Draw
+            call s:DrawObject(i)
+            let b:t{i} = b:s{b:x{i}}
+            let b:y{i} = b:y{i} + 1
+         else
+            " Regenerate
+            call s:CreateObject(i)
+         endif
+      endif
+
+      let b:t{i} = b:t{i} - 1
+      let i = i + 1
+   endwhile
+   redraw
+   if getchar(1)
+      let b:run = 0
+   endif
+   sleep 20m
+endfunction
+
+function! s:Reset()
+   " Clear screen
+   let b:w = winwidth(0)
+   let b:h = winheight(0)
+   exec 'norm! gg"_dG' . b:h . 'O' . nr2char(27) . 'gg'
+   redraw
+   if b:w < 10 || b:h < 8
+      let b:run = 0
+      return
+   endif
+
+   " Set number of columns.  This is rounded down due to line wrapping
+   " at the last column if the screen width is even.  So you end up
+   " seeing the cursor blinking a lot at the right side of the screen.
+   " Alternatively, ':set rl' before running the script to have it
+   " blink on the left side.
+   let b:columns = (b:w - 1) / 2
+
+   " Initialize columns.
+   let i = 0
+   while i < b:columns
+      " Set delay time.  Each column gets the same delay time.
+      let b:s{i} = s:Rand() % (s:maxdelay - s:mindelay) + s:mindelay
+
+      " Unreserve column
+      let b:reserve{i} = b:h
+      let i = i + 1
+   endwhile
+
+   " Initialize objects
+   let b:objcount = b:columns - 2
+   let i = 0
+   while i < b:objcount
+      call s:CreateObject(i)
+      let i = i + 1
+   endwhile
+endfunction
+
+function! s:Init()
+   " Create new buffer and hide the existing buffers.  Hiding the
+   " existing buffers without switching to a new buffer preserves
+   " undo history.
+   exec 'mksession! ' . s:session_file
+   let s:num_orig_win = winnr("$")
+
+   " move to top window, so created window will become window 1,
+   " then attempt to create new window
+   1 wincmd w
+   silent! new
+
+   " check that there really is an additional window
+   if winnr("$") != s:num_orig_win + 1
+      return 1
+   endif
+   let s:newbuf = bufnr('%')
+
+   " close all but window 1, which is the new window
+   only
+
+   setl bh=delete bt=nofile ma nolist nonu noro noswf tw=0 nowrap
+
+   " Set GUI options
+   if has('gui')
+      let s:o_gcr = &gcr
+      let s:o_go = &go
+      set gcr=a:ver1-blinkon0 go=
+   endif
+   if has('cmdline_info')
+      let s:o_ru = &ru
+      let s:o_sc = &sc
+      set noru nosc
+   endif
+   if has('title')
+      let s:o_ts = &titlestring
+      exec 'set titlestring=\ '
+   endif
+   if v:version >= 700
+      let s:o_spell = &spell
+      let s:o_cul = &cul
+      let s:o_cuc = &cuc
+      set nospell nocul nocuc
+   endif
+   let s:o_ch = &ch
+   let s:o_ls = &ls
+   let s:o_lz = &lz
+   let s:o_siso = &siso
+   let s:o_sm = &sm
+   let s:o_smd = &smd
+   let s:o_so = &so
+   let s:o_ve = &ve
+   set ch=1 ls=0 lz nosm nosmd siso=0 so=0 ve=all
+
+   " Initialize PRNG
+   let b:seed = localtime()
+   let b:run = 1
+
+   " Clear screen and initialize objects
+   call s:Reset()
+
+   " Set colors.  Output looks better if your color scheme has black
+   " background.  I would rather not have the script change the
+   " current color scheme since there is no good way to restore them
+   " afterwards.
+   hi MatrixHidden ctermfg=Black ctermbg=Black guifg=#000000 guibg=#000000
+   hi MatrixNormal ctermfg=DarkGreen ctermbg=Black guifg=#008000 guibg=#000000
+   hi MatrixBold ctermfg=LightGreen ctermbg=Black guifg=#00ff00 guibg=#000000
+   hi MatrixHead ctermfg=White ctermbg=Black guifg=#ffffff guibg=#000000
+   sy match MatrixNormal /^.*/ contains=MatrixHidden
+   sy match MatrixHidden contained /.`/ contains=MatrixBold
+   sy match MatrixHidden contained /._/ contains=MatrixHead
+   sy match MatrixBold contained /.\(`\)\@=/
+   sy match MatrixHead contained /.\(_\)\@=/
+
+   " Create random char dictionary
+   let b:d = ''
+   let i = 33
+   while i < 127
+      if i != 95 && i != 96
+         let b:d = b:d . nr2char(i)
+      endif
+      let i = i + 1
+   endwhile
+   let b:dl = strlen(b:d)
+   return 0
+endfunction
+
+function! s:Cleanup()
+   " Restore options
+   if has('gui')
+      let &gcr = s:o_gcr
+      let &go = s:o_go
+      unlet s:o_gcr s:o_go
+   endif
+   if has('cmdline_info')
+      let &ru = s:o_ru
+      let &sc = s:o_sc
+      unlet s:o_ru s:o_sc
+   endif
+   if has('title')
+      let &titlestring = s:o_ts
+      unlet s:o_ts
+   endif
+   if v:version >= 700
+      let &spell = s:o_spell
+      let &cul = s:o_cul
+      let &cuc = s:o_cuc
+      unlet s:o_cul s:o_cuc
+   endif
+   let &ch = s:o_ch
+   let &ls = s:o_ls
+   let &lz = s:o_lz
+   let &siso = s:o_siso
+   let &sm = s:o_sm
+   let &smd = s:o_smd
+   let &so = s:o_so
+   let &ve = s:o_ve
+   unlet s:o_ch s:o_ls s:o_lz s:o_siso s:o_sm s:o_smd s:o_so s:o_ve
+
+   " Restore old buffers
+   exec 'source ' . s:session_file
+   exec 'bwipe ' . s:newbuf
+   unlet s:newbuf
+
+   " Clear keystroke
+   let c = getchar(0)
+endfunction
+
+function! Matrix()
+   if s:Init()
+      echohl ErrorMsg
+      echon 'Can not create window'
+      echohl None
+      return
+   endif
+
+   while b:run
+      if b:w != winwidth(0) || b:h != winheight(0)
+         call s:Reset()
+      else
+         call s:Animate()
+      endif
+   endwhile
+
+   call s:Cleanup()
+endfunction
+
+
+if !has('virtualedit') || !has('windows') || !has('syntax')
+   echohl ErrorMsg
+   echon 'Not enough features, need at least +virtualedit, +windows and +syntax'
+   echohl None
+else
+   command! Matrix call Matrix()
+endif
diff --git a/.vim/plugin/vcsbzr.vim b/.vim/plugin/vcsbzr.vim
new file mode 100644 (file)
index 0000000..b8665b8
--- /dev/null
@@ -0,0 +1,262 @@
+" vim600: set foldmethod=marker:
+"
+" BZR extension for VCSCommand.
+"
+" Maintainer:    Bob Hiestand <bob.hiestand@gmail.com>
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+"
+" Section: Documentation {{{1
+"
+" Options documentation: {{{2
+"
+" VCSCommandBZRExec
+"   This variable specifies the BZR executable.  If not set, it defaults to
+"   'bzr' executed from the user's executable path.
+
+" Section: Plugin header {{{1
+
+if exists('VCSCommandDisableAll')
+       finish
+endif
+
+if v:version < 700
+  echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+  finish
+endif
+
+runtime plugin/vcscommand.vim
+
+if !executable(VCSCommandGetOption('VCSCommandBZRExec', 'bzr'))
+  " BZR is not installed
+  finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Variable initialization {{{1
+
+let s:bzrFunctions = {}
+
+" Section: Utility functions {{{1
+
+" Function: s:Executable() {{{2
+" Returns the executable used to invoke bzr suitable for use in a shell
+" command.
+function! s:Executable()
+       return shellescape(VCSCommandGetOption('VCSCommandBZRExec', 'bzr'))
+endfunction
+
+" Function: s:DoCommand(cmd, cmdName, statusText) {{{2
+" Wrapper to VCSCommandDoCommand to add the name of the BZR executable to the
+" command argument.
+function! s:DoCommand(cmd, cmdName, statusText, options)
+  if VCSCommandGetVCSType(expand('%')) == 'BZR'
+    let fullCmd = s:Executable() . ' ' . a:cmd
+    return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options)
+  else
+    throw 'BZR VCSCommand plugin called on non-BZR item.'
+  endif
+endfunction
+
+" Section: VCS function implementations {{{1
+
+" Function: s:bzrFunctions.Identify(buffer) {{{2
+function! s:bzrFunctions.Identify(buffer)
+  let fileName = resolve(bufname(a:buffer))
+  let l:save_bzr_log=$BZR_LOG
+  try
+    let $BZR_LOG=has("win32") || has("win95") || has("win64") || has("win16") ? "nul" : "/dev/null"
+    let statusText = s:VCSCommandUtility.system(s:Executable() . ' info -- "' . fileName . '"')
+  finally
+    let $BZR_LOG=l:save_bzr_log
+  endtry
+  if(v:shell_error)
+    return 0
+  else
+    return 1
+  endif
+endfunction
+
+" Function: s:bzrFunctions.Add() {{{2
+function! s:bzrFunctions.Add(argList)
+  return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:bzrFunctions.Annotate(argList) {{{2
+function! s:bzrFunctions.Annotate(argList)
+  if len(a:argList) == 0
+    if &filetype == 'BZRannotate'
+      " Perform annotation of the version indicated by the current line.
+      let caption = matchstr(getline('.'),'\v^\s+\zs\d+')
+      let options = ' -r' . caption
+    else
+      let caption = ''
+      let options = ''
+    endif
+  elseif len(a:argList) == 1 && a:argList[0] !~ '^-'
+    let caption = a:argList[0]
+    let options = ' -r' . caption
+  else
+    let caption = join(a:argList, ' ')
+    let options = ' ' . caption
+  endif
+
+  let resultBuffer = s:DoCommand('blame' . options, 'annotate', caption, {})
+  if resultBuffer > 0
+    normal 1G2dd
+  endif
+  return resultBuffer
+endfunction
+
+" Function: s:bzrFunctions.Commit(argList) {{{2
+function! s:bzrFunctions.Commit(argList)
+  let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {})
+  if resultBuffer == 0
+    echomsg 'No commit needed.'
+  endif
+endfunction
+
+" Function: s:bzrFunctions.Delete() {{{2
+function! s:bzrFunctions.Delete(argList)
+  return s:DoCommand(join(['rm'] + a:argList, ' '), 'rm', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:bzrFunctions.Diff(argList) {{{2
+function! s:bzrFunctions.Diff(argList)
+  if len(a:argList) == 0
+    let revOptions = []
+    let caption = ''
+  elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+    let revOptions = ['-r' . join(a:argList, '..')]
+    let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')'
+  else
+    " Pass-through
+    let caption = join(a:argList, ' ')
+    let revOptions = a:argList
+  endif
+
+  return s:DoCommand(join(['diff'] + revOptions), 'diff', caption, {'allowNonZeroExit': 1})
+endfunction
+
+" Function: s:bzrFunctions.GetBufferInfo() {{{2
+" Provides version control details for the current file.  Current version
+" number and current repository version number are required to be returned by
+" the vcscommand plugin.
+" Returns: List of results:  [revision, repository]
+
+function! s:bzrFunctions.GetBufferInfo()
+  let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+  let fileName = resolve(bufname(originalBuffer))
+  let statusText = s:VCSCommandUtility.system(s:Executable() . ' status -S -- "' . fileName . '"')
+  let revision = s:VCSCommandUtility.system(s:Executable() . ' revno -- "' . fileName . '"')
+  if(v:shell_error)
+    return []
+  endif
+
+  " File not under BZR control.
+  if statusText =~ '^?'
+    return ['Unknown']
+  endif
+
+  let [flags, repository] = matchlist(statusText, '^\(.\{3}\)\s\+\(\S\+\)')[1:2]
+  if revision == ''
+    " Error
+    return ['Unknown']
+  elseif flags =~ '^A'
+    return ['New', 'New']
+  else
+    return [revision, repository]
+  endif
+endfunction
+
+" Function: s:bzrFunctions.Info(argList) {{{2
+function! s:bzrFunctions.Info(argList)
+  return s:DoCommand(join(['version-info'] + a:argList, ' '), 'version-info', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:bzrFunctions.Lock(argList) {{{2
+function! s:bzrFunctions.Lock(argList)
+  echomsg 'bzr lock is not necessary'
+endfunction
+
+" Function: s:bzrFunctions.Log() {{{2
+function! s:bzrFunctions.Log(argList)
+  if len(a:argList) == 0
+    let options = []
+    let caption = ''
+  elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+    let options = ['-r' . join(a:argList, ':')]
+    let caption = options[0]
+  else
+    " Pass-through
+    let options = a:argList
+    let caption = join(a:argList, ' ')
+  endif
+
+  let resultBuffer = s:DoCommand(join(['log', '-v'] + options), 'log', caption, {})
+  return resultBuffer
+endfunction
+
+" Function: s:bzrFunctions.Revert(argList) {{{2
+function! s:bzrFunctions.Revert(argList)
+  return s:DoCommand('revert', 'revert', '', {})
+endfunction
+
+" Function: s:bzrFunctions.Review(argList) {{{2
+function! s:bzrFunctions.Review(argList)
+  if len(a:argList) == 0
+    let versiontag = '(current)'
+    let versionOption = ''
+  else
+    let versiontag = a:argList[0]
+    let versionOption = ' -r ' . versiontag . ' '
+  endif
+
+  return s:DoCommand('cat' . versionOption, 'review', versiontag, {})
+endfunction
+
+" Function: s:bzrFunctions.Status(argList) {{{2
+function! s:bzrFunctions.Status(argList)
+  let options = ['-S']
+  if len(a:argList) != 0
+    let options = a:argList
+  endif
+  return s:DoCommand(join(['status'] + options, ' '), 'status', join(options, ' '), {})
+endfunction
+
+" Function: s:bzrFunctions.Unlock(argList) {{{2
+function! s:bzrFunctions.Unlock(argList)
+  echomsg 'bzr unlock is not necessary'
+endfunction
+" Function: s:bzrFunctions.Update(argList) {{{2
+function! s:bzrFunctions.Update(argList)
+  return s:DoCommand('update', 'update', '', {})
+endfunction
+
+" Annotate setting {{{2
+let s:bzrFunctions.AnnotateSplitRegex = '^[^|]\+ | '
+
+" Section: Plugin Registration {{{1
+let s:VCSCommandUtility = VCSCommandRegisterModule('BZR', expand('<sfile>'), s:bzrFunctions, [])
+
+let &cpo = s:save_cpo
diff --git a/.vim/plugin/vcscommand.vim b/.vim/plugin/vcscommand.vim
new file mode 100644 (file)
index 0000000..98214f2
--- /dev/null
@@ -0,0 +1,1427 @@
+" vim600: set foldmethod=marker:
+"
+" Vim plugin to assist in working with files under control of various Version
+" Control Systems, such as CVS, SVN, SVK, and git.
+"
+" Maintainer:    Bob Hiestand <bob.hiestand@gmail.com>
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+"
+" Section: Documentation {{{1
+"
+" Provides functions to invoke various source control commands on the current
+" file (either the current buffer, or, in the case of an directory buffer, the
+" directory and all subdirectories associated with the current buffer).  The
+" output of the commands is captured in a new scratch window.
+"
+" This plugin needs additional extension plugins, each specific to a source
+" control system, to function.  Several options include the name of the
+" version control system in the option name.  Such options use the placeholder
+" text '{VCSType}', which would be replaced in actual usage with 'CVS' or
+" 'SVN', for instance.
+"
+" Command documentation {{{2
+"
+" VCSAdd           Adds the current file to source control.
+"
+" VCSAnnotate[!]   Displays the current file with each line annotated with the
+"                  version in which it was most recently changed.  If an
+"                  argument is given, the argument is used as a revision
+"                  number to display.  If not given an argument, it uses the
+"                  most recent version of the file on the current branch.
+"                  Additionally, if the current buffer is a VCSAnnotate buffer
+"                  already, the version number on the current line is used.
+"
+"                  If '!' is used, the view of the annotated buffer is split
+"                  so that the annotation is in a separate window from the
+"                  content, and each is highlighted separately.
+"
+" VCSBlame         Alias for 'VCSAnnotate'.
+"
+" VCSCommit[!]     Commits changes to the current file to source control.
+"
+"                  If called with arguments, the arguments are the log message.
+"
+"                  If '!' is used, an empty log message is committed.
+"
+"                  If called with no arguments, this is a two-step command.
+"                  The first step opens a buffer to accept a log message.
+"                  When that buffer is written, it is automatically closed and
+"                  the file is committed using the information from that log
+"                  message.  The commit can be abandoned if the log message
+"                  buffer is deleted or wiped before being written.
+"
+" VCSDelete        Deletes the current file and removes it from source control.
+"
+" VCSDiff          With no arguments, this displays the differences between
+"                  the current file and its parent version under source
+"                  control in a new scratch buffer.
+"
+"                  With one argument, the diff is performed on the
+"                  current file against the specified revision.
+"
+"                  With two arguments, the diff is performed between the
+"                  specified revisions of the current file.
+"
+"                  This command uses the 'VCSCommand{VCSType}DiffOpt' variable
+"                  to specify diff options.  If that variable does not exist,
+"                  a plugin-specific default is used.  If you wish to have no
+"                  options, then set it to the empty string.
+"
+" VCSGotoOriginal  Jumps to the source buffer if the current buffer is a VCS
+"                  scratch buffer.  If VCSGotoOriginal[!] is used, remove all
+"                  VCS scratch buffers associated with the original file.
+"
+" VCSInfo          Displays extended information about the current file in a
+"                  new scratch buffer.
+"
+" VCSLock          Locks the current file in order to prevent other users from
+"                  concurrently modifying it.  The exact semantics of this
+"                  command depend on the underlying VCS.
+"
+" VCSLog           Displays the version history of the current file in a new
+"                  scratch buffer.
+"
+" VCSRemove        Alias for 'VCSDelete'.
+"
+" VCSRevert        Replaces the modified version of the current file with the
+"                  most recent version from the repository.
+"
+" VCSReview        Displays a particular version of the current file in a new
+"                  scratch buffer.  If no argument is given, the most recent
+"                  version of the file on the current branch is retrieved.
+"
+" VCSStatus        Displays versioning information about the current file in a
+"                  new scratch buffer.
+"
+" VCSUnlock        Unlocks the current file in order to allow other users from
+"                  concurrently modifying it.  The exact semantics of this
+"                  command depend on the underlying VCS.
+"
+" VCSUpdate        Updates the current file with any relevant changes from the
+"                  repository.
+"
+" VCSVimDiff       Uses vimdiff to display differences between versions of the
+"                  current file.
+"
+"                  If no revision is specified, the most recent version of the
+"                  file on the current branch is used.  With one argument,
+"                  that argument is used as the revision as above.  With two
+"                  arguments, the differences between the two revisions is
+"                  displayed using vimdiff.
+"
+"                  With either zero or one argument, the original buffer is
+"                  used to perform the vimdiff.  When the scratch buffer is
+"                  closed, the original buffer will be returned to normal
+"                  mode.
+"
+"                  Once vimdiff mode is started using the above methods,
+"                  additional vimdiff buffers may be added by passing a single
+"                  version argument to the command.  There may be up to 4
+"                  vimdiff buffers total.
+"
+"                  Using the 2-argument form of the command resets the vimdiff
+"                  to only those 2 versions.  Additionally, invoking the
+"                  command on a different file will close the previous vimdiff
+"                  buffers.
+"
+" Mapping documentation: {{{2
+"
+" By default, a mapping is defined for each command.  User-provided mappings
+" can be used instead by mapping to <Plug>CommandName, for instance:
+"
+" nmap ,ca <Plug>VCSAdd
+"
+" The default mappings are as follow:
+"
+"   <Leader>ca VCSAdd
+"   <Leader>cn VCSAnnotate
+"   <Leader>cN VCSAnnotate!
+"   <Leader>cc VCSCommit
+"   <Leader>cD VCSDelete
+"   <Leader>cd VCSDiff
+"   <Leader>cg VCSGotoOriginal
+"   <Leader>cG VCSGotoOriginal!
+"   <Leader>ci VCSInfo
+"   <Leader>cl VCSLog
+"   <Leader>cL VCSLock
+"   <Leader>cr VCSReview
+"   <Leader>cs VCSStatus
+"   <Leader>cu VCSUpdate
+"   <Leader>cU VCSUnlock
+"   <Leader>cv VCSVimDiff
+"
+" Options documentation: {{{2
+"
+" Several variables are checked by the script to determine behavior as follow:
+"
+" VCSCommandCommitOnWrite
+"   This variable, if set to a non-zero value, causes the pending commit to
+"   take place immediately as soon as the log message buffer is written.  If
+"   set to zero, only the VCSCommit mapping will cause the pending commit to
+"   occur.  If not set, it defaults to 1.
+"
+" VCSCommandDeleteOnHide
+"   This variable, if set to a non-zero value, causes the temporary VCS result
+"   buffers to automatically delete themselves when hidden.
+"
+" VCSCommand{VCSType}DiffOpt
+"   This variable, if set, determines the options passed to the diff command
+"   of the underlying VCS.  Each VCS plugin defines a default value.
+"
+" VCSCommandDiffSplit
+"   This variable overrides the VCSCommandSplit variable, but only for buffers
+"   created with VCSVimDiff.
+"
+" VCSCommandDisableAll
+"   This variable, if set, prevents the plugin or any extensions from loading
+"   at all.  This is useful when a single runtime distribution is used on
+"   multiple systems with varying versions.
+"
+" VCSCommandDisableMappings
+"   This variable, if set to a non-zero value, prevents the default command
+"   mappings from being set.
+"
+" VCSCommandDisableExtensionMappings
+"   This variable, if set to a non-zero value, prevents the default command
+"   mappings from being set for commands specific to an individual VCS.
+"
+" VCSCommandDisableMenu
+"   This variable, if set to a non-zero value, prevents the default command
+"   menu from being set.
+"
+" VCSCommandEdit
+"   This variable controls whether to split the current window to display a
+"   scratch buffer ('split'), or to display it in the current buffer ('edit').
+"   If not set, it defaults to 'split'.
+"
+" VCSCommandEnableBufferSetup
+"   This variable, if set to a non-zero value, activates VCS buffer management
+"   mode.  This mode means that the buffer variable 'VCSRevision' is set if
+"   the file is VCS-controlled.  This is useful for displaying version
+"   information in the status bar.  Additional options may be set by
+"   individual VCS plugins.
+"
+" VCSCommandMappings
+"   This variable, if set, overrides the default mappings used for shortcuts.
+"   It should be a List of 2-element Lists, each containing a shortcut and
+"   function name pair.
+"
+" VCSCommandMapPrefix
+"   This variable, if set, overrides the default mapping prefix ('<Leader>c').
+"   This allows customization of the mapping space used by the vcscommand
+"   shortcuts.
+"
+" VCSCommandMenuPriority
+"   This variable, if set, overrides the default menu priority '' (empty)
+"
+" VCSCommandMenuRoot
+"   This variable, if set, overrides the default menu root 'Plugin.VCS'
+"
+" VCSCommandResultBufferNameExtension
+"   This variable, if set to a non-blank value, is appended to the name of the
+"   VCS command output buffers.  For example, '.vcs'.  Using this option may
+"   help avoid problems caused by autocommands dependent on file extension.
+"
+" VCSCommandResultBufferNameFunction
+"   This variable, if set, specifies a custom function for naming VCS command
+"   output buffers.  This function will be passed the following arguments:
+"
+"   command - name of the VCS command being executed (such as 'Log' or
+"   'Diff').
+"
+"   originalBuffer - buffer number of the source file.
+"
+"   vcsType - type of VCS controlling this file (such as 'CVS' or 'SVN').
+"
+"   statusText - extra text associated with the VCS action (such as version
+"   numbers).
+"
+" VCSCommandSplit
+"   This variable controls the orientation of the various window splits that
+"   may occur (such as with VCSVimDiff, when using a VCS command on a VCS
+"   command buffer, or when the 'VCSCommandEdit' variable is set to 'split'.
+"   If set to 'horizontal', the resulting windows will be on stacked on top of
+"   one another.  If set to 'vertical', the resulting windows will be
+"   side-by-side.  If not set, it defaults to 'horizontal' for all but
+"   VCSVimDiff windows.
+"
+" VCSCommandVCSTypeOverride
+"   This variable allows the VCS type detection to be overridden on a
+"   path-by-path basis.  The value of this variable is expected to be a List
+"   of Lists.  Each high-level List item is a List containing two elements.
+"   The first element is a regular expression that will be matched against the
+"   full file name of a given buffer.  If it matches, the second element will
+"   be used as the VCS type.
+"
+" Event documentation {{{2
+"   For additional customization, VCSCommand.vim uses User event autocommand
+"   hooks.  Each event is in the VCSCommand group, and different patterns
+"   match the various hooks.
+"
+"   For instance, the following could be added to the vimrc to provide a 'q'
+"   mapping to quit a VCS scratch buffer:
+"
+"   augroup VCSCommand
+"     au VCSCommand User VCSBufferCreated silent! nmap <unique> <buffer> q :bwipeout<cr>
+"   augroup END
+"
+"   The following hooks are available:
+"
+"   VCSBufferCreated           This event is fired just after a VCS command
+"                              output buffer is created.  It is executed
+"                              within the context of the new buffer.
+"
+"   VCSBufferSetup             This event is fired just after VCS buffer setup
+"                              occurs, if enabled.
+"
+"   VCSPluginInit              This event is fired when the VCSCommand plugin
+"                              first loads.
+"
+"   VCSPluginFinish            This event is fired just after the VCSCommand
+"                              plugin loads.
+"
+"   VCSVimDiffFinish           This event is fired just after the VCSVimDiff
+"                              command executes to allow customization of,
+"                              for instance, window placement and focus.
+"
+" Section: Plugin header {{{1
+
+" loaded_VCSCommand is set to 1 when the initialization begins, and 2 when it
+" completes.  This allows various actions to only be taken by functions after
+" system initialization.
+
+if exists('VCSCommandDisableAll')
+       finish
+endif
+
+if exists('loaded_VCSCommand')
+       finish
+endif
+let loaded_VCSCommand = 1
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Event group setup {{{1
+
+augroup VCSCommand
+augroup END
+
+augroup VCSCommandCommit
+augroup END
+
+" Section: Plugin initialization {{{1
+silent do VCSCommand User VCSPluginInit
+
+" Section: Constants declaration {{{1
+
+let g:VCSCOMMAND_IDENTIFY_EXACT = 1
+let g:VCSCOMMAND_IDENTIFY_INEXACT = -1
+
+" Section: Script variable initialization {{{1
+
+" Hidden functions for use by extensions
+let s:VCSCommandUtility = {}
+
+" plugin-specific information:  {vcs -> [script, {command -> function}, {key -> mapping}]}
+let s:plugins = {}
+
+" temporary values of overridden configuration variables
+let s:optionOverrides = {}
+
+" state flag used to vary behavior of certain automated actions
+let s:isEditFileRunning = 0
+
+" Section: Utility functions {{{1
+
+" Function: s:ReportError(mapping) {{{2
+" Displays the given error in a consistent faction.  This is intended to be
+" invoked from a catch statement.
+
+function! s:ReportError(error)
+       echohl WarningMsg|echomsg 'VCSCommand:  ' . a:error|echohl None
+endfunction
+
+" Function: s:VCSCommandUtility.system(...) {{{2
+" Replacement for system() function.  This version protects the quoting in the
+" command line on Windows systems.
+
+function! s:VCSCommandUtility.system(...)
+       if (has("win32") || has("win64")) && &sxq !~ '"'
+               let save_sxq = &sxq
+               set sxq=\"
+       endif
+       try
+               return call('system', a:000)
+       finally
+               if exists("save_sxq")
+                       let &sxq = save_sxq
+               endif
+       endtry
+endfunction
+
+" Function: s:VCSCommandUtility.addMenuItem(shortcut, command) {{{2
+" Adds the given menu item.
+
+function! s:VCSCommandUtility.addMenuItem(shortcut, command)
+       if s:menuEnabled
+           exe 'amenu <silent> '.s:menuPriority.' '.s:menuRoot.'.'.a:shortcut.' '.a:command
+       endif
+endfunction
+
+" Function: s:ClearMenu() {{{2
+" Removes all VCSCommand menu items
+function! s:ClearMenu()
+       if s:menuEnabled
+               execute 'aunmenu' s:menuRoot
+       endif
+endfunction
+
+" Function: s:CreateMapping(shortcut, expansion, display) {{{2
+" Creates the given mapping by prepending the contents of
+" 'VCSCommandMapPrefix' (by default '<Leader>c') to the given shortcut and
+" mapping it to the given plugin function.  If a mapping exists for the
+" specified shortcut + prefix, emit an error but continue.  If a mapping
+" exists for the specified function, do nothing.
+
+function! s:CreateMapping(shortcut, expansion, display)
+       let lhs = VCSCommandGetOption('VCSCommandMapPrefix', '<Leader>c') . a:shortcut
+       if !hasmapto(a:expansion)
+               try
+                       execute 'nmap <silent> <unique>' lhs a:expansion
+               catch /^Vim(.*):E227:/
+                       if(&verbose != 0)
+                               echohl WarningMsg|echomsg 'VCSCommand:  mapping ''' . lhs . ''' already exists, refusing to overwrite.  The mapping for ' . a:display . ' will not be available.'|echohl None
+                       endif
+               endtry
+       endif
+endfunction
+
+" Function: s:ExecuteExtensionMapping(mapping) {{{2
+" Invokes the appropriate extension mapping depending on the type of the
+" current buffer.
+
+function! s:ExecuteExtensionMapping(mapping)
+       let buffer = bufnr('%')
+       let vcsType = VCSCommandGetVCSType(buffer)
+       if !has_key(s:plugins, vcsType)
+               throw 'Unknown VCS type:  ' . vcsType
+       endif
+       if !has_key(s:plugins[vcsType][2], a:mapping)
+               throw 'This extended mapping is not defined for ' . vcsType
+       endif
+       silent execute 'normal' ':' .  s:plugins[vcsType][2][a:mapping] . "\<CR>"
+endfunction
+
+" Function: s:ExecuteVCSCommand(command, argList) {{{2
+" Calls the indicated plugin-specific VCS command on the current buffer.
+" Returns: buffer number of resulting output scratch buffer, or -1 if an error
+" occurs.
+
+function! s:ExecuteVCSCommand(command, argList)
+       try
+               let buffer = bufnr('%')
+
+               let vcsType = VCSCommandGetVCSType(buffer)
+               if !has_key(s:plugins, vcsType)
+                       throw 'Unknown VCS type:  ' . vcsType
+               endif
+
+               let originalBuffer = VCSCommandGetOriginalBuffer(buffer)
+               let bufferName = bufname(originalBuffer)
+
+               " It is already known that the directory is under VCS control.  No further
+               " checks are needed.  Otherwise, perform some basic sanity checks to avoid
+               " VCS-specific error messages from confusing things.
+               if !isdirectory(bufferName)
+                       if !filereadable(bufferName)
+                               throw 'No such file ' . bufferName
+                       endif
+               endif
+
+               let functionMap = s:plugins[vcsType][1]
+               if !has_key(functionMap, a:command)
+                       throw 'Command ''' . a:command . ''' not implemented for ' . vcsType
+               endif
+               return functionMap[a:command](a:argList)
+       catch
+               call s:ReportError(v:exception)
+               return -1
+       endtry
+endfunction
+
+" Function: s:GenerateResultBufferName(command, originalBuffer, vcsType, statusText) {{{2
+" Default method of generating the name for VCS result buffers.  This can be
+" overridden with the VCSResultBufferNameFunction variable.
+
+function! s:GenerateResultBufferName(command, originalBuffer, vcsType, statusText)
+       let fileName = bufname(a:originalBuffer)
+       let bufferName = a:vcsType . ' ' . a:command
+       if strlen(a:statusText) > 0
+               let bufferName .= ' ' . a:statusText
+       endif
+       let bufferName .= ' ' . fileName
+       let counter = 0
+       let versionedBufferName = bufferName
+       while bufexists(versionedBufferName)
+               let counter += 1
+               let versionedBufferName = bufferName . ' (' . counter . ')'
+       endwhile
+       return versionedBufferName
+endfunction
+
+" Function: s:GenerateResultBufferNameWithExtension(command, originalBuffer, vcsType, statusText) {{{2
+" Method of generating the name for VCS result buffers that uses the original
+" file name with the VCS type and command appended as extensions.
+
+function! s:GenerateResultBufferNameWithExtension(command, originalBuffer, vcsType, statusText)
+       let fileName = bufname(a:originalBuffer)
+       let bufferName = a:vcsType . ' ' . a:command
+       if strlen(a:statusText) > 0
+               let bufferName .= ' ' . a:statusText
+       endif
+       let bufferName .= ' ' . fileName . VCSCommandGetOption('VCSCommandResultBufferNameExtension', '.vcs')
+       let counter = 0
+       let versionedBufferName = bufferName
+       while bufexists(versionedBufferName)
+               let counter += 1
+               let versionedBufferName = '(' . counter . ') ' . bufferName
+       endwhile
+       return versionedBufferName
+endfunction
+
+" Function: s:EditFile(command, originalBuffer, statusText) {{{2
+" Creates a new buffer of the given name and associates it with the given
+" original buffer.
+
+function! s:EditFile(command, originalBuffer, statusText)
+       let vcsType = getbufvar(a:originalBuffer, 'VCSCommandVCSType')
+
+       " Protect against useless buffer set-up
+       let s:isEditFileRunning += 1
+       try
+               let editCommand = VCSCommandGetOption('VCSCommandEdit', 'split')
+               if editCommand == 'split'
+                       if VCSCommandGetOption('VCSCommandSplit', 'horizontal') == 'horizontal'
+                               rightbelow split
+                       else
+                               vert rightbelow split
+                       endif
+               endif
+
+               enew
+
+               call s:SetupScratchBuffer(a:command, vcsType, a:originalBuffer, a:statusText)
+
+       finally
+               let s:isEditFileRunning -= 1
+       endtry
+endfunction
+
+" Function: s:SetupScratchBuffer(command, vcsType, originalBuffer, statusText) {{{2
+" Creates convenience buffer variables and the name of a vcscommand result
+" buffer.
+
+function! s:SetupScratchBuffer(command, vcsType, originalBuffer, statusText)
+       let nameExtension = VCSCommandGetOption('VCSCommandResultBufferNameExtension', '')
+       if nameExtension == ''
+               let nameFunction = VCSCommandGetOption('VCSCommandResultBufferNameFunction', 's:GenerateResultBufferName')
+       else
+               let nameFunction = VCSCommandGetOption('VCSCommandResultBufferNameFunction', 's:GenerateResultBufferNameWithExtension')
+       endif
+
+       let name = call(nameFunction, [a:command, a:originalBuffer, a:vcsType, a:statusText])
+
+       let b:VCSCommandCommand = a:command
+       let b:VCSCommandOriginalBuffer = a:originalBuffer
+       let b:VCSCommandSourceFile = bufname(a:originalBuffer)
+       let b:VCSCommandVCSType = a:vcsType
+       if a:statusText != ''
+               let b:VCSCommandStatusText = a:statusText
+       endif
+
+       setlocal buftype=nofile
+       setlocal noswapfile
+       let &filetype = tolower(a:vcsType . a:command)
+
+       if VCSCommandGetOption('VCSCommandDeleteOnHide', 0)
+               setlocal bufhidden=delete
+       endif
+       silent noautocmd file `=name`
+endfunction
+
+" Function: s:SetupBuffer() {{{2
+" Attempts to set the b:VCSCommandBufferInfo variable
+
+function! s:SetupBuffer()
+       if (exists('b:VCSCommandBufferSetup') && b:VCSCommandBufferSetup)
+               " This buffer is already set up.
+               return
+       endif
+
+       if !isdirectory(@%) && (strlen(&buftype) > 0 || !filereadable(@%))
+               " No special status for special buffers other than directory buffers.
+               return
+       endif
+
+       if !VCSCommandGetOption('VCSCommandEnableBufferSetup', 0) || s:isEditFileRunning > 0
+               unlet! b:VCSCommandBufferSetup
+               return
+       endif
+
+       try
+               let vcsType = VCSCommandGetVCSType(bufnr('%'))
+               let b:VCSCommandBufferInfo = s:plugins[vcsType][1].GetBufferInfo()
+               silent do VCSCommand User VCSBufferSetup
+       catch /No suitable plugin/
+               " This is not a VCS-controlled file.
+               let b:VCSCommandBufferInfo = []
+       endtry
+
+       let b:VCSCommandBufferSetup = 1
+endfunction
+
+" Function: s:MarkOrigBufferForSetup(buffer) {{{2
+" Resets the buffer setup state of the original buffer for a given VCS scratch
+" buffer.
+" Returns:  The VCS buffer number in a passthrough mode.
+
+function! s:MarkOrigBufferForSetup(buffer)
+       checktime
+       if a:buffer > 0
+               let origBuffer = VCSCommandGetOriginalBuffer(a:buffer)
+               " This should never not work, but I'm paranoid
+               if origBuffer != a:buffer
+                       call setbufvar(origBuffer, 'VCSCommandBufferSetup', 0)
+               endif
+       endif
+       return a:buffer
+endfunction
+
+" Function: s:OverrideOption(option, [value]) {{{2
+" Provides a temporary override for the given VCS option.  If no value is
+" passed, the override is disabled.
+
+function! s:OverrideOption(option, ...)
+       if a:0 == 0
+               call remove(s:optionOverrides[a:option], -1)
+       else
+               if !has_key(s:optionOverrides, a:option)
+                       let s:optionOverrides[a:option] = []
+               endif
+               call add(s:optionOverrides[a:option], a:1)
+       endif
+endfunction
+
+" Function: s:WipeoutCommandBuffers() {{{2
+" Clears all current VCS output buffers of the specified type for a given source.
+
+function! s:WipeoutCommandBuffers(originalBuffer, VCSCommand)
+       let buffer = 1
+       while buffer <= bufnr('$')
+               if getbufvar(buffer, 'VCSCommandOriginalBuffer') == a:originalBuffer
+                       if getbufvar(buffer, 'VCSCommandCommand') == a:VCSCommand
+                               execute 'bw' buffer
+                       endif
+               endif
+               let buffer = buffer + 1
+       endwhile
+endfunction
+
+" Function: s:VimDiffRestore(vimDiffBuff) {{{2
+" Checks whether the given buffer is one whose deletion should trigger
+" restoration of an original buffer after it was diffed.  If so, it executes
+" the appropriate setting command stored with that original buffer.
+
+function! s:VimDiffRestore(vimDiffBuff)
+       let s:isEditFileRunning += 1
+       try
+               if exists('t:vcsCommandVimDiffSourceBuffer')
+                       if a:vimDiffBuff == t:vcsCommandVimDiffSourceBuffer
+                               " Original file is being removed.
+                               unlet! t:vcsCommandVimDiffSourceBuffer
+                               unlet! t:vcsCommandVimDiffRestoreCmd
+                               unlet! t:vcsCommandVimDiffScratchList
+                       else
+                               let index = index(t:vcsCommandVimDiffScratchList, a:vimDiffBuff)
+                               if index >= 0
+                                       call remove(t:vcsCommandVimDiffScratchList, index)
+                                       if len(t:vcsCommandVimDiffScratchList) == 0
+                                               if exists('t:vcsCommandVimDiffRestoreCmd')
+                                                       " All scratch buffers are gone, reset the original.
+                                                       " Only restore if the source buffer is still in Diff mode
+
+                                                       let sourceWinNR = bufwinnr(t:vcsCommandVimDiffSourceBuffer)
+                                                       if sourceWinNR != -1
+                                                               " The buffer is visible in at least one window
+                                                               let currentWinNR = winnr()
+                                                               while winbufnr(sourceWinNR) != -1
+                                                                       if winbufnr(sourceWinNR) == t:vcsCommandVimDiffSourceBuffer
+                                                                               execute sourceWinNR . 'wincmd w'
+                                                                               if getwinvar(0, '&diff')
+                                                                                       execute t:vcsCommandVimDiffRestoreCmd
+                                                                               endif
+                                                                       endif
+                                                                       let sourceWinNR = sourceWinNR + 1
+                                                               endwhile
+                                                               execute currentWinNR . 'wincmd w'
+                                                       else
+                                                               " The buffer is hidden.  It must be visible in order to set the
+                                                               " diff option.
+                                                               let currentBufNR = bufnr('')
+                                                               execute 'hide buffer' t:vcsCommandVimDiffSourceBuffer
+                                                               if getwinvar(0, '&diff')
+                                                                       execute t:vcsCommandVimDiffRestoreCmd
+                                                               endif
+                                                               execute 'hide buffer' currentBufNR
+                                                       endif
+
+                                                       unlet t:vcsCommandVimDiffRestoreCmd
+                                               endif
+                                               " All buffers are gone.
+                                               unlet t:vcsCommandVimDiffSourceBuffer
+                                               unlet t:vcsCommandVimDiffScratchList
+                                       endif
+                               endif
+                       endif
+               endif
+       finally
+               let s:isEditFileRunning -= 1
+       endtry
+endfunction
+
+" Section: Generic VCS command functions {{{1
+
+" Function: s:VCSAnnotate(...) {{{2
+function! s:VCSAnnotate(bang, ...)
+       try
+               let line = line('.')
+               let currentBuffer = bufnr('%')
+               let originalBuffer = VCSCommandGetOriginalBuffer(currentBuffer)
+
+               let annotateBuffer = s:ExecuteVCSCommand('Annotate', a:000)
+               if annotateBuffer == -1
+                       return -1
+               endif
+               if a:bang == '!' && VCSCommandGetOption('VCSCommandDisableSplitAnnotate', 0) == 0
+                       let vcsType = VCSCommandGetVCSType(annotateBuffer)
+                       let functionMap = s:plugins[vcsType][1]
+                       let splitRegex = ''
+                       if has_key(s:plugins[vcsType][1], 'AnnotateSplitRegex')
+                               let splitRegex = s:plugins[vcsType][1]['AnnotateSplitRegex']
+                       endif
+                       let splitRegex = VCSCommandGetOption('VCSCommand' . vcsType . 'AnnotateSplitRegex', splitRegex)
+                       if splitRegex == ''
+                               return annotateBuffer
+                       endif
+                       let originalFileType = getbufvar(originalBuffer, '&ft')
+                       let annotateFileType = getbufvar(annotateBuffer, '&ft')
+                       execute "normal 0zR\<c-v>G/" . splitRegex . "/e\<cr>d"
+                       call setbufvar('%', '&filetype', getbufvar(originalBuffer, '&filetype'))
+                       set scrollbind
+                       leftabove vert new
+                       normal 0P
+                       execute "normal" . col('$') . "\<c-w>|"
+                       call s:SetupScratchBuffer('annotate', vcsType, originalBuffer, 'header')
+                       wincmd l
+               endif
+
+               if currentBuffer == originalBuffer
+                       " Starting from the original source buffer, so the
+                       " current line is relevant.
+                       if a:0 == 0
+                               " No argument list means that we're annotating
+                               " the current version, so jumping to the same
+                               " line is the expected action.
+                               execute "normal" line . 'G'
+                               if has('folding')
+                                       " The execution of the buffer created autocommand
+                                       " re-folds the buffer.  Display the current line
+                                       " unfolded.
+                                       normal zv
+                               endif
+                       endif
+               endif
+
+               return annotateBuffer
+       catch
+               call s:ReportError(v:exception)
+               return -1
+       endtry
+endfunction
+
+" Function: s:VCSCommit() {{{2
+function! s:VCSCommit(bang, message)
+       try
+               let vcsType = VCSCommandGetVCSType(bufnr('%'))
+               if !has_key(s:plugins, vcsType)
+                       throw 'Unknown VCS type:  ' . vcsType
+               endif
+
+               let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+
+               " Handle the commit message being specified.  If a message is supplied, it
+               " is used; if bang is supplied, an empty message is used; otherwise, the
+               " user is provided a buffer from which to edit the commit message.
+
+               if strlen(a:message) > 0 || a:bang == '!'
+                       return s:VCSFinishCommit([a:message], originalBuffer)
+               endif
+
+               call s:EditFile('commitlog', originalBuffer, '')
+               setlocal ft=vcscommit
+
+               " Create a commit mapping.
+
+               nnoremap <silent> <buffer> <Plug>VCSCommit :call <SID>VCSFinishCommitWithBuffer()<CR>
+
+               silent 0put ='VCS: ----------------------------------------------------------------------'
+               silent put ='VCS: Please enter log message.  Lines beginning with ''VCS:'' are removed automatically.'
+               silent put ='VCS: To finish the commit, Type <leader>cc (or your own <Plug>VCSCommit mapping)'
+
+               if VCSCommandGetOption('VCSCommandCommitOnWrite', 1) == 1
+                       setlocal buftype=acwrite
+                       au VCSCommandCommit BufWriteCmd <buffer> call s:VCSFinishCommitWithBuffer()
+                       silent put ='VCS: or write this buffer'
+               endif
+
+               silent put ='VCS: ----------------------------------------------------------------------'
+               $
+               setlocal nomodified
+               silent do VCSCommand User VCSBufferCreated
+       catch
+               call s:ReportError(v:exception)
+               return -1
+       endtry
+endfunction
+
+" Function: s:VCSFinishCommitWithBuffer() {{{2
+" Wrapper for s:VCSFinishCommit which is called only from a commit log buffer
+" which removes all lines starting with 'VCS:'.
+
+function! s:VCSFinishCommitWithBuffer()
+       setlocal nomodified
+       let currentBuffer = bufnr('%')
+       let logMessageList = getbufline('%', 1, '$')
+       call filter(logMessageList, 'v:val !~ ''^\s*VCS:''')
+       let resultBuffer = s:VCSFinishCommit(logMessageList, b:VCSCommandOriginalBuffer)
+       if resultBuffer >= 0
+               execute 'bw' currentBuffer
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:VCSFinishCommit(logMessageList, originalBuffer) {{{2
+function! s:VCSFinishCommit(logMessageList, originalBuffer)
+       let messageFileName = tempname()
+       call writefile(a:logMessageList, messageFileName)
+       try
+               let resultBuffer = s:ExecuteVCSCommand('Commit', [messageFileName])
+               if resultBuffer < 0
+                       return resultBuffer
+               endif
+               return s:MarkOrigBufferForSetup(resultBuffer)
+       finally
+               call delete(messageFileName)
+       endtry
+endfunction
+
+" Function: s:VCSGotoOriginal(bang) {{{2
+function! s:VCSGotoOriginal(bang)
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       if originalBuffer > 0
+               let origWinNR = bufwinnr(originalBuffer)
+               if origWinNR == -1
+                       execute 'buffer' originalBuffer
+               else
+                       execute origWinNR . 'wincmd w'
+               endif
+               if a:bang == '!'
+                       let buffnr = 1
+                       let buffmaxnr = bufnr('$')
+                       while buffnr <= buffmaxnr
+                               if getbufvar(buffnr, 'VCSCommandOriginalBuffer') == originalBuffer
+                                       execute 'bw' buffnr
+                               endif
+                               let buffnr = buffnr + 1
+                       endwhile
+               endif
+       endif
+endfunction
+
+function! s:VCSDiff(...)  "{{{2
+       let resultBuffer = s:ExecuteVCSCommand('Diff', a:000)
+       if resultBuffer > 0
+               let &filetype = 'diff'
+       elseif resultBuffer == 0
+               echomsg 'No differences found'
+       endif
+       return resultBuffer
+endfunction
+
+function! s:VCSReview(...)  "{{{2
+       let resultBuffer = s:ExecuteVCSCommand('Review', a:000)
+       if resultBuffer > 0
+               let &filetype = getbufvar(b:VCSCommandOriginalBuffer, '&filetype')
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:VCSVimDiff(...) {{{2
+function! s:VCSVimDiff(...)
+       try
+               let vcsType = VCSCommandGetVCSType(bufnr('%'))
+               if !has_key(s:plugins, vcsType)
+                       throw 'Unknown VCS type:  ' . vcsType
+               endif
+               let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+               let s:isEditFileRunning = s:isEditFileRunning + 1
+               try
+                       " If there's already a VimDiff'ed window, restore it.
+                       " There may only be one VCSVimDiff original window at a time.
+
+                       if exists('t:vcsCommandVimDiffSourceBuffer') && t:vcsCommandVimDiffSourceBuffer != originalBuffer
+                               " Clear the existing vimdiff setup by removing the result buffers.
+                               call s:WipeoutCommandBuffers(t:vcsCommandVimDiffSourceBuffer, 'vimdiff')
+                       endif
+
+                       let orientation = &diffopt =~ 'horizontal' ? 'horizontal' : 'vertical'
+                       let orientation = VCSCommandGetOption('VCSCommandSplit', orientation)
+                       let orientation = VCSCommandGetOption('VCSCommandDiffSplit', orientation)
+
+                       " Split and diff
+                       if(a:0 == 2)
+                               " Reset the vimdiff system, as 2 explicit versions were provided.
+                               if exists('t:vcsCommandVimDiffSourceBuffer')
+                                       call s:WipeoutCommandBuffers(t:vcsCommandVimDiffSourceBuffer, 'vimdiff')
+                               endif
+                               let resultBuffer = s:VCSReview(a:1)
+                               if resultBuffer < 0
+                                       echomsg 'Can''t open revision ' . a:1
+                                       return resultBuffer
+                               endif
+                               let b:VCSCommandCommand = 'vimdiff'
+                               diffthis
+                               let t:vcsCommandVimDiffScratchList = [resultBuffer]
+                               " If no split method is defined, cheat, and set it to vertical.
+                               try
+                                       call s:OverrideOption('VCSCommandSplit', orientation)
+                                       let resultBuffer = s:VCSReview(a:2)
+                               finally
+                                       call s:OverrideOption('VCSCommandSplit')
+                               endtry
+                               if resultBuffer < 0
+                                       echomsg 'Can''t open revision ' . a:1
+                                       return resultBuffer
+                               endif
+                               let b:VCSCommandCommand = 'vimdiff'
+                               diffthis
+                               let t:vcsCommandVimDiffScratchList += [resultBuffer]
+                       else
+                               " Add new buffer
+                               call s:OverrideOption('VCSCommandEdit', 'split')
+                               try
+                                       " Force splitting behavior, otherwise why use vimdiff?
+                                       call s:OverrideOption('VCSCommandSplit', orientation)
+                                       try
+                                               if(a:0 == 0)
+                                                       let resultBuffer = s:VCSReview()
+                                               else
+                                                       let resultBuffer = s:VCSReview(a:1)
+                                               endif
+                                       finally
+                                               call s:OverrideOption('VCSCommandSplit')
+                                       endtry
+                               finally
+                                       call s:OverrideOption('VCSCommandEdit')
+                               endtry
+                               if resultBuffer < 0
+                                       echomsg 'Can''t open current revision'
+                                       return resultBuffer
+                               endif
+                               let b:VCSCommandCommand = 'vimdiff'
+                               diffthis
+
+                               if !exists('t:vcsCommandVimDiffSourceBuffer')
+                                       " New instance of vimdiff.
+                                       let t:vcsCommandVimDiffScratchList = [resultBuffer]
+
+                                       " This could have been invoked on a VCS result buffer, not the
+                                       " original buffer.
+                                       wincmd W
+                                       execute 'buffer' originalBuffer
+                                       " Store info for later original buffer restore
+                                       let t:vcsCommandVimDiffRestoreCmd =
+                                                               \    'call setbufvar('.originalBuffer.', ''&diff'', '.getbufvar(originalBuffer, '&diff').')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&foldcolumn'', '.getbufvar(originalBuffer, '&foldcolumn').')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&foldenable'', '.getbufvar(originalBuffer, '&foldenable').')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&foldmethod'', '''.getbufvar(originalBuffer, '&foldmethod').''')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&foldlevel'', '''.getbufvar(originalBuffer, '&foldlevel').''')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&scrollbind'', '.getbufvar(originalBuffer, '&scrollbind').')'
+                                                               \ . '|call setbufvar('.originalBuffer.', ''&wrap'', '.getbufvar(originalBuffer, '&wrap').')'
+                                                               \ . '|if &foldmethod==''manual''|execute ''normal zE''|endif'
+                                       diffthis
+                                       wincmd w
+                               else
+                                       " Adding a window to an existing vimdiff
+                                       let t:vcsCommandVimDiffScratchList += [resultBuffer]
+                               endif
+                       endif
+
+                       let t:vcsCommandVimDiffSourceBuffer = originalBuffer
+
+                       " Avoid executing the modeline in the current buffer after the autocommand.
+
+                       let currentBuffer = bufnr('%')
+                       let saveModeline = getbufvar(currentBuffer, '&modeline')
+                       try
+                               call setbufvar(currentBuffer, '&modeline', 0)
+                               silent do VCSCommand User VCSVimDiffFinish
+                       finally
+                               call setbufvar(currentBuffer, '&modeline', saveModeline)
+                       endtry
+                       return resultBuffer
+               finally
+                       let s:isEditFileRunning = s:isEditFileRunning - 1
+               endtry
+       catch
+               call s:ReportError(v:exception)
+               return -1
+       endtry
+endfunction
+
+" Section: Public functions {{{1
+
+" Function: VCSCommandGetVCSType() {{{2
+" Sets the b:VCSCommandVCSType variable in the given buffer to the
+" appropriate source control system name.
+"
+" This uses the Identify extension function to test the buffer.  If the
+" Identify function returns VCSCOMMAND_IDENTIFY_EXACT, the match is considered
+" exact.  If the Identify function returns VCSCOMMAND_IDENTIFY_INEXACT, the
+" match is considered inexact, and is only applied if no exact match is found.
+" Multiple inexact matches is currently considered an error.
+
+function! VCSCommandGetVCSType(buffer)
+       let vcsType = getbufvar(a:buffer, 'VCSCommandVCSType')
+       if strlen(vcsType) > 0
+               return vcsType
+       endif
+       if exists("g:VCSCommandVCSTypeOverride")
+               let fullpath = fnamemodify(bufname(a:buffer), ':p')
+               for [path, vcsType] in g:VCSCommandVCSTypeOverride
+                       if match(fullpath, path) > -1
+                               call setbufvar(a:buffer, 'VCSCommandVCSType', vcsType)
+                               return vcsType
+                       endif
+               endfor
+       endif
+       let matches = []
+       for vcsType in keys(s:plugins)
+               let identified = s:plugins[vcsType][1].Identify(a:buffer)
+               if identified
+                       if identified == g:VCSCOMMAND_IDENTIFY_EXACT
+                               let matches = [vcsType]
+                               break
+                       else
+                               let matches += [vcsType]
+                       endif
+               endif
+       endfor
+       if len(matches) == 1
+               call setbufvar(a:buffer, 'VCSCommandVCSType', matches[0])
+               return matches[0]
+       elseif len(matches) == 0
+               throw 'No suitable plugin'
+       else
+               throw 'Too many matching VCS:  ' . join(matches)
+       endif
+endfunction
+
+" Function: VCSCommandChdir(directory) {{{2
+" Changes the current directory, respecting :lcd changes.
+
+function! VCSCommandChdir(directory)
+       let command = 'cd'
+       if exists("*haslocaldir") && haslocaldir()
+               let command = 'lcd'
+       endif
+       execute command escape(a:directory, ' ')
+endfunction
+
+" Function: VCSCommandChangeToCurrentFileDir() {{{2
+" Go to the directory in which the given file is located.
+
+function! VCSCommandChangeToCurrentFileDir(fileName)
+       let oldCwd = getcwd()
+       let newCwd = fnamemodify(resolve(a:fileName), ':p:h')
+       if strlen(newCwd) > 0
+               call VCSCommandChdir(newCwd)
+       endif
+       return oldCwd
+endfunction
+
+" Function: VCSCommandGetOriginalBuffer(vcsBuffer) {{{2
+" Attempts to locate the original file to which VCS operations were applied
+" for a given buffer.
+
+function! VCSCommandGetOriginalBuffer(vcsBuffer)
+       let origBuffer = getbufvar(a:vcsBuffer, 'VCSCommandOriginalBuffer')
+       if origBuffer
+               if bufexists(origBuffer)
+                       return origBuffer
+               else
+                       " Original buffer no longer exists.
+                       throw 'Original buffer for this VCS buffer no longer exists.'
+               endif
+       else
+               " No original buffer
+               return a:vcsBuffer
+       endif
+endfunction
+
+" Function: VCSCommandRegisterModule(name, file, commandMap) {{{2
+" Allows VCS modules to register themselves.
+
+function! VCSCommandRegisterModule(name, path, commandMap, mappingMap)
+       let s:plugins[a:name] = [a:path, a:commandMap, a:mappingMap]
+       if !empty(a:mappingMap)
+                               \ && !VCSCommandGetOption('VCSCommandDisableMappings', 0)
+                               \ && !VCSCommandGetOption('VCSCommandDisableExtensionMappings', 0)
+               for shortcut in keys(a:mappingMap)
+                       let expansion = ":call <SID>ExecuteExtensionMapping('" . shortcut . "')<CR>"
+                       call s:CreateMapping(shortcut, expansion, a:name . " extension mapping " . shortcut)
+               endfor
+       endif
+       return s:VCSCommandUtility
+endfunction
+
+" Function: VCSCommandDoCommand(cmd, cmdName, statusText, [options]) {{{2
+" General skeleton for VCS function execution.  The given command is executed
+" after appending the current buffer name (or substituting it for
+" <VCSCOMMANDFILE>, if such a token is present).  The output is captured in a
+" new buffer.
+"
+" The optional 'options' Dictionary may contain the following options:
+"      allowNonZeroExit:  if non-zero, if the underlying VCS command has a
+"              non-zero exit status, the command is still considered
+"              successfuly.  This defaults to zero.
+" Returns: name of the new command buffer containing the command results
+
+function! VCSCommandDoCommand(cmd, cmdName, statusText, options)
+       let allowNonZeroExit = 0
+       if has_key(a:options, 'allowNonZeroExit')
+               let allowNonZeroExit = a:options.allowNonZeroExit
+       endif
+
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       if originalBuffer == -1
+               throw 'Original buffer no longer exists, aborting.'
+       endif
+
+       let path = resolve(bufname(originalBuffer))
+
+       " Work with netrw or other systems where a directory listing is displayed in
+       " a buffer.
+
+       if isdirectory(path)
+               let fileName = '.'
+       else
+               let fileName = fnamemodify(path, ':t')
+       endif
+
+       if match(a:cmd, '<VCSCOMMANDFILE>') > 0
+               let fullCmd = substitute(a:cmd, '<VCSCOMMANDFILE>', fileName, 'g')
+       else
+               let fullCmd = a:cmd . ' -- "' . fileName . '"'
+       endif
+
+       " Change to the directory of the current buffer.  This is done for CVS, but
+       " is left in for other systems as it does not affect them negatively.
+
+       let oldCwd = VCSCommandChangeToCurrentFileDir(path)
+       try
+               let output = s:VCSCommandUtility.system(fullCmd)
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+
+       " HACK:  if line endings in the repository have been corrupted, the output
+       " of the command will be confused.
+       let output = substitute(output, "\r", '', 'g')
+
+       if v:shell_error && !allowNonZeroExit
+               if strlen(output) == 0
+                       throw 'Version control command failed'
+               else
+                       let output = substitute(output, '\n', '  ', 'g')
+                       throw 'Version control command failed:  ' . output
+               endif
+       endif
+
+       if strlen(output) == 0
+               " Handle case of no output.  In this case, it is important to check the
+               " file status, especially since cvs edit/unedit may change the attributes
+               " of the file with no visible output.
+
+               checktime
+               return 0
+       endif
+
+       call s:EditFile(a:cmdName, originalBuffer, a:statusText)
+
+       silent 0put=output
+
+       " The last command left a blank line at the end of the buffer.  If the
+       " last line is folded (a side effect of the 'put') then the attempt to
+       " remove the blank line will kill the last fold.
+       "
+       " This could be fixed by explicitly detecting whether the last line is
+       " within a fold, but I prefer to simply unfold the result buffer altogether.
+
+       if has('folding')
+               normal zR
+       endif
+
+       $d
+       1
+
+       " Define the environment and execute user-defined hooks.
+
+       silent do VCSCommand User VCSBufferCreated
+       return bufnr('%')
+endfunction
+
+" Function: VCSCommandGetOption(name, default) {{{2
+" Grab a user-specified option to override the default provided.  Options are
+" searched in the window, buffer, then global spaces.
+
+function! VCSCommandGetOption(name, default)
+       if has_key(s:optionOverrides, a:name) && len(s:optionOverrides[a:name]) > 0
+               return s:optionOverrides[a:name][-1]
+       elseif exists('w:' . a:name)
+               return w:{a:name}
+       elseif exists('b:' . a:name)
+               return b:{a:name}
+       elseif exists('g:' . a:name)
+               return g:{a:name}
+       else
+               return a:default
+       endif
+endfunction
+
+" Function: VCSCommandDisableBufferSetup() {{{2
+" Global function for deactivating the buffer autovariables.
+
+function! VCSCommandDisableBufferSetup()
+       let g:VCSCommandEnableBufferSetup = 0
+       silent! augroup! VCSCommandPlugin
+endfunction
+
+" Function: VCSCommandEnableBufferSetup() {{{2
+" Global function for activating the buffer autovariables.
+
+function! VCSCommandEnableBufferSetup()
+       let g:VCSCommandEnableBufferSetup = 1
+       augroup VCSCommandPlugin
+               au!
+               au BufEnter * call s:SetupBuffer()
+       augroup END
+
+       " Only auto-load if the plugin is fully loaded.  This gives other plugins a
+       " chance to run.
+       if g:loaded_VCSCommand == 2
+               call s:SetupBuffer()
+       endif
+endfunction
+
+" Function: VCSCommandGetStatusLine() {{{2
+" Default (sample) status line entry for VCS-controlled files.  This is only
+" useful if VCS-managed buffer mode is on (see the VCSCommandEnableBufferSetup
+" variable for how to do this).
+
+function! VCSCommandGetStatusLine()
+       if exists('b:VCSCommandCommand')
+               " This is a result buffer.  Return nothing because the buffer name
+               " contains information already.
+               return ''
+       endif
+
+       if exists('b:VCSCommandVCSType')
+                               \ && exists('g:VCSCommandEnableBufferSetup')
+                               \ && g:VCSCommandEnableBufferSetup
+                               \ && exists('b:VCSCommandBufferInfo')
+               return '[' . join(extend([b:VCSCommandVCSType], b:VCSCommandBufferInfo), ' ') . ']'
+       else
+               return ''
+       endif
+endfunction
+
+" Section: Command definitions {{{1
+" Section: Primary commands {{{2
+com! -nargs=* VCSAdd call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Add', [<f-args>]))
+com! -nargs=* -bang VCSAnnotate call s:VCSAnnotate(<q-bang>, <f-args>)
+com! -nargs=* -bang VCSBlame call s:VCSAnnotate(<q-bang>, <f-args>)
+com! -nargs=? -bang VCSCommit call s:VCSCommit(<q-bang>, <q-args>)
+com! -nargs=* VCSDelete call s:ExecuteVCSCommand('Delete', [<f-args>])
+com! -nargs=* VCSDiff call s:VCSDiff(<f-args>)
+com! -nargs=0 -bang VCSGotoOriginal call s:VCSGotoOriginal(<q-bang>)
+com! -nargs=* VCSInfo call s:ExecuteVCSCommand('Info', [<f-args>])
+com! -nargs=* VCSLock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Lock', [<f-args>]))
+com! -nargs=* VCSLog call s:ExecuteVCSCommand('Log', [<f-args>])
+com! -nargs=* VCSRemove call s:ExecuteVCSCommand('Delete', [<f-args>])
+com! -nargs=0 VCSRevert call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Revert', []))
+com! -nargs=? VCSReview call s:VCSReview(<f-args>)
+com! -nargs=* VCSStatus call s:ExecuteVCSCommand('Status', [<f-args>])
+com! -nargs=* VCSUnlock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Unlock', [<f-args>]))
+com! -nargs=0 VCSUpdate call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Update', []))
+com! -nargs=* VCSVimDiff call s:VCSVimDiff(<f-args>)
+
+" Section: VCS buffer management commands {{{2
+com! VCSCommandDisableBufferSetup call VCSCommandDisableBufferSetup()
+com! VCSCommandEnableBufferSetup call VCSCommandEnableBufferSetup()
+
+" Allow reloading VCSCommand.vim
+com! VCSReload let savedPlugins = s:plugins|let s:plugins = {}|call s:ClearMenu()|unlet! g:loaded_VCSCommand|runtime plugin/vcscommand.vim|for plugin in values(savedPlugins)|execute 'source' plugin[0]|endfor|unlet savedPlugins
+
+" Section: Plugin command mappings {{{1
+nnoremap <silent> <Plug>VCSAdd :VCSAdd<CR>
+nnoremap <silent> <Plug>VCSAnnotate :VCSAnnotate<CR>
+nnoremap <silent> <Plug>VCSCommit :VCSCommit<CR>
+nnoremap <silent> <Plug>VCSDelete :VCSDelete<CR>
+nnoremap <silent> <Plug>VCSDiff :VCSDiff<CR>
+nnoremap <silent> <Plug>VCSGotoOriginal :VCSGotoOriginal<CR>
+nnoremap <silent> <Plug>VCSClearAndGotoOriginal :VCSGotoOriginal!<CR>
+nnoremap <silent> <Plug>VCSInfo :VCSInfo<CR>
+nnoremap <silent> <Plug>VCSLock :VCSLock<CR>
+nnoremap <silent> <Plug>VCSLog :VCSLog<CR>
+nnoremap <silent> <Plug>VCSRevert :VCSRevert<CR>
+nnoremap <silent> <Plug>VCSReview :VCSReview<CR>
+nnoremap <silent> <Plug>VCSSplitAnnotate :VCSAnnotate!<CR>
+nnoremap <silent> <Plug>VCSStatus :VCSStatus<CR>
+nnoremap <silent> <Plug>VCSUnlock :VCSUnlock<CR>
+nnoremap <silent> <Plug>VCSUpdate :VCSUpdate<CR>
+nnoremap <silent> <Plug>VCSVimDiff :VCSVimDiff<CR>
+
+" Section: Default mappings {{{1
+
+let s:defaultMappings = [
+                       \['a', 'VCSAdd'],
+                       \['c', 'VCSCommit'],
+                       \['D', 'VCSDelete'],
+                       \['d', 'VCSDiff'],
+                       \['G', 'VCSClearAndGotoOriginal'],
+                       \['g', 'VCSGotoOriginal'],
+                       \['i', 'VCSInfo'],
+                       \['L', 'VCSLock'],
+                       \['l', 'VCSLog'],
+                       \['N', 'VCSSplitAnnotate'],
+                       \['n', 'VCSAnnotate'],
+                       \['q', 'VCSRevert'],
+                       \['r', 'VCSReview'],
+                       \['s', 'VCSStatus'],
+                       \['U', 'VCSUnlock'],
+                       \['u', 'VCSUpdate'],
+                       \['v', 'VCSVimDiff'],
+                       \]
+
+if !VCSCommandGetOption('VCSCommandDisableMappings', 0)
+       for [s:shortcut, s:vcsFunction] in VCSCommandGetOption('VCSCommandMappings', s:defaultMappings)
+               call s:CreateMapping(s:shortcut, '<Plug>' . s:vcsFunction, '''' . s:vcsFunction . '''')
+       endfor
+       unlet s:shortcut s:vcsFunction
+endif
+unlet s:defaultMappings
+
+" Section: Menu items {{{1
+
+let s:menuEnabled = !VCSCommandGetOption('VCSCommandDisableMenu', 0)
+let s:menuRoot = VCSCommandGetOption('VCSCommandMenuRoot', '&Plugin.VCS')
+let s:menuPriority = VCSCommandGetOption('VCSCommandMenuPriority', '')
+
+for [s:shortcut, s:command] in [
+                       \['&Add', '<Plug>VCSAdd'],
+                       \['A&nnotate', '<Plug>VCSAnnotate'],
+                       \['&Commit', '<Plug>VCSCommit'],
+                       \['Delete', '<Plug>VCSDelete'],
+                       \['&Diff', '<Plug>VCSDiff'],
+                       \['&Info', '<Plug>VCSInfo'],
+                       \['&Log', '<Plug>VCSLog'],
+                       \['Revert', '<Plug>VCSRevert'],
+                       \['&Review', '<Plug>VCSReview'],
+                       \['&Status', '<Plug>VCSStatus'],
+                       \['&Update', '<Plug>VCSUpdate'],
+                       \['&VimDiff', '<Plug>VCSVimDiff']
+                       \]
+       call s:VCSCommandUtility.addMenuItem(s:shortcut, s:command)
+endfor
+unlet s:shortcut s:command
+
+" Section: Autocommands to restore vimdiff state {{{1
+augroup VimDiffRestore
+       au!
+       au BufUnload * call s:VimDiffRestore(str2nr(expand('<abuf>')))
+augroup END
+
+" Section: Optional activation of buffer management {{{1
+
+if VCSCommandGetOption('VCSCommandEnableBufferSetup', 0)
+       call VCSCommandEnableBufferSetup()
+endif
+
+" Section: VIM shutdown hook {{{1
+
+" Close all result buffers when VIM exits, to prevent them from being restored
+" via viminfo.
+
+" Function: s:CloseAllResultBuffers() {{{2
+" Closes all vcscommand result buffers.
+function! s:CloseAllResultBuffers()
+       " This avoids using bufdo as that may load buffers already loaded in another
+       " vim process, resulting in an error.
+       let buffnr = 1
+       let buffmaxnr = bufnr('$')
+       while buffnr <= buffmaxnr
+               if getbufvar(buffnr, 'VCSCommandOriginalBuffer') != ""
+                       execute 'bw' buffnr
+               endif
+               let buffnr = buffnr + 1
+       endwhile
+endfunction
+
+augroup VCSCommandVIMShutdown
+       au!
+       au VimLeavePre * call s:CloseAllResultBuffers()
+augroup END
+
+" Section: Plugin completion {{{1
+
+let loaded_VCSCommand = 2
+
+silent do VCSCommand User VCSPluginFinish
+
+let &cpo = s:save_cpo
diff --git a/.vim/plugin/vcscvs.vim b/.vim/plugin/vcscvs.vim
new file mode 100644 (file)
index 0000000..73d6a7f
--- /dev/null
@@ -0,0 +1,449 @@
+" vim600: set foldmethod=marker:
+"
+" CVS extension for VCSCommand.
+"
+" Maintainer:    Bob Hiestand <bob.hiestand@gmail.com>
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+"
+" Section: Documentation {{{1
+"
+" Command documentation {{{2
+"
+" The following commands only apply to files under CVS source control.
+"
+" CVSEdit          Performs "cvs edit" on the current file.
+"
+" CVSEditors       Performs "cvs editors" on the current file.
+"
+" CVSUnedit        Performs "cvs unedit" on the current file.
+"
+" CVSWatch         Takes an argument which must be one of [on|off|add|remove].
+"                  Performs "cvs watch" with the given argument on the current
+"                  file.
+"
+" CVSWatchers      Performs "cvs watchers" on the current file.
+"
+" CVSWatchAdd      Alias for "CVSWatch add"
+"
+" CVSWatchOn       Alias for "CVSWatch on"
+"
+" CVSWatchOff      Alias for "CVSWatch off"
+"
+" CVSWatchRemove   Alias for "CVSWatch remove"
+"
+" Mapping documentation: {{{2
+"
+" By default, a mapping is defined for each command.  User-provided mappings
+" can be used instead by mapping to <Plug>CommandName, for instance:
+"
+" nnoremap ,ce <Plug>CVSEdit
+"
+" The default mappings are as follow:
+"
+"   <Leader>ce CVSEdit
+"   <Leader>cE CVSEditors
+"   <Leader>ct CVSUnedit
+"   <Leader>cwv CVSWatchers
+"   <Leader>cwa CVSWatchAdd
+"   <Leader>cwn CVSWatchOn
+"   <Leader>cwf CVSWatchOff
+"   <Leader>cwr CVSWatchRemove
+"
+" Options documentation: {{{2
+"
+" VCSCommandCVSExec
+"   This variable specifies the CVS executable.  If not set, it defaults to
+"   'cvs' executed from the user's executable path.
+"
+" VCSCommandCVSDiffOpt
+"   This variable, if set, determines the options passed to the cvs diff
+"   command.  If not set, it defaults to 'u'.
+
+" Section: Plugin header {{{1
+
+if exists('VCSCommandDisableAll')
+       finish
+endif
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+runtime plugin/vcscommand.vim
+
+if !executable(VCSCommandGetOption('VCSCommandCVSExec', 'cvs'))
+       " CVS is not installed
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Variable initialization {{{1
+
+let s:cvsFunctions = {}
+
+" Section: Utility functions {{{1
+
+" Function: s:Executable() {{{2
+" Returns the executable used to invoke cvs suitable for use in a shell
+" command.
+function! s:Executable()
+       return shellescape(VCSCommandGetOption('VCSCommandCVSExec', 'cvs'))
+endfunction
+
+" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
+" Wrapper to VCSCommandDoCommand to add the name of the CVS executable to the
+" command argument.
+function! s:DoCommand(cmd, cmdName, statusText, options)
+       if VCSCommandGetVCSType(expand('%')) == 'CVS'
+               let fullCmd = s:Executable() . ' ' . a:cmd
+               let ret = VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options)
+
+               if ret > 0
+                       if getline(line('$')) =~ '^cvs \w\+: closing down connection'
+                               $d
+                               1
+                       endif
+
+               endif
+
+               return ret
+       else
+               throw 'CVS VCSCommand plugin called on non-CVS item.'
+       endif
+endfunction
+
+" Function: s:GetRevision() {{{2
+" Function for retrieving the current buffer's revision number.
+" Returns: Revision number or an empty string if an error occurs.
+
+function! s:GetRevision()
+       if !exists('b:VCSCommandBufferInfo')
+               let b:VCSCommandBufferInfo =  s:cvsFunctions.GetBufferInfo()
+       endif
+
+       if len(b:VCSCommandBufferInfo) > 0
+               return b:VCSCommandBufferInfo[0]
+       else
+               return ''
+       endif
+endfunction
+
+" Section: VCS function implementations {{{1
+
+" Function: s:cvsFunctions.Identify(buffer) {{{2
+function! s:cvsFunctions.Identify(buffer)
+       let fileName = resolve(bufname(a:buffer))
+       if isdirectory(fileName)
+               let directoryName = fileName
+       else
+               let directoryName = fnamemodify(fileName, ':h')
+       endif
+       if strlen(directoryName) > 0
+               let CVSRoot = directoryName . '/CVS/Root'
+       else
+               let CVSRoot = 'CVS/Root'
+       endif
+       if filereadable(CVSRoot)
+               return 1
+       else
+               return 0
+       endif
+endfunction
+
+" Function: s:cvsFunctions.Add(argList) {{{2
+function! s:cvsFunctions.Add(argList)
+       return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:cvsFunctions.Annotate(argList) {{{2
+function! s:cvsFunctions.Annotate(argList)
+       if len(a:argList) == 0
+               if &filetype == 'CVSannotate'
+                       " This is a CVSAnnotate buffer.  Perform annotation of the version
+                       " indicated by the current line.
+                       let caption = matchstr(getline('.'),'\v^[0-9.]+')
+
+                       if VCSCommandGetOption('VCSCommandCVSAnnotateParent', 0) != 0
+                               if caption != '1.1'
+                                       let revmaj = matchstr(caption,'\v[0-9.]+\ze\.[0-9]+')
+                                       let revmin = matchstr(caption,'\v[0-9.]+\.\zs[0-9]+') - 1
+                                       if revmin == 0
+                                               " Jump to ancestor branch
+                                               let caption = matchstr(revmaj,'\v[0-9.]+\ze\.[0-9]+')
+                                       else
+                                               let caption = revmaj . "." .  revmin
+                                       endif
+                               endif
+                       endif
+
+                       let options = ['-r' . caption]
+               else
+                       " CVS defaults to pulling HEAD, regardless of current branch.
+                       " Therefore, always pass desired revision.
+                       let caption = ''
+                       let options = ['-r' .  s:GetRevision()]
+               endif
+       elseif len(a:argList) == 1 && a:argList[0] !~ '^-'
+               let caption = a:argList[0]
+               let options = ['-r' . caption]
+       else
+               let caption = join(a:argList)
+               let options = a:argList
+       endif
+
+       let resultBuffer = s:DoCommand(join(['-q', 'annotate'] + options), 'annotate', caption, {})
+       if resultBuffer > 0
+               " Remove header lines from standard error
+               silent v/^\d\+\%(\.\d\+\)\+/d
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:cvsFunctions.Commit(argList) {{{2
+function! s:cvsFunctions.Commit(argList)
+       let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {})
+       if resultBuffer == 0
+               echomsg 'No commit needed.'
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:cvsFunctions.Delete() {{{2
+" By default, use the -f option to remove the file first.  If options are
+" passed in, use those instead.
+function! s:cvsFunctions.Delete(argList)
+       let options = ['-f']
+       let caption = ''
+       if len(a:argList) > 0
+               let options = a:argList
+               let caption = join(a:argList, ' ')
+       endif
+       return s:DoCommand(join(['remove'] + options, ' '), 'delete', caption, {})
+endfunction
+
+" Function: s:cvsFunctions.Diff(argList) {{{2
+function! s:cvsFunctions.Diff(argList)
+       if len(a:argList) == 0
+               let revOptions = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let revOptions = ['-r' . join(a:argList, ' -r')]
+               let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')'
+       else
+               " Pass-through
+               let caption = join(a:argList, ' ')
+               let revOptions = a:argList
+       endif
+
+       let cvsDiffOpt = VCSCommandGetOption('VCSCommandCVSDiffOpt', 'u')
+       if cvsDiffOpt == ''
+               let diffOptions = []
+       else
+               let diffOptions = ['-' . cvsDiffOpt]
+       endif
+
+       return s:DoCommand(join(['diff'] + diffOptions + revOptions), 'diff', caption, {'allowNonZeroExit': 1})
+endfunction
+
+" Function: s:cvsFunctions.GetBufferInfo() {{{2
+" Provides version control details for the current file.  Current version
+" number and current repository version number are required to be returned by
+" the vcscommand plugin.  This CVS extension adds branch name to the return
+" list as well.
+" Returns: List of results:  [revision, repository, branch]
+
+function! s:cvsFunctions.GetBufferInfo()
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       let fileName = bufname(originalBuffer)
+       if isdirectory(fileName)
+               let tag = ''
+               if filereadable(fileName . '/CVS/Tag')
+                       let tagFile = readfile(fileName . '/CVS/Tag')
+                       if len(tagFile) == 1
+                               let tag = substitute(tagFile[0], '^T', '', '')
+                       endif
+               endif
+               return [tag]
+       endif
+       let realFileName = fnamemodify(resolve(fileName), ':t')
+       if !filereadable(fileName)
+               return ['Unknown']
+       endif
+       let oldCwd = VCSCommandChangeToCurrentFileDir(fileName)
+       try
+               let statusText=s:VCSCommandUtility.system(s:Executable() . ' status -- "' . realFileName . '"')
+               if(v:shell_error)
+                       return []
+               endif
+               let revision=substitute(statusText, '^\_.*Working revision:\s*\(\d\+\%(\.\d\+\)\+\|New file!\)\_.*$', '\1', '')
+
+               " We can still be in a CVS-controlled directory without this being a CVS
+               " file
+               if match(revision, '^New file!$') >= 0
+                       let revision='New'
+               elseif match(revision, '^\d\+\.\d\+\%(\.\d\+\.\d\+\)*$') <0
+                       return ['Unknown']
+               endif
+
+               let branch=substitute(statusText, '^\_.*Sticky Tag:\s\+\(\d\+\%(\.\d\+\)\+\|\a[A-Za-z0-9-_]*\|(none)\).*$', '\1', '')
+               let repository=substitute(statusText, '^\_.*Repository revision:\s*\(\d\+\%(\.\d\+\)\+\|New file!\|No revision control file\)\_.*$', '\1', '')
+               let repository=substitute(repository, '^New file!\|No revision control file$', 'New', '')
+               return [revision, repository, branch]
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+endfunction
+
+" Function: s:cvsFunctions.Log() {{{2
+function! s:cvsFunctions.Log(argList)
+       if len(a:argList) == 0
+               let options = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let options = ['-r' . join(a:argList, ':')]
+               let caption = options[0]
+       else
+               " Pass-through
+               let options = a:argList
+               let caption = join(a:argList, ' ')
+       endif
+
+       return s:DoCommand(join(['log'] + options), 'log', caption, {})
+endfunction
+
+" Function: s:cvsFunctions.Revert(argList) {{{2
+function! s:cvsFunctions.Revert(argList)
+       return s:DoCommand('update -C', 'revert', '', {})
+endfunction
+
+" Function: s:cvsFunctions.Review(argList) {{{2
+function! s:cvsFunctions.Review(argList)
+       if len(a:argList) == 0
+               let versiontag = '(current)'
+               let versionOption = ''
+       else
+               let versiontag = a:argList[0]
+               let versionOption = ' -r ' . versiontag . ' '
+       endif
+
+       return s:DoCommand('-q update -p' . versionOption, 'review', versiontag, {})
+endfunction
+
+" Function: s:cvsFunctions.Status(argList) {{{2
+function! s:cvsFunctions.Status(argList)
+       return s:DoCommand(join(['status'] + a:argList, ' '), 'status', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:cvsFunctions.Update(argList) {{{2
+function! s:cvsFunctions.Update(argList)
+       return s:DoCommand('update', 'update', '', {})
+endfunction
+
+" Section: CVS-specific functions {{{1
+
+" Function: s:CVSEdit() {{{2
+function! s:CVSEdit()
+       return s:DoCommand('edit', 'cvsedit', '', {})
+endfunction
+
+" Function: s:CVSEditors() {{{2
+function! s:CVSEditors()
+       return s:DoCommand('editors', 'cvseditors', '', {})
+endfunction
+
+" Function: s:CVSUnedit() {{{2
+function! s:CVSUnedit()
+       return s:DoCommand('unedit', 'cvsunedit', '', {})
+endfunction
+
+" Function: s:CVSWatch(onoff) {{{2
+function! s:CVSWatch(onoff)
+       if a:onoff !~ '^\c\%(on\|off\|add\|remove\)$'
+               echoerr 'Argument to CVSWatch must be one of [on|off|add|remove]'
+               return -1
+       end
+       return s:DoCommand('watch ' . tolower(a:onoff), 'cvswatch', '', {})
+endfunction
+
+" Function: s:CVSWatchers() {{{2
+function! s:CVSWatchers()
+       return s:DoCommand('watchers', 'cvswatchers', '', {})
+endfunction
+
+" Annotate setting {{{2
+let s:cvsFunctions.AnnotateSplitRegex = '): '
+
+" Section: Command definitions {{{1
+" Section: Primary commands {{{2
+com! CVSEdit call s:CVSEdit()
+com! CVSEditors call s:CVSEditors()
+com! CVSUnedit call s:CVSUnedit()
+com! -nargs=1 CVSWatch call s:CVSWatch(<f-args>)
+com! CVSWatchAdd call s:CVSWatch('add')
+com! CVSWatchOn call s:CVSWatch('on')
+com! CVSWatchOff call s:CVSWatch('off')
+com! CVSWatchRemove call s:CVSWatch('remove')
+com! CVSWatchers call s:CVSWatchers()
+
+" Section: Plugin command mappings {{{1
+
+let s:cvsExtensionMappings = {}
+let mappingInfo = [
+                       \['CVSEdit', 'CVSEdit', 'e'],
+                       \['CVSEditors', 'CVSEditors', 'E'],
+                       \['CVSUnedit', 'CVSUnedit', 't'],
+                       \['CVSWatchers', 'CVSWatchers', 'wv'],
+                       \['CVSWatchAdd', 'CVSWatch add', 'wa'],
+                       \['CVSWatchOff', 'CVSWatch off', 'wf'],
+                       \['CVSWatchOn', 'CVSWatch on', 'wn'],
+                       \['CVSWatchRemove', 'CVSWatch remove', 'wr']
+                       \]
+
+for [pluginName, commandText, shortCut] in mappingInfo
+       execute 'nnoremap <silent> <Plug>' . pluginName . ' :' . commandText . '<CR>'
+       if !hasmapto('<Plug>' . pluginName)
+               let s:cvsExtensionMappings[shortCut] = commandText
+       endif
+endfor
+
+" Section: Plugin Registration {{{1
+let s:VCSCommandUtility = VCSCommandRegisterModule('CVS', expand('<sfile>'), s:cvsFunctions, s:cvsExtensionMappings)
+
+" Section: Menu items {{{1
+for [s:shortcut, s:command] in [
+                       \['CVS.&Edit', '<Plug>CVSEdit'],
+                       \['CVS.Ed&itors', '<Plug>CVSEditors'],
+                       \['CVS.Unedi&t', '<Plug>CVSUnedit'],
+                       \['CVS.&Watchers', '<Plug>CVSWatchers'],
+                       \['CVS.WatchAdd', '<Plug>CVSWatchAdd'],
+                       \['CVS.WatchOn', '<Plug>CVSWatchOn'],
+                       \['CVS.WatchOff', '<Plug>CVSWatchOff'],
+                       \['CVS.WatchRemove', '<Plug>CVSWatchRemove']
+                       \]
+       call s:VCSCommandUtility.addMenuItem(s:shortcut, s:command)
+endfor
+unlet s:shortcut s:command
+
+let &cpo = s:save_cpo
diff --git a/.vim/plugin/vcsgit.vim b/.vim/plugin/vcsgit.vim
new file mode 100644 (file)
index 0000000..4e299d9
--- /dev/null
@@ -0,0 +1,247 @@
+" vim600: set foldmethod=marker:
+"
+" git extension for VCSCommand.
+"
+" Maintainer:    Bob Hiestand <bob.hiestand@gmail.com>
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+"
+" Section: Documentation {{{1
+"
+" Options documentation: {{{2
+"
+" VCSCommandGitExec
+"   This variable specifies the git executable.  If not set, it defaults to
+"   'git' executed from the user's executable path.
+"
+" VCSCommandGitDiffOpt
+"   This variable, if set, determines the default options passed to the
+"   VCSDiff command.  If any options (starting with '-') are passed to the
+"   command, this variable is not used.
+
+" Section: Plugin header {{{1
+
+if exists('VCSCommandDisableAll')
+       finish
+endif
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+runtime plugin/vcscommand.vim
+
+if !executable(VCSCommandGetOption('VCSCommandGitExec', 'git'))
+       " git is not installed
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Variable initialization {{{1
+
+let s:gitFunctions = {}
+
+" Section: Utility functions {{{1
+
+" Function: s:Executable() {{{2
+" Returns the executable used to invoke git suitable for use in a shell
+" command.
+function! s:Executable()
+       return shellescape(VCSCommandGetOption('VCSCommandGitExec', 'git'))
+endfunction
+
+" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
+" Wrapper to VCSCommandDoCommand to add the name of the git executable to the
+" command argument.
+function! s:DoCommand(cmd, cmdName, statusText, options)
+       if VCSCommandGetVCSType(expand('%')) == 'git'
+               let fullCmd = s:Executable() . ' ' . a:cmd
+               return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options)
+       else
+               throw 'git VCSCommand plugin called on non-git item.'
+       endif
+endfunction
+
+" Section: VCS function implementations {{{1
+
+" Function: s:gitFunctions.Identify(buffer) {{{2
+" This function only returns an inexact match due to the detection method used
+" by git, which simply traverses the directory structure upward.
+function! s:gitFunctions.Identify(buffer)
+       let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname(a:buffer)))
+       try
+               call s:VCSCommandUtility.system(s:Executable() . ' rev-parse --is-inside-work-tree')
+               if(v:shell_error)
+                       return 0
+               else
+                       return g:VCSCOMMAND_IDENTIFY_INEXACT
+               endif
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+endfunction
+
+" Function: s:gitFunctions.Add(argList) {{{2
+function! s:gitFunctions.Add(argList)
+       return s:DoCommand(join(['add'] + ['-v'] + a:argList, ' '), 'add', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:gitFunctions.Annotate(argList) {{{2
+function! s:gitFunctions.Annotate(argList)
+       if len(a:argList) == 0
+               if &filetype == 'gitannotate'
+                       " Perform annotation of the version indicated by the current line.
+                       let options = matchstr(getline('.'),'^\x\+')
+               else
+                       let options = ''
+               endif
+       elseif len(a:argList) == 1 && a:argList[0] !~ '^-'
+               let options = a:argList[0]
+       else
+               let options = join(a:argList, ' ')
+       endif
+
+       return s:DoCommand('blame ' . options, 'annotate', options, {})
+endfunction
+
+" Function: s:gitFunctions.Commit(argList) {{{2
+function! s:gitFunctions.Commit(argList)
+       try
+               return s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {})
+       catch /\m^Version control command failed.*nothing\%( added\)\? to commit/
+               echomsg 'No commit needed.'
+       endtry
+endfunction
+
+" Function: s:gitFunctions.Delete() {{{2
+" All options are passed through.
+function! s:gitFunctions.Delete(argList)
+       let options = a:argList
+       let caption = join(a:argList, ' ')
+       return s:DoCommand(join(['rm'] + options, ' '), 'delete', caption, {})
+endfunction
+
+" Function: s:gitFunctions.Diff(argList) {{{2
+" Pass-through call to git-diff.  If no options (starting with '-') are found,
+" then the options in the 'VCSCommandGitDiffOpt' variable are added.
+function! s:gitFunctions.Diff(argList)
+       let gitDiffOpt = VCSCommandGetOption('VCSCommandGitDiffOpt', '')
+       if gitDiffOpt == ''
+               let diffOptions = []
+       else
+               let diffOptions = [gitDiffOpt]
+               for arg in a:argList
+                       if arg =~ '^-'
+                               let diffOptions = []
+                               break
+                       endif
+               endfor
+       endif
+
+       return s:DoCommand(join(['diff'] + diffOptions + a:argList), 'diff', join(a:argList), {})
+endfunction
+
+" Function: s:gitFunctions.GetBufferInfo() {{{2
+" Provides version control details for the current file.  Current version
+" number and current repository version number are required to be returned by
+" the vcscommand plugin.  This CVS extension adds branch name to the return
+" list as well.
+" Returns: List of results:  [revision, repository, branch]
+
+function! s:gitFunctions.GetBufferInfo()
+       let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname('%')))
+       try
+               let branch = substitute(s:VCSCommandUtility.system(s:Executable() . ' symbolic-ref -q HEAD'), '\n$', '', '')
+               if v:shell_error
+                       let branch = 'DETACHED'
+               else
+                       let branch = substitute(branch, '^refs/heads/', '', '')
+               endif
+
+               let info = [branch]
+
+               for method in split(VCSCommandGetOption('VCSCommandGitDescribeArgList', (',tags,all,always')), ',', 1)
+                       if method != ''
+                               let method = ' --' . method
+                       endif
+                       let tag = substitute(s:VCSCommandUtility.system(s:Executable() . ' describe' . method), '\n$', '', '')
+                       if !v:shell_error
+                               call add(info, tag)
+                               break
+                       endif
+               endfor
+
+               return info
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+endfunction
+
+" Function: s:gitFunctions.Log() {{{2
+function! s:gitFunctions.Log(argList)
+       return s:DoCommand(join(['log'] + a:argList), 'log', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:gitFunctions.Revert(argList) {{{2
+function! s:gitFunctions.Revert(argList)
+       return s:DoCommand('checkout', 'revert', '', {})
+endfunction
+
+" Function: s:gitFunctions.Review(argList) {{{2
+function! s:gitFunctions.Review(argList)
+       if len(a:argList) == 0
+               let revision = 'HEAD'
+       else
+               let revision = a:argList[0]
+       endif
+
+       let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname(VCSCommandGetOriginalBuffer('%'))))
+       try
+               let prefix = s:VCSCommandUtility.system(s:Executable() . ' rev-parse --show-prefix')
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+
+       let prefix = substitute(prefix, '\n$', '', '')
+       let blob = '"' . revision . ':' . prefix . '<VCSCOMMANDFILE>"'
+       return s:DoCommand('show ' . blob, 'review', revision, {})
+endfunction
+
+" Function: s:gitFunctions.Status(argList) {{{2
+function! s:gitFunctions.Status(argList)
+       return s:DoCommand(join(['status'] + a:argList), 'status', join(a:argList), {'allowNonZeroExit': 1})
+endfunction
+
+" Function: s:gitFunctions.Update(argList) {{{2
+function! s:gitFunctions.Update(argList)
+       throw "This command is not implemented for git because file-by-file update doesn't make much sense in that context.  If you have an idea for what it should do, please let me know."
+endfunction
+
+" Annotate setting {{{2
+let s:gitFunctions.AnnotateSplitRegex = ') '
+
+" Section: Plugin Registration {{{1
+let s:VCSCommandUtility = VCSCommandRegisterModule('git', expand('<sfile>'), s:gitFunctions, [])
+
+let &cpo = s:save_cpo
diff --git a/.vim/plugin/vcshg.vim b/.vim/plugin/vcshg.vim
new file mode 100644 (file)
index 0000000..2c616c0
--- /dev/null
@@ -0,0 +1,273 @@
+" vim600: set foldmethod=marker:
+"
+" Mercurial extension for VCSCommand.
+"
+" Maintainer:    Bob Hiestand <bob.hiestand@gmail.com>
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+"
+" Section: Documentation {{{1
+"
+" Options documentation: {{{2
+"
+" VCSCommandHGExec
+"   This variable specifies the mercurial executable.  If not set, it defaults
+"   to 'hg' executed from the user's executable path.
+"
+" VCSCommandHGDiffExt
+"   This variable, if set, sets the external diff program used by Subversion.
+"
+" VCSCommandHGDiffOpt
+"   This variable, if set, determines the options passed to the hg diff
+"   command (such as 'u', 'w', or 'b').
+
+" Section: Plugin header {{{1
+
+if exists('VCSCommandDisableAll')
+       finish
+endif
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+runtime plugin/vcscommand.vim
+
+if !executable(VCSCommandGetOption('VCSCommandHGExec', 'hg'))
+       " HG is not installed
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Variable initialization {{{1
+
+let s:hgFunctions = {}
+
+" Section: Utility functions {{{1
+
+" Function: s:Executable() {{{2
+" Returns the executable used to invoke hg suitable for use in a shell
+" command.
+function! s:Executable()
+       return shellescape(VCSCommandGetOption('VCSCommandHGExec', 'hg'))
+endfunction
+
+" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
+" Wrapper to VCSCommandDoCommand to add the name of the HG executable to the
+" command argument.
+function! s:DoCommand(cmd, cmdName, statusText, options)
+       if VCSCommandGetVCSType(expand('%')) == 'HG'
+               let fullCmd = s:Executable() . ' ' . a:cmd
+               return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options)
+       else
+               throw 'HG VCSCommand plugin called on non-HG item.'
+       endif
+endfunction
+
+" Section: VCS function implementations {{{1
+
+" Function: s:hgFunctions.Identify(buffer) {{{2
+function! s:hgFunctions.Identify(buffer)
+       let oldCwd = VCSCommandChangeToCurrentFileDir(resolve(bufname(a:buffer)))
+       try
+               call s:VCSCommandUtility.system(s:Executable() . ' root')
+               if(v:shell_error)
+                       return 0
+               else
+                       return g:VCSCOMMAND_IDENTIFY_INEXACT
+               endif
+       finally
+               call VCSCommandChdir(oldCwd)
+       endtry
+endfunction
+
+" Function: s:hgFunctions.Add() {{{2
+function! s:hgFunctions.Add(argList)
+       return s:DoCommand(join(['add -v'] + a:argList, ' '), 'add', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:hgFunctions.Annotate(argList) {{{2
+function! s:hgFunctions.Annotate(argList)
+       if len(a:argList) == 0
+               if &filetype == 'HGannotate'
+                       " Perform annotation of the version indicated by the current line.
+                       let caption = matchstr(getline('.'),'\v^\s+\zs\d+')
+                       let options = ' -r' . caption
+               else
+                       let caption = ''
+                       let options = ' -un'
+               endif
+       elseif len(a:argList) == 1 && a:argList[0] !~ '^-'
+               let caption = a:argList[0]
+               let options = ' -un -r' . caption
+       else
+               let caption = join(a:argList, ' ')
+               let options = ' ' . caption
+       endif
+
+       return s:DoCommand('blame' . options, 'annotate', caption, {})
+endfunction
+
+" Function: s:hgFunctions.Commit(argList) {{{2
+function! s:hgFunctions.Commit(argList)
+       try
+               return s:DoCommand('commit -v -l "' . a:argList[0] . '"', 'commit', '', {})
+       catch /Version control command failed.*nothing changed/
+               echomsg 'No commit needed.'
+       endtry
+endfunction
+
+" Function: s:hgFunctions.Delete() {{{2
+function! s:hgFunctions.Delete(argList)
+       return s:DoCommand(join(['remove'] + a:argList, ' '), 'remove', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:hgFunctions.Diff(argList) {{{2
+function! s:hgFunctions.Diff(argList)
+       if len(a:argList) == 0
+               let revOptions = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let revOptions = ['-r' . join(a:argList, ':')]
+               let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')'
+       else
+               " Pass-through
+               let caption = join(a:argList, ' ')
+               let revOptions = a:argList
+       endif
+
+       let hgDiffExt = VCSCommandGetOption('VCSCommandHGDiffExt', '')
+       if hgDiffExt == ''
+               let diffExt = []
+       else
+               let diffExt = ['--diff-cmd ' . hgDiffExt]
+       endif
+
+       let hgDiffOpt = VCSCommandGetOption('VCSCommandHGDiffOpt', '')
+       if hgDiffOpt == ''
+               let diffOptions = []
+       else
+               let diffOptions = ['-x -' . hgDiffOpt]
+       endif
+
+       return s:DoCommand(join(['diff'] + diffExt + diffOptions + revOptions), 'diff', caption, {})
+endfunction
+
+" Function: s:hgFunctions.Info(argList) {{{2
+function! s:hgFunctions.Info(argList)
+       return s:DoCommand(join(['log --limit 1'] + a:argList, ' '), 'log', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:hgFunctions.GetBufferInfo() {{{2
+" Provides version control details for the current file.  Current version
+" number and current repository version number are required to be returned by
+" the vcscommand plugin.
+" Returns: List of results:  [revision, repository, branch]
+
+function! s:hgFunctions.GetBufferInfo()
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       let fileName = bufname(originalBuffer)
+       let statusText = s:VCSCommandUtility.system(s:Executable() . ' status -- "' . fileName . '"')
+       if(v:shell_error)
+               return []
+       endif
+
+       " File not under HG control.
+       if statusText =~ '^?'
+               return ['Unknown']
+       endif
+
+       let parentsText = s:VCSCommandUtility.system(s:Executable() . ' parents -- "' . fileName . '"')
+       let revision = matchlist(parentsText, '^changeset:\s\+\(\S\+\)\n')[1]
+
+       let logText = s:VCSCommandUtility.system(s:Executable() . ' log -- "' . fileName . '"')
+       let repository = matchlist(logText, '^changeset:\s\+\(\S\+\)\n')[1]
+
+       if revision == ''
+               " Error
+               return ['Unknown']
+       elseif statusText =~ '^A'
+               return ['New', 'New']
+       else
+               return [revision, repository]
+       endif
+endfunction
+
+" Function: s:hgFunctions.Log(argList) {{{2
+function! s:hgFunctions.Log(argList)
+       if len(a:argList) == 0
+               let options = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let options = ['-r' . join(a:argList, ':')]
+               let caption = options[0]
+       else
+               " Pass-through
+               let options = a:argList
+               let caption = join(a:argList, ' ')
+       endif
+
+       let resultBuffer = s:DoCommand(join(['log', '-v'] + options), 'log', caption, {})
+       return resultBuffer
+endfunction
+
+" Function: s:hgFunctions.Revert(argList) {{{2
+function! s:hgFunctions.Revert(argList)
+       return s:DoCommand('revert', 'revert', '', {})
+endfunction
+
+" Function: s:hgFunctions.Review(argList) {{{2
+function! s:hgFunctions.Review(argList)
+       if len(a:argList) == 0
+               let versiontag = '(current)'
+               let versionOption = ''
+       else
+               let versiontag = a:argList[0]
+               let versionOption = ' -r ' . versiontag . ' '
+       endif
+
+       return s:DoCommand('cat' . versionOption, 'review', versiontag, {})
+endfunction
+
+" Function: s:hgFunctions.Status(argList) {{{2
+function! s:hgFunctions.Status(argList)
+       let options = ['-A', '-v']
+       if len(a:argList) != 0
+               let options = a:argList
+       endif
+       return s:DoCommand(join(['status'] + options, ' '), 'status', join(options, ' '), {})
+endfunction
+
+" Function: s:hgFunctions.Update(argList) {{{2
+function! s:hgFunctions.Update(argList)
+       return s:DoCommand('update', 'update', '', {})
+endfunction
+
+" Annotate setting {{{2
+let s:hgFunctions.AnnotateSplitRegex = '\d\+: '
+
+" Section: Plugin Registration {{{1
+let s:VCSCommandUtility = VCSCommandRegisterModule('HG', expand('<sfile>'), s:hgFunctions, [])
+
+let &cpo = s:save_cpo
diff --git a/.vim/plugin/vcssvk.vim b/.vim/plugin/vcssvk.vim
new file mode 100644 (file)
index 0000000..b7cc6c9
--- /dev/null
@@ -0,0 +1,257 @@
+" vim600: set foldmethod=marker:
+"
+" SVK extension for VCSCommand.
+"
+" Maintainer:    Bob Hiestand <bob.hiestand@gmail.com>
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+"
+" Section: Documentation {{{1
+"
+" Options documentation: {{{2
+"
+" VCSCommandSVKExec
+"   This variable specifies the SVK executable.  If not set, it defaults to
+"   'svk' executed from the user's executable path.
+
+" Section: Plugin header {{{1
+
+if exists('VCSCommandDisableAll')
+       finish
+endif
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+runtime plugin/vcscommand.vim
+
+if !executable(VCSCommandGetOption('VCSCommandSVKExec', 'svk'))
+       " SVK is not installed
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Variable initialization {{{1
+
+let s:svkFunctions = {}
+
+" Section: Utility functions {{{1
+
+" Function: s:Executable() {{{2
+" Returns the executable used to invoke SVK suitable for use in a shell
+" command.
+function! s:Executable()
+       return shellescape(VCSCommandGetOption('VCSCommandSVKExec', 'svk'))
+endfunction
+
+" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
+" Wrapper to VCSCommandDoCommand to add the name of the SVK executable to the
+" command argument.
+function! s:DoCommand(cmd, cmdName, statusText, options)
+       if VCSCommandGetVCSType(expand('%')) == 'SVK'
+               let fullCmd = s:Executable() . ' ' . a:cmd
+               return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options)
+       else
+               throw 'SVK VCSCommand plugin called on non-SVK item.'
+       endif
+endfunction
+
+" Section: VCS function implementations {{{1
+
+" Function: s:svkFunctions.Identify(buffer) {{{2
+function! s:svkFunctions.Identify(buffer)
+       let fileName = resolve(bufname(a:buffer))
+       if isdirectory(fileName)
+               let directoryName = fileName
+       else
+               let directoryName = fnamemodify(fileName, ':p:h')
+       endif
+       let statusText = s:VCSCommandUtility.system(s:Executable() . ' info -- "' . directoryName . '"', "no")
+       if(v:shell_error)
+               return 0
+       else
+               return 1
+       endif
+endfunction
+
+" Function: s:svkFunctions.Add() {{{2
+function! s:svkFunctions.Add(argList)
+       return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svkFunctions.Annotate(argList) {{{2
+function! s:svkFunctions.Annotate(argList)
+       if len(a:argList) == 0
+               if &filetype == 'SVKannotate'
+                       " Perform annotation of the version indicated by the current line.
+                       let caption = matchstr(getline('.'),'\v^\s+\zs\d+')
+                       let options = ' -r' . caption
+               else
+                       let caption = ''
+                       let options = ''
+               endif
+       elseif len(a:argList) == 1 && a:argList[0] !~ '^-'
+               let caption = a:argList[0]
+               let options = ' -r' . caption
+       else
+               let caption = join(a:argList, ' ')
+               let options = ' ' . caption
+       endif
+
+       let resultBuffer = s:DoCommand('blame' . options, 'annotate', caption, {})
+       if resultBuffer > 0
+               normal 1G2dd
+       endif
+       return resultBuffer
+endfunction
+
+" Function: s:svkFunctions.Commit(argList) {{{2
+function! s:svkFunctions.Commit(argList)
+       let resultBuffer = s:DoCommand('commit -F "' . a:argList[0] . '"', 'commit', '', {})
+       if resultBuffer == 0
+               echomsg 'No commit needed.'
+       endif
+endfunction
+
+" Function: s:svkFunctions.Delete() {{{2
+function! s:svkFunctions.Delete(argList)
+       return s:DoCommand(join(['delete'] + a:argList, ' '), 'delete', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svkFunctions.Diff(argList) {{{2
+function! s:svkFunctions.Diff(argList)
+       if len(a:argList) == 0
+               let revOptions = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let revOptions = ['-r' . join(a:argList, ':')]
+               let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')'
+       else
+               " Pass-through
+               let caption = join(a:argList, ' ')
+               let revOptions = a:argList
+       endif
+
+       return s:DoCommand(join(['diff'] + revOptions), 'diff', caption, {})
+endfunction
+
+" Function: s:svkFunctions.GetBufferInfo() {{{2
+" Provides version control details for the current file.  Current version
+" number and current repository version number are required to be returned by
+" the vcscommand plugin.
+" Returns: List of results:  [revision, repository]
+
+function! s:svkFunctions.GetBufferInfo()
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       let fileName = resolve(bufname(originalBuffer))
+       let statusText = s:VCSCommandUtility.system(s:Executable() . ' status -v -- "' . fileName . '"')
+       if(v:shell_error)
+               return []
+       endif
+
+       " File not under SVK control.
+       if statusText =~ '^?'
+               return ['Unknown']
+       endif
+
+       let [flags, revision, repository] = matchlist(statusText, '^\(.\{3}\)\s\+\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)\s')[1:3]
+       if revision == ''
+               " Error
+               return ['Unknown']
+       elseif flags =~ '^A'
+               return ['New', 'New']
+       else
+               return [revision, repository]
+       endif
+endfunction
+
+" Function: s:svkFunctions.Info(argList) {{{2
+function! s:svkFunctions.Info(argList)
+       return s:DoCommand(join(['info'] + a:argList, ' '), 'info', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svkFunctions.Lock(argList) {{{2
+function! s:svkFunctions.Lock(argList)
+       return s:DoCommand(join(['lock'] + a:argList, ' '), 'lock', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svkFunctions.Log() {{{2
+function! s:svkFunctions.Log(argList)
+       if len(a:argList) == 0
+               let options = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let options = ['-r' . join(a:argList, ':')]
+               let caption = options[0]
+       else
+               " Pass-through
+               let options = a:argList
+               let caption = join(a:argList, ' ')
+       endif
+
+       let resultBuffer = s:DoCommand(join(['log', '-v'] + options), 'log', caption, {})
+       return resultBuffer
+endfunction
+
+" Function: s:svkFunctions.Revert(argList) {{{2
+function! s:svkFunctions.Revert(argList)
+       return s:DoCommand('revert', 'revert', '', {})
+endfunction
+
+" Function: s:svkFunctions.Review(argList) {{{2
+function! s:svkFunctions.Review(argList)
+       if len(a:argList) == 0
+               let versiontag = '(current)'
+               let versionOption = ''
+       else
+               let versiontag = a:argList[0]
+               let versionOption = ' -r ' . versiontag . ' '
+       endif
+
+       return s:DoCommand('cat' . versionOption, 'review', versiontag, {})
+endfunction
+
+" Function: s:svkFunctions.Status(argList) {{{2
+function! s:svkFunctions.Status(argList)
+       let options = ['-v']
+       if len(a:argList) != 0
+               let options = a:argList
+       endif
+       return s:DoCommand(join(['status'] + options, ' '), 'status', join(options, ' '), {})
+endfunction
+
+" Function: s:svkFunctions.Unlock(argList) {{{2
+function! s:svkFunctions.Unlock(argList)
+       return s:DoCommand(join(['unlock'] + a:argList, ' '), 'unlock', join(a:argList, ' '), {})
+endfunction
+" Function: s:svkFunctions.Update(argList) {{{2
+function! s:svkFunctions.Update(argList)
+       return s:DoCommand('update', 'update', '', {})
+endfunction
+
+" Section: Plugin Registration {{{1
+let s:VCSCommandUtility = VCSCommandRegisterModule('SVK', expand('<sfile>'), s:svkFunctions, [])
+
+let &cpo = s:save_cpo
diff --git a/.vim/plugin/vcssvn.vim b/.vim/plugin/vcssvn.vim
new file mode 100644 (file)
index 0000000..758f819
--- /dev/null
@@ -0,0 +1,284 @@
+" vim600: set foldmethod=marker:
+"
+" SVN extension for VCSCommand.
+"
+" Maintainer:    Bob Hiestand <bob.hiestand@gmail.com>
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+"
+" Section: Documentation {{{1
+"
+" Options documentation: {{{2
+"
+" VCSCommandSVNExec
+"   This variable specifies the SVN executable.  If not set, it defaults to
+"   'svn' executed from the user's executable path.
+"
+" VCSCommandSVNDiffExt
+"   This variable, if set, sets the external diff program used by Subversion.
+"
+" VCSCommandSVNDiffOpt
+"   This variable, if set, determines the options passed to the svn diff
+"   command (such as 'u', 'w', or 'b').
+
+" Section: Plugin header {{{1
+
+if exists('VCSCommandDisableAll')
+       finish
+endif
+
+if v:version < 700
+       echohl WarningMsg|echomsg 'VCSCommand requires at least VIM 7.0'|echohl None
+       finish
+endif
+
+runtime plugin/vcscommand.vim
+
+if !executable(VCSCommandGetOption('VCSCommandSVNExec', 'svn'))
+       " SVN is not installed
+       finish
+endif
+
+let s:save_cpo=&cpo
+set cpo&vim
+
+" Section: Variable initialization {{{1
+
+let s:svnFunctions = {}
+
+" Section: Utility functions {{{1
+
+" Function: s:Executable() {{{2
+" Returns the executable used to invoke git suitable for use in a shell
+" command.
+function! s:Executable()
+       return shellescape(VCSCommandGetOption('VCSCommandSVNExec', 'svn'))
+endfunction
+
+" Function: s:DoCommand(cmd, cmdName, statusText, options) {{{2
+" Wrapper to VCSCommandDoCommand to add the name of the SVN executable to the
+" command argument.
+function! s:DoCommand(cmd, cmdName, statusText, options)
+       if VCSCommandGetVCSType(expand('%')) == 'SVN'
+               let fullCmd = s:Executable() . ' ' . a:cmd
+               return VCSCommandDoCommand(fullCmd, a:cmdName, a:statusText, a:options)
+       else
+               throw 'SVN VCSCommand plugin called on non-SVN item.'
+       endif
+endfunction
+
+" Section: VCS function implementations {{{1
+
+" Function: s:svnFunctions.Identify(buffer) {{{2
+function! s:svnFunctions.Identify(buffer)
+       let fileName = resolve(bufname(a:buffer))
+       if isdirectory(fileName)
+               let directoryName = fileName
+       else
+               let directoryName = fnamemodify(fileName, ':h')
+       endif
+       if strlen(directoryName) > 0
+               let svnDir = directoryName . '/.svn'
+       else
+               let svnDir = '.svn'
+       endif
+       if isdirectory(svnDir)
+               return 1
+       else
+               return 0
+       endif
+endfunction
+
+" Function: s:svnFunctions.Add() {{{2
+function! s:svnFunctions.Add(argList)
+       return s:DoCommand(join(['add'] + a:argList, ' '), 'add', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Annotate(argList) {{{2
+function! s:svnFunctions.Annotate(argList)
+       if len(a:argList) == 0
+               if &filetype == 'SVNannotate'
+                       " Perform annotation of the version indicated by the current line.
+                       let caption = matchstr(getline('.'),'\v^\s+\zs\d+')
+                       let options = ' -r' . caption
+               else
+                       let caption = ''
+                       let options = ''
+               endif
+       elseif len(a:argList) == 1 && a:argList[0] !~ '^-'
+               let caption = a:argList[0]
+               let options = ' -r' . caption
+       else
+               let caption = join(a:argList, ' ')
+               let options = ' ' . caption
+       endif
+
+       return s:DoCommand('blame --non-interactive' . options, 'annotate', caption, {})
+endfunction
+
+" Function: s:svnFunctions.Commit(argList) {{{2
+function! s:svnFunctions.Commit(argList)
+       let resultBuffer = s:DoCommand('commit --non-interactive -F "' . a:argList[0] . '"', 'commit', '', {})
+       if resultBuffer == 0
+               echomsg 'No commit needed.'
+       endif
+endfunction
+
+" Function: s:svnFunctions.Delete() {{{2
+function! s:svnFunctions.Delete(argList)
+       return s:DoCommand(join(['delete --non-interactive'] + a:argList, ' '), 'delete', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Diff(argList) {{{2
+function! s:svnFunctions.Diff(argList)
+       if len(a:argList) == 0
+               let revOptions = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let revOptions = ['-r' . join(a:argList, ':')]
+               let caption = '(' . a:argList[0] . ' : ' . get(a:argList, 1, 'current') . ')'
+       else
+               " Pass-through
+               let caption = join(a:argList, ' ')
+               let revOptions = a:argList
+       endif
+
+       let svnDiffExt = VCSCommandGetOption('VCSCommandSVNDiffExt', '')
+       if svnDiffExt == ''
+               let diffExt = []
+       else
+               let diffExt = ['--diff-cmd ' . svnDiffExt]
+       endif
+
+       let svnDiffOpt = VCSCommandGetOption('VCSCommandSVNDiffOpt', '')
+       if svnDiffOpt == ''
+               let diffOptions = []
+       else
+               let diffOptions = ['-x -' . svnDiffOpt]
+       endif
+
+       return s:DoCommand(join(['diff --non-interactive'] + diffExt + diffOptions + revOptions), 'diff', caption, {})
+endfunction
+
+" Function: s:svnFunctions.GetBufferInfo() {{{2
+" Provides version control details for the current file.  Current version
+" number and current repository version number are required to be returned by
+" the vcscommand plugin.
+" Returns: List of results:  [revision, repository, branch]
+
+function! s:svnFunctions.GetBufferInfo()
+       let originalBuffer = VCSCommandGetOriginalBuffer(bufnr('%'))
+       let fileName = bufname(originalBuffer)
+       let statusText = s:VCSCommandUtility.system(s:Executable() . ' status --non-interactive -vu -- "' . fileName . '"')
+       if(v:shell_error)
+               return []
+       endif
+
+       " File not under SVN control.
+       if statusText =~ '^?'
+               return ['Unknown']
+       endif
+
+       let [flags, revision, repository] = matchlist(statusText, '^\(.\{9}\)\s*\(\d\+\)\s\+\(\d\+\)')[1:3]
+       if revision == ''
+               " Error
+               return ['Unknown']
+       elseif flags =~ '^A'
+               return ['New', 'New']
+       elseif flags =~ '*'
+               return [revision, repository, '*']
+       else
+               return [revision, repository]
+       endif
+endfunction
+
+" Function: s:svnFunctions.Info(argList) {{{2
+function! s:svnFunctions.Info(argList)
+       return s:DoCommand(join(['info --non-interactive'] + a:argList, ' '), 'info', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Lock(argList) {{{2
+function! s:svnFunctions.Lock(argList)
+       return s:DoCommand(join(['lock --non-interactive'] + a:argList, ' '), 'lock', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Log(argList) {{{2
+function! s:svnFunctions.Log(argList)
+       if len(a:argList) == 0
+               let options = []
+               let caption = ''
+       elseif len(a:argList) <= 2 && match(a:argList, '^-') == -1
+               let options = ['-r' . join(a:argList, ':')]
+               let caption = options[0]
+       else
+               " Pass-through
+               let options = a:argList
+               let caption = join(a:argList, ' ')
+       endif
+
+       let resultBuffer = s:DoCommand(join(['log --non-interactive', '-v'] + options), 'log', caption, {})
+       return resultBuffer
+endfunction
+
+" Function: s:svnFunctions.Revert(argList) {{{2
+function! s:svnFunctions.Revert(argList)
+       return s:DoCommand('revert', 'revert', '', {})
+endfunction
+
+" Function: s:svnFunctions.Review(argList) {{{2
+function! s:svnFunctions.Review(argList)
+       if len(a:argList) == 0
+               let versiontag = '(current)'
+               let versionOption = ''
+       else
+               let versiontag = a:argList[0]
+               let versionOption = ' -r ' . versiontag . ' '
+       endif
+
+       return s:DoCommand('cat --non-interactive' . versionOption, 'review', versiontag, {})
+endfunction
+
+" Function: s:svnFunctions.Status(argList) {{{2
+function! s:svnFunctions.Status(argList)
+       let options = ['-u', '-v']
+       if len(a:argList) != 0
+               let options = a:argList
+       endif
+       return s:DoCommand(join(['status --non-interactive'] + options, ' '), 'status', join(options, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Unlock(argList) {{{2
+function! s:svnFunctions.Unlock(argList)
+       return s:DoCommand(join(['unlock --non-interactive'] + a:argList, ' '), 'unlock', join(a:argList, ' '), {})
+endfunction
+
+" Function: s:svnFunctions.Update(argList) {{{2
+function! s:svnFunctions.Update(argList)
+       return s:DoCommand('update --non-interactive', 'update', '', {})
+endfunction
+
+" Annotate setting {{{2
+let s:svnFunctions.AnnotateSplitRegex = '\s\+\S\+\s\+\S\+ '
+
+" Section: Plugin Registration {{{1
+let s:VCSCommandUtility = VCSCommandRegisterModule('SVN', expand('<sfile>'), s:svnFunctions, [])
+
+let &cpo = s:save_cpo
diff --git a/.vim/readme-ian.txt b/.vim/readme-ian.txt
new file mode 100644 (file)
index 0000000..15aa87e
--- /dev/null
@@ -0,0 +1,47 @@
+" Note, this config is many years old and needs serious updating.
+
+notes for everything in .vim.
+
+.vim/unix.vim
+this is just a vimrc to set nocomptable for testing
+
+not currently installed:
+
+ColorSamplerPack: {{{
+download from vim.org
+cd ~/.vim
+unzip ~/dt/ColorSamplerPack.zip
+}}}
+javacomplete - omnicompletion for java.{{{
+http://www.vim.org/scripts/script.php?script_id=1785
+
+files are:
+  .vim/autoload/java_parser.vim
+  .vim/doc/javacomplete.txt
+  .vim/autoload/javacomplete.vim
+  .vim/autoload/Reflection.java
+
+install instructions are on the site, and in the vim documentation.
+my install procedure:
+download from website
+in shell:
+> unzip ~/dt/javacomplete.zip -d ~/.vim
+> vim -c "helptags $HOME/.vim/doc"
+add stuff to vimrc, has "javacomplete near added parts.
+}}}
+
+{{{ syntaxattr.vim
+get this file:
+autoload/SyntaxAttr.vim
+
+add this to vimrc:
+
+autocmd FuncUndefined * exe 'runtime autoload/' . expand('<afile>') . '.vim'
+map <F6> :call SyntaxAttr()<CR>
+
+}}}
+
+syntax/sh.vim, modified from official version to make highlighting better
+
+
+" vim:sw=4 ts=4 sts=4 foldmethod=marker
diff --git a/.vim/syntax/cvsannotate.vim b/.vim/syntax/cvsannotate.vim
new file mode 100644 (file)
index 0000000..f35ed96
--- /dev/null
@@ -0,0 +1,45 @@
+" Vim syntax file
+" Language:    CVS annotate output
+" Maintainer:  Bob Hiestand <bob.hiestand@gmail.com>
+" Remark:      Used by the cvscommand plugin.  Originally written by Mathieu
+" Clabaut
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+
+if version < 600
+       syntax clear
+elseif exists("b:current_syntax")
+       finish
+endif
+
+syn match cvsDate      /\d\d-...-\d\d/                 contained
+syn match cvsName      /(\S* /hs=s+1,he=e-1            contained nextgroup=cvsDate
+syn match cvsVer       /^\d\+\(\.\d\+\)\+/             contained nextgroup=cvsName
+syn region cvsHead     start="^\d\+\.\d\+" end="):"    contains=cvsVer,cvsName,cvsDate
+
+if !exists("did_cvsannotate_syntax_inits")
+       let did_cvsannotate_syntax_inits = 1
+       hi link cvsDate         Comment
+       hi link cvsName Type
+       hi link cvsVer  Statement
+endif
+
+let b:current_syntax="CVSAnnotate"
diff --git a/.vim/syntax/gitannotate.vim b/.vim/syntax/gitannotate.vim
new file mode 100644 (file)
index 0000000..77ad14e
--- /dev/null
@@ -0,0 +1,44 @@
+" Vim syntax file
+" Language:    git annotate output
+" Maintainer:  Bob Hiestand <bob.hiestand@gmail.com>
+" Remark:      Used by the vcscommand plugin.
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+
+if exists("b:current_syntax")
+       finish
+endif
+
+syn region gitName start="(\@<=" end="\( \d\d\d\d-\)\@=" contained
+syn match gitCommit /^\^\?\x\+/ contained
+syn match gitDate /\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d [+-]\d\d\d\d/ contained
+syn match gitLineNumber /\d\+)\@=/ contained
+syn region gitAnnotation start="^" end=") " oneline keepend contains=gitCommit,gitLineNumber,gitDate,gitName
+
+if !exists("did_gitannotate_syntax_inits")
+       let did_gitannotate_syntax_inits = 1
+       hi link gitName Type
+       hi link gitCommit Statement
+       hi link gitDate Comment
+       hi link gitLineNumber Label
+endif
+
+let b:current_syntax="gitAnnotate"
diff --git a/.vim/syntax/hgannotate.vim b/.vim/syntax/hgannotate.vim
new file mode 100644 (file)
index 0000000..4e1d627
--- /dev/null
@@ -0,0 +1,40 @@
+" Vim syntax file
+" Language:    HG annotate output
+" Maintainer:  Bob Hiestand <bob.hiestand@gmail.com>
+" Remark:      Used by the vcscommand plugin.
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+
+if exists("b:current_syntax")
+       finish
+endif
+
+syn match hgVer /\d\+/ contained
+syn match hgName /^\s*\S\+/ contained
+syn match hgHead /^\s*\S\+\s\+\d\+:/ contains=hgVer,hgName
+
+if !exists("did_hgannotate_syntax_inits")
+       let did_hgannotate_syntax_inits = 1
+       hi link hgName Type
+       hi link hgVer Statement
+endif
+
+let b:current_syntax="hgAnnotate"
diff --git a/.vim/syntax/sh.vim b/.vim/syntax/sh.vim
new file mode 100644 (file)
index 0000000..ea654d9
--- /dev/null
@@ -0,0 +1,562 @@
+" modified to remove obnoxious overhighliting
+" Vim syntax file
+" Language:            shell (sh) Korn shell (ksh) bash (sh)
+" Maintainer:          Dr. Charles E. Campbell, Jr.  <NdrOchipS@PcampbellAfamily.Mbiz>
+" Previous Maintainer: Lennart Schultz <Lennart.Schultz@ecmwf.int>
+" Last Change:         Sep 17, 2008
+" Version:             104
+" URL:         http://mysite.verizon.net/astronaut/vim/index.html#vimlinks_syntax
+" For options and settings, please use:      :help ft-sh-syntax
+" This file includes many ideas from Éric Brunet (eric.brunet@ens.fr)
+
+" For version 5.x: Clear all syntax items {{{1
+" For version 6.x: Quit when a syntax file was already loaded
+if version < 600
+  syntax clear
+elseif exists("b:current_syntax")
+  finish
+endif
+
+" handling /bin/sh with is_kornshell/is_sh {{{1
+" b:is_sh is set when "#! /bin/sh" is found;
+" However, it often is just a masquerade by bash (typically Linux)
+" or kornshell (typically workstations with Posix "sh").
+" So, when the user sets "is_bash" or "is_kornshell",
+" a b:is_sh is converted into b:is_bash/b:is_kornshell,
+" respectively.
+if !exists("b:is_kornshell") && !exists("b:is_bash")
+  if exists("g:is_posix") && !exists("g:is_kornshell")
+   let g:is_kornshell= g:is_posix
+  endif
+  if exists("g:is_kornshell")
+    let b:is_kornshell= 1
+    if exists("b:is_sh")
+      unlet b:is_sh
+    endif
+  elseif exists("g:is_bash")
+    let b:is_bash= 1
+    if exists("b:is_sh")
+      unlet b:is_sh
+    endif
+  else
+    let b:is_sh= 1
+  endif
+endif
+
+" set up default g:sh_fold_enabled {{{1
+if !exists("g:sh_fold_enabled")
+ let g:sh_fold_enabled= 0
+elseif g:sh_fold_enabled != 0 && !has("folding")
+ let g:sh_fold_enabled= 0
+ echomsg "Ignoring g:sh_fold_enabled=".g:sh_fold_enabled."; need to re-compile vim for +fold support"
+endif
+if !exists("s:sh_fold_functions")
+ let s:sh_fold_functions = 1
+endif
+if !exists("s:sh_fold_heredoc")
+ let s:sh_fold_heredoc   = 2
+endif
+if !exists("s:sh_fold_ifdofor")
+ let s:sh_fold_ifdofor   = 4
+endif
+if g:sh_fold_enabled && &fdm == "manual"
+ set fdm=syntax
+endif
+
+" sh syntax is case sensitive {{{1
+syn case match
+
+" Clusters: contains=@... clusters {{{1
+"==================================
+syn cluster shErrorList        contains=shDoError,shIfError,shInError,shCaseError,shEsacError,shCurlyError,shParenError,shTestError
+if exists("b:is_kornshell")
+ syn cluster ErrorList add=shDTestError
+endif
+syn cluster shArithParenList   contains=shArithmetic,shDeref,shDerefSimple,shEcho,shEscape,shNumber,shOperator,shPosnParm,shExSingleQuote,shRedir,shSingleQuote,shDoubleQuote,shStatement,shVariable,shAlias,shTest,shCtrlSeq,shSpecial,shParen
+syn cluster shArithList        contains=@shArithParenList,shParenError
+syn cluster shCaseEsacList     contains=shCaseStart,shCase,shCaseBar,shCaseIn,shComment,shDeref,shDerefSimple,shCaseCommandSub,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote,shCtrlSeq,@shErrorList,shStringSpecial
+syn cluster shCaseList contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq
+syn cluster shColonList        contains=@shCaseList
+syn cluster shCommandSubList   contains=shArithmetic,shDeref,shDerefSimple,shEscape,shNumber,shOperator,shPosnParm,shExSingleQuote,shSingleQuote,shDoubleQuote,shStatement,shVariable,shSubSh,shAlias,shTest,shCtrlSeq,shSpecial
+syn cluster shCurlyList        contains=shNumber,shComma,shDeref,shDerefSimple,shDerefSpecial
+syn cluster shDblQuoteList     contains=shCommandSub,shDeref,shDerefSimple,shPosnParm,shExSingleQuote,shCtrlSeq,shSpecial
+syn cluster shDerefList        contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial,shDerefWordError,shDerefPPS
+syn cluster shDerefVarList     contains=shDerefOp,shDerefVarArray
+syn cluster shEchoList contains=shArithmetic,shCommandSub,shDeref,shDerefSimple,shExpr,shExSingleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote
+syn cluster shExprList1        contains=shCharClass,shNumber,shOperator,shExSingleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq
+syn cluster shExprList2        contains=@shExprList1,@shCaseList,shTest
+syn cluster shFunctionList     contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shRedir,shSetList,shSource,shStatement,shVariable,shOperator,shCtrlSeq
+if exists("b:is_kornshell") || exists("b:is_bash")
+ syn cluster shFunctionList    add=shRepeat
+ syn cluster shFunctionList    add=shDblBrace,shDblParen
+endif
+syn cluster shHereBeginList    contains=@shCommandSubList
+syn cluster shHereList contains=shBeginHere,shHerePayload
+syn cluster shHereListDQ       contains=shBeginHere,@shDblQuoteList,shHerePayload
+syn cluster shIdList   contains=shCommandSub,shWrapLineOperator,shSetOption,shDeref,shDerefSimple,shRedir,shExSingleQuote,shSingleQuote,shDoubleQuote,shExpr,shCtrlSeq,shStringSpecial
+syn cluster shLoopList contains=@shCaseList,shTestOpr,shExpr,shDblBrace,shConditional,shCaseEsac,shTest,@shErrorList,shSet
+syn cluster shSubShList        contains=@shCaseList,shOperator
+syn cluster shTestList contains=shCharClass,shComment,shCommandSub,shDeref,shDerefSimple,shDoubleQuote,shExpr,shExpr,shNumber,shOperator,shExSingleQuote,shSingleQuote,shTestOpr,shTest,shCtrlSeq
+
+
+" Alias: {{{1
+" =====
+if exists("b:is_kornshell") || exists("b:is_bash")
+ syn match shStatement "\<alias\>"
+ syn region shAlias matchgroup=shStatement start="\<alias\>\s\+\(\w\+\)\@=" skip="\\$" end="\>\|`"
+ syn region shAlias matchgroup=shStatement start="\<alias\>\s\+\(\w\+=\)\@=" skip="\\$" end="="
+endif
+
+" Error Codes: {{{1
+" ============
+syn match   shDoError "\<done\>"
+syn match   shIfError "\<fi\>"
+syn match   shInError "\<in\>"
+syn match   shCaseError ";;"
+syn match   shEsacError "\<esac\>"
+syn match   shCurlyError "}"
+syn match   shParenError ")"
+if exists("b:is_kornshell")
+ syn match     shDTestError "]]"
+endif
+syn match     shTestError "]"
+
+" Options: {{{1
+" ====================
+syn match   shOption   "\s\zs[-+][a-zA-Z0-9]\+\>"
+syn match   shOption   "\s\zs--[^ \t$`'"|]\+"
+
+" File Redirection Highlighted As Operators: {{{1
+"===========================================
+syn match      shRedir "\d\=>\(&[-0-9]\)\="
+syn match      shRedir "\d\=>>-\="
+syn match      shRedir "\d\=<\(&[-0-9]\)\="
+syn match      shRedir "\d<<-\="
+
+" Operators: {{{1
+" ==========
+syn match   shOperator "<<\|>>"                contained
+syn match   shOperator "[!&;|]"                contained
+syn match   shOperator "\[[[^:]\|\]]"          contained
+syn match   shOperator "!\=="          skipwhite nextgroup=shPattern
+syn match   shPattern  "\<\S\+\())\)\@="       contained contains=shExSingleQuote,shSingleQuote,shDoubleQuote,shDeref
+
+" Subshells: {{{1
+" ==========
+syn region shExpr  transparent matchgroup=shExprRegion  start="{" end="}"      contains=@shExprList2
+syn region shSubSh transparent matchgroup=shSubShRegion start="(" end=")"      contains=@shSubShList
+
+" Tests: {{{1
+"=======
+"syn region  shExpr transparent matchgroup=shRange start="\[" skip=+\\\\\|\\$+ end="\]" contains=@shTestList
+syn region shExpr      matchgroup=shRange start="\[" skip=+\\\\\|\\$+ end="\]" contains=@shTestList
+syn region shTest      transparent matchgroup=shStatement start="\<test\>" skip=+\\\\\|\\$+ matchgroup=NONE end="[;&|]"me=e-1 end="$" contains=@shExprList1
+syn match  shTestOpr   contained       "<=\|>=\|!=\|==\|-.\>\|-\(nt\|ot\|ef\|eq\|ne\|lt\|le\|gt\|ge\)\>\|[!<>]"
+syn match  shTestOpr   contained       '=' skipwhite nextgroup=shTestDoubleQuote,shTestSingleQuote,shTestPattern
+syn match  shTestPattern       contained       '\w\+'
+syn match  shTestDoubleQuote   contained       '"[^"]*"'
+syn match  shTestSingleQuote   contained       '\\.'
+syn match  shTestSingleQuote   contained       "'[^']*'"
+if exists("b:is_kornshell") || exists("b:is_bash")
+ syn region  shDblBrace matchgroup=Delimiter start="\[\[" skip=+\\\\\|\\$+ end="\]\]"  contains=@shTestList
+ syn region  shDblParen matchgroup=Delimiter start="((" skip=+\\\\\|\\$+ end="))"      contains=@shTestList
+endif
+
+" Character Class In Range: {{{1
+" =========================
+syn match   shCharClass        contained       "\[:\(backspace\|escape\|return\|xdigit\|alnum\|alpha\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|tab\):\]"
+
+" Loops: do, if, while, until {{{1
+" ======
+if (g:sh_fold_enabled % (s:sh_fold_ifdofor * 2))/s:sh_fold_ifdofor
+ syn region shDo       fold transparent matchgroup=shConditional start="\<do\>" matchgroup=shConditional end="\<done\>" contains=@shLoopList
+ syn region shIf       fold transparent matchgroup=shConditional start="\<if\_s" matchgroup=shConditional end="\<;\_s*then\>" end="\<fi\>"   contains=@shLoopList,shDblBrace,shDblParen,shFunctionKey
+ syn region shFor      fold matchgroup=shLoop start="\<for\_s" end="\<in\_s" end="\<do\>"me=e-2        contains=@shLoopList,shDblParen skipwhite nextgroup=shCurlyIn
+else
+ syn region shDo       transparent matchgroup=shConditional start="\<do\>" matchgroup=shConditional end="\<done\>" contains=@shLoopList
+ syn region shIf       transparent matchgroup=shConditional start="\<if\_s" matchgroup=shConditional end="\<;\_s*then\>" end="\<fi\>"   contains=@shLoopList,shDblBrace,shDblParen,shFunctionKey
+ syn region shFor      matchgroup=shLoop start="\<for\_s" end="\<in\>" end="\<do\>"me=e-2      contains=@shLoopList,shDblParen skipwhite nextgroup=shCurlyIn
+endif
+if exists("b:is_kornshell") || exists("b:is_bash")
+ syn cluster shCaseList        add=shRepeat
+ syn cluster shFunctionList    add=shRepeat
+ syn region shRepeat   matchgroup=shLoop   start="\<while\_s" end="\<in\_s" end="\<do\>"me=e-2 contains=@shLoopList,shDblParen,shDblBrace
+ syn region shRepeat   matchgroup=shLoop   start="\<until\_s" end="\<in\_s" end="\<do\>"me=e-2 contains=@shLoopList,shDblParen,shDblBrace
+ syn region shCaseEsac matchgroup=shConditional start="\<select\s" matchgroup=shConditional end="\<in\>" end="\<do\>" contains=@shLoopList
+else
+ syn region shRepeat   matchgroup=shLoop   start="\<while\_s" end="\<do\>"me=e-2               contains=@shLoopList
+ syn region shRepeat   matchgroup=shLoop   start="\<until\_s" end="\<do\>"me=e-2               contains=@shLoopList
+endif
+syn region shCurlyIn   contained       matchgroup=Delimiter start="{" end="}" contains=@shCurlyList
+syn match  shComma     contained       ","
+
+" Case: case...esac {{{1
+" ====
+syn match   shCaseBar  contained skipwhite "\(^\|[^\\]\)\(\\\\\)*\zs|"         nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote
+syn match   shCaseStart        contained skipwhite skipnl "("                  nextgroup=shCase,shCaseBar
+syn region  shCase     contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)"  end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment
+if (g:sh_fold_enabled % (s:sh_fold_ifdofor * 2))/s:sh_fold_ifdofor
+ syn region  shCaseEsac        fold matchgroup=shConditional start="\<case\>" end="\<esac\>"   contains=@shCaseEsacList
+else
+ syn region  shCaseEsac        matchgroup=shConditional start="\<case\>" end="\<esac\>"        contains=@shCaseEsacList
+endif
+syn keyword shCaseIn   contained skipwhite skipnl in                   nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote
+if exists("b:is_bash")
+ syn region  shCaseExSingleQuote       matchgroup=shOperator start=+\$'+ skip=+\\\\\|\\.+ end=+'+      contains=shStringSpecial,shSpecial      skipwhite skipnl nextgroup=shCaseBar    contained
+else
+ syn region  shCaseExSingleQuote       matchgroup=Error start=+\$'+ skip=+\\\\\|\\.+ end=+'+   contains=shStringSpecial        skipwhite skipnl nextgroup=shCaseBar    contained
+endif
+syn region  shCaseSingleQuote  matchgroup=shOperator start=+'+ end=+'+         contains=shStringSpecial                skipwhite skipnl nextgroup=shCaseBar    contained
+syn region  shCaseDoubleQuote  matchgroup=shOperator start=+"+ skip=+\\\\\|\\.+ end=+"+        contains=@shDblQuoteList,shStringSpecial        skipwhite skipnl nextgroup=shCaseBar    contained
+syn region  shCaseCommandSub   start=+`+ skip=+\\\\\|\\.+ end=+`+              contains=@shCommandSubList              skipwhite skipnl nextgroup=shCaseBar    contained
+
+" Misc: {{{1
+"======
+syn match   shWrapLineOperator "\\$"
+syn region  shCommandSub   start="`" skip="\\\\\|\\." end="`" contains=@shCommandSubList
+syn match   shEscape   contained       '\\.'
+
+" $() and $(()): {{{1
+" $(..) is not supported by sh (Bourne shell).  However, apparently
+" some systems (HP?) have as their /bin/sh a (link to) Korn shell
+" (ie. Posix compliant shell).  /bin/ksh should work for those
+" systems too, however, so the following syntax will flag $(..) as
+" an Error under /bin/sh.  By consensus of vimdev'ers!
+if exists("b:is_kornshell") || exists("b:is_bash")
+ syn region shCommandSub matchgroup=shCmdSubRegion start="\$("  skip='\\\\\|\\.' end=")"  contains=@shCommandSubList
+ syn region shArithmetic matchgroup=shArithRegion  start="\$((" skip='\\\\\|\\.' end="))" contains=@shArithList
+ syn match  shSkipInitWS contained     "^\s\+"
+else
+ syn region shCommandSub matchgroup=Error start="\$(" end=")" contains=@shCommandSubList
+endif
+
+if exists("b:is_bash")
+ syn cluster shCommandSubList add=bashSpecialVariables,bashStatement
+ syn cluster shCaseList add=bashAdminStatement,bashStatement
+ syn keyword bashSpecialVariables contained BASH BASH_ENV BASH_VERSINFO BASH_VERSION CDPATH DIRSTACK EUID FCEDIT FIGNORE GLOBIGNORE GROUPS HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HOME HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LC_ALL LC_COLLATE LC_MESSAGES LINENO MACHTYPE MAIL MAILCHECK MAILPATH OLDPWD OPTARG OPTERR OPTIND OSTYPE PATH PIPESTATUS PPID PROMPT_COMMAND PS1 PS2 PS3 PS4 PWD RANDOM REPLY SECONDS SHELLOPTS SHLVL TIMEFORMAT TIMEOUT UID auto_resume histchars
+endif
+
+if exists("b:is_kornshell")
+ syn cluster shCommandSubList add=kshSpecialVariables,kshStatement
+ syn cluster shCaseList add=kshStatement
+ syn keyword kshSpecialVariables contained CDPATH COLUMNS EDITOR ENV ERRNO FCEDIT FPATH HISTFILE HISTSIZE HOME IFS LINENO LINES MAIL MAILCHECK MAILPATH OLDPWD OPTARG OPTIND PATH PPID PS1 PS2 PS3 PS4 PWD RANDOM REPLY SECONDS SHELL TMOUT VISUAL
+ syn keyword kshStatement cat chmod clear cp du egrep expr fgrep find grep install killall less ls mkdir mv nice printenv rm rmdir sed sort strip stty tail touch tput
+endif
+
+syn match   shSource   "^\.\s"
+syn match   shSource   "\s\.\s"
+syn region  shColon    start="^\s*:" end="$\|" end="#"me=e-1 contains=@shColonList
+
+" String And Character Constants: {{{1
+"================================
+syn match   shNumber   "-\=\<\d\+\>#\="
+syn match   shCtrlSeq  "\\\d\d\d\|\\[abcfnrtv0]"               contained
+if exists("b:is_bash")
+ syn match   shSpecial "\\\o\o\o\|\\x\x\x\|\\c.\|\\[abefnrtv]" contained
+endif
+if exists("b:is_bash")
+ syn region  shExSingleQuote   matchgroup=shOperator start=+\$'+ skip=+\\\\\|\\.+ end=+'+      contains=shStringSpecial,shSpecial
+else
+ syn region  shExSingleQuote   matchGroup=Error start=+\$'+ skip=+\\\\\|\\.+ end=+'+   contains=shStringSpecial
+endif
+syn region  shSingleQuote      matchgroup=shOperator start=+'+ end=+'+         contains=shStringSpecial,@Spell
+syn region  shDoubleQuote      matchgroup=shOperator start=+"+ skip=+\\"+ end=+"+      contains=@shDblQuoteList,shStringSpecial,@Spell
+syn match   shStringSpecial    "[^[:print:] \t]"       contained
+syn match   shStringSpecial    "\%(\\\\\)*\\[\\"'`$()#]"
+syn match   shSpecial  "[^\\]\zs\%(\\\\\)*\\[\\"'`$()#]"
+syn match   shSpecial  "^\%(\\\\\)*\\[\\"'`$()#]"
+
+" Comments: {{{1
+"==========
+syn cluster    shCommentGroup  contains=shTodo,@Spell
+syn keyword    shTodo  contained       COMBAK FIXME TODO XXX
+syn match      shComment       "^\s*\zs#.*$"   contains=@shCommentGroup
+syn match      shComment       "\s\zs#.*$"     contains=@shCommentGroup
+syn match      shQuickComment  contained       "#.*$"
+
+" Here Documents: {{{1
+" =========================================
+if version < 600
+ syn region shHereDoc matchgroup=shRedir start="<<\s*\**END[a-zA-Z_0-9]*\**"  matchgroup=shRedir end="^END[a-zA-Z_0-9]*$" contains=@shDblQuoteList
+ syn region shHereDoc matchgroup=shRedir start="<<-\s*\**END[a-zA-Z_0-9]*\**" matchgroup=shRedir end="^\s*END[a-zA-Z_0-9]*$" contains=@shDblQuoteList
+ syn region shHereDoc matchgroup=shRedir start="<<\s*\**EOF\**"        matchgroup=shRedir      end="^EOF$"     contains=@shDblQuoteList
+ syn region shHereDoc matchgroup=shRedir start="<<-\s*\**EOF\**" matchgroup=shRedir    end="^\s*EOF$"  contains=@shDblQuoteList
+ syn region shHereDoc matchgroup=shRedir start="<<\s*\**\.\**" matchgroup=shRedir      end="^\.$"      contains=@shDblQuoteList
+ syn region shHereDoc matchgroup=shRedir start="<<-\s*\**\.\**"        matchgroup=shRedir      end="^\s*\.$"   contains=@shDblQuoteList
+
+elseif (g:sh_fold_enabled % (s:sh_fold_heredoc * 2))/s:sh_fold_heredoc
+ syn region shHereDoc matchgroup=shRedir fold start="<<\s*\z(\S*\)"            matchgroup=shRedir end="^\z1\s*$"       contains=@shDblQuoteList
+ syn region shHereDoc matchgroup=shRedir fold start="<<\s*\"\z(\S*\)\""                matchgroup=shRedir end="^\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir fold start="<<\s*'\z(\S*\)'"          matchgroup=shRedir end="^\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\z(\S*\)"           matchgroup=shRedir end="^\s*\z1\s*$"    contains=@shDblQuoteList
+ syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\"\z(\S*\)\""               matchgroup=shRedir end="^\s*\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir fold start="<<-\s*'\z(\S*\)'"         matchgroup=shRedir end="^\s*\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir fold start="<<\s*\\\_$\_s*\z(\S*\)"           matchgroup=shRedir end="^\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir fold start="<<\s*\\\_$\_s*\"\z(\S*\)\""       matchgroup=shRedir end="^\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\\\_$\_s*'\z(\S*\)'"                matchgroup=shRedir end="^\s*\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\\\_$\_s*\z(\S*\)"          matchgroup=shRedir end="^\s*\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir fold start="<<-\s*\\\_$\_s*\"\z(\S*\)\""      matchgroup=shRedir end="^\s*\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir fold start="<<\s*\\\_$\_s*'\z(\S*\)'"         matchgroup=shRedir end="^\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir fold start="<<\\\z(\S*\)"             matchgroup=shRedir end="^\z1\s*$"
+
+else
+ syn region shHereDoc matchgroup=shRedir start="<<\s*\\\=\z(\S*\)"     matchgroup=shRedir end="^\z1\s*$"    contains=@shDblQuoteList
+ syn region shHereDoc matchgroup=shRedir start="<<\s*\"\z(\S*\)\""     matchgroup=shRedir end="^\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir start="<<-\s*\z(\S*\)"                matchgroup=shRedir end="^\s*\z1\s*$" contains=@shDblQuoteList
+ syn region shHereDoc matchgroup=shRedir start="<<-\s*'\z(\S*\)'"      matchgroup=shRedir end="^\s*\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir start="<<\s*'\z(\S*\)'"       matchgroup=shRedir end="^\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir start="<<-\s*\"\z(\S*\)\""    matchgroup=shRedir end="^\s*\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir start="<<\s*\\\_$\_s*\z(\S*\)"        matchgroup=shRedir end="^\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir start="<<-\s*\\\_$\_s*\z(\S*\)"       matchgroup=shRedir end="^\s*\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir start="<<-\s*\\\_$\_s*'\z(\S*\)'"     matchgroup=shRedir end="^\s*\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir start="<<\s*\\\_$\_s*'\z(\S*\)'"      matchgroup=shRedir end="^\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir start="<<\s*\\\_$\_s*\"\z(\S*\)\""    matchgroup=shRedir end="^\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir start="<<-\s*\\\_$\_s*\"\z(\S*\)\""   matchgroup=shRedir end="^\s*\z1\s*$"
+ syn region shHereDoc matchgroup=shRedir start="<<\\\z(\S*\)"          matchgroup=shRedir end="^\z1\s*$"
+endif
+
+" Here Strings: {{{1
+" =============
+if exists("b:is_bash")
+ syn match shRedir "<<<"
+endif
+
+" Identifiers: {{{1
+"=============
+syn match  shSetOption "\s\zs[-+][a-zA-Z0-9]\+\>"      contained
+syn match  shVariable  "\<\([bwglsav]:\)\=[a-zA-Z0-9.!@_%+,]*\ze="     nextgroup=shSetIdentifier
+syn match  shSetIdentifier     "="             contained       nextgroup=shPattern,shDeref,shDerefSimple,shDoubleQuote,shSingleQuote,shExSingleQuote
+
+" Functions: {{{1
+if !exists("g:is_posix")
+ syn keyword shFunctionKey function    skipwhite skipnl nextgroup=shFunctionTwo
+endif
+
+if exists("b:is_bash")
+ if (g:sh_fold_enabled % (s:sh_fold_functions * 2))/s:sh_fold_functions
+  syn region shFunctionOne fold        matchgroup=shFunction start="^\s*\h[-a-zA-Z_0-9]*\s*()\_s*{" end="}"    contains=@shFunctionList                        skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
+  syn region shFunctionTwo fold        matchgroup=shFunction start="\h[-a-zA-Z_0-9]*\s*\%(()\)\=\_s*{" end="}" contains=shFunctionKey,@shFunctionList contained        skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
+ else
+  syn region shFunctionOne     matchgroup=shFunction start="^\s*\h[-a-zA-Z_0-9]*\s*()\_s*{"    end="}" contains=@shFunctionList
+  syn region shFunctionTwo     matchgroup=shFunction start="\h[-a-zA-Z_0-9]*\s*\%(()\)\=\_s*{" end="}" contains=shFunctionKey,@shFunctionList contained
+ endif
+else
+ if (g:sh_fold_enabled % (s:sh_fold_functions * 2))/s:sh_fold_functions
+  syn region shFunctionOne fold        matchgroup=shFunction start="^\s*\h\w*\s*()\_s*{" end="}"       contains=@shFunctionList                        skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
+  syn region shFunctionTwo fold        matchgroup=shFunction start="\h\w*\s*\%(()\)\=\_s*{"    end="}" contains=shFunctionKey,@shFunctionList contained        skipwhite skipnl nextgroup=shFunctionStart,shQuickComment
+ else
+  syn region shFunctionOne     matchgroup=shFunction start="^\s*\h\w*\s*()\_s*{"       end="}" contains=@shFunctionList
+  syn region shFunctionTwo     matchgroup=shFunction start="\h\w*\s*\%(()\)\=\_s*{"    end="}" contains=shFunctionKey,@shFunctionList contained
+ endif
+endif
+
+" Parameter Dereferencing: {{{1
+" ========================
+syn match  shDerefSimple       "\$\%(\h\w*\|\d\)"
+syn region shDeref     matchgroup=PreProc start="\${" end="}"  contains=@shDerefList,shDerefVarArray
+syn match  shDerefSimple       "\$[-#*@!?]"
+syn match  shDerefSimple       "\$\$"
+if exists("b:is_bash") || exists("b:is_kornshell")
+ syn region shDeref    matchgroup=PreProc start="\${##\=" end="}"      contains=@shDerefList
+ syn region shDeref    matchgroup=PreProc start="\${\$\$" end="}"      contains=@shDerefList
+endif
+
+" bash: ${!prefix*} and ${#parameter}: {{{1
+" ====================================
+if exists("b:is_bash")
+ syn region shDeref    matchgroup=PreProc start="\${!" end="\*\=}"     contains=@shDerefList,shDerefOp
+ syn match  shDerefVar contained       "{\@<=!\w\+"            nextgroup=@shDerefVarList
+endif
+
+syn match  shDerefSpecial      contained       "{\@<=[-*@?0]"          nextgroup=shDerefOp
+syn match  shDerefSpecial      contained       "\({[#!]\)\@<=[[:alnum:]*@_]\+" nextgroup=@shDerefVarList,shDerefOp
+syn match  shDerefVar  contained       "{\@<=\w\+"             nextgroup=@shDerefVarList
+
+" sh ksh bash : ${var[... ]...}  array reference: {{{1
+syn region  shDerefVarArray   contained        matchgroup=shDeref start="\[" end="]"   contains=@shCommandSubList nextgroup=shDerefOp
+
+" Special ${parameter OPERATOR word} handling: {{{1
+" sh ksh bash : ${parameter:-word}    word is default value
+" sh ksh bash : ${parameter:=word}    assign word as default value
+" sh ksh bash : ${parameter:?word}    display word if parameter is null
+" sh ksh bash : ${parameter:+word}    use word if parameter is not null, otherwise nothing
+"    ksh bash : ${parameter#pattern}  remove small left  pattern
+"    ksh bash : ${parameter##pattern} remove large left  pattern
+"    ksh bash : ${parameter%pattern}  remove small right pattern
+"    ksh bash : ${parameter%%pattern} remove large right pattern
+syn cluster shDerefPatternList contains=shDerefPattern,shDerefString
+syn match  shDerefOp   contained       ":\=[-=?]"      nextgroup=@shDerefPatternList
+syn match  shDerefOp   contained       ":\=+"  nextgroup=@shDerefPatternList
+if exists("b:is_bash") || exists("b:is_kornshell")
+ syn match  shDerefOp  contained       "#\{1,2}"       nextgroup=@shDerefPatternList
+ syn match  shDerefOp  contained       "%\{1,2}"       nextgroup=@shDerefPatternList
+ syn match  shDerefPattern     contained       "[^{}]\+"       contains=shDeref,shDerefSimple,shDerefPattern,shDerefString,shCommandSub,shDerefEscape nextgroup=shDerefPattern
+ syn region shDerefPattern     contained       start="{" end="}"       contains=shDeref,shDerefSimple,shDerefString,shCommandSub nextgroup=shDerefPattern
+ syn match  shDerefEscape      contained       '\%(\\\\\)*\\.'
+endif
+syn region shDerefString       contained       matchgroup=shOperator start=+\%(\\\)\@<!'+ end=+'+              contains=shStringSpecial
+syn region shDerefString       contained       matchgroup=shOperator start=+\%(\\\)\@<!"+ skip=+\\"+ end=+"+   contains=@shDblQuoteList,shStringSpecial
+syn match  shDerefString       contained       "\\["']"        nextgroup=shDerefPattern
+
+if exists("b:is_bash")
+ " bash : ${parameter:offset}
+ " bash : ${parameter:offset:length}
+ syn region shDerefOp  contained       start=":[$[:alnum:]_]"me=e-1 end=":"me=e-1 end="}"me=e-1 contains=@shCommandSubList nextgroup=shDerefPOL
+ syn match  shDerefPOL contained       ":[^}]\+"       contains=@shCommandSubList
+
+ " bash : ${parameter//pattern/string}
+ " bash : ${parameter//pattern}
+ syn match  shDerefPPS contained       '/\{1,2}'       nextgroup=shDerefPPSleft
+ syn region shDerefPPSleft     contained       start='.'       skip=@\%(\\\)\/@ matchgroup=shDerefOp end='/' end='\ze}' nextgroup=shDerefPPSright contains=@shCommandSubList
+ syn region shDerefPPSright    contained       start='.'       end='\ze}'      contains=@shCommandSubList
+endif
+
+" Arithmetic Parenthesized Expressions: {{{1
+syn region shParen matchgroup=shArithRegion start='(\ze[^(]' end=')' contains=@shArithParenList
+
+" Useful sh Keywords: {{{1
+" ===================
+syn keyword shConditional contained elif else then
+syn keyword shCondError elif else then
+
+" Useful ksh Keywords: {{{1
+" ====================
+ if exists("g:is_posix")
+  syn keyword shStatement command
+ else
+  syn keyword shStatement time
+ endif
+
+" Useful bash Keywords: {{{1
+" =====================
+
+" Synchronization: {{{1
+" ================
+if !exists("sh_minlines")
+  let sh_minlines = 200
+endif
+if !exists("sh_maxlines")
+  let sh_maxlines = 2 * sh_minlines
+endif
+exec "syn sync minlines=" . sh_minlines . " maxlines=" . sh_maxlines
+syn sync match shCaseEsacSync  grouphere       shCaseEsac      "\<case\>"
+syn sync match shCaseEsacSync  groupthere      shCaseEsac      "\<esac\>"
+syn sync match shDoSync        grouphere       shDo    "\<do\>"
+syn sync match shDoSync        groupthere      shDo    "\<done\>"
+syn sync match shForSync       grouphere       shFor   "\<for\>"
+syn sync match shForSync       groupthere      shFor   "\<in\>"
+syn sync match shIfSync        grouphere       shIf    "\<if\>"
+syn sync match shIfSync        groupthere      shIf    "\<fi\>"
+syn sync match shUntilSync     grouphere       shRepeat        "\<until\>"
+syn sync match shWhileSync     grouphere       shRepeat        "\<while\>"
+
+" Default Highlighting: {{{1
+" =====================
+hi def link shArithRegion      shShellVariables
+hi def link shBeginHere        shRedir
+hi def link shCaseBar  shConditional
+hi def link shCaseCommandSub   shCommandSub
+hi def link shCaseDoubleQuote  shDoubleQuote
+hi def link shCaseIn   shConditional
+hi def link shCaseSingleQuote  shSingleQuote
+hi def link shCaseStart        shConditional
+hi def link shCmdSubRegion     shShellVariables
+hi def link shColon    shStatement
+hi def link shDerefOp  shOperator
+hi def link shDerefPOL shDerefOp
+hi def link shDerefPPS shDerefOp
+hi def link shDeref    shShellVariables
+hi def link shDerefSimple      shDeref
+hi def link shDerefSpecial     shDeref
+hi def link shDerefString      shDoubleQuote
+hi def link shDerefVar shDeref
+hi def link shDoubleQuote      shString
+hi def link shEcho     shString
+hi def link shEchoQuote        shString
+hi def link shEmbeddedEcho     shString
+hi def link shEscape   shCommandSub
+hi def link shExSingleQuote    shSingleQuote
+hi def link shFunction Function
+hi def link shHereDoc  shString
+hi def link shHerePayload      shHereDoc
+hi def link shLoop     shStatement
+hi def link shOption   shCommandSub
+hi def link shPattern  shString
+hi def link shParen    shArithmetic
+hi def link shPosnParm shShellVariables
+hi def link shQuickComment     shComment
+hi def link shRange    shOperator
+hi def link shRedir    shOperator
+hi def link shSetOption        shOption
+hi def link shSingleQuote      shString
+hi def link shSource   shOperator
+hi def link shStringSpecial    shSpecial
+hi def link shSubShRegion      shOperator
+hi def link shTestOpr  shConditional
+hi def link shTestPattern      shString
+hi def link shTestDoubleQuote  shString
+hi def link shTestSingleQuote  shString
+hi def link shVariable shSetList
+hi def link shWrapLineOperator shOperator
+
+if exists("b:is_bash")
+  hi def link bashAdminStatement       shStatement
+  hi def link bashSpecialVariables     shShellVariables
+  hi def link bashStatement            shStatement
+  hi def link shFunctionParen          Delimiter
+  hi def link shFunctionDelim          Delimiter
+endif
+if exists("b:is_kornshell")
+  hi def link kshSpecialVariables      shShellVariables
+  hi def link kshStatement             shStatement
+  hi def link shFunctionParen          Delimiter
+endif
+
+hi def link shCaseError                Error
+hi def link shCondError                Error
+hi def link shCurlyError               Error
+hi def link shDerefError               Error
+hi def link shDerefWordError           Error
+hi def link shDoError          Error
+hi def link shEsacError                Error
+hi def link shIfError          Error
+hi def link shInError          Error
+hi def link shParenError               Error
+hi def link shTestError                Error
+if exists("b:is_kornshell")
+  hi def link shDTestError             Error
+endif
+
+hi def link shArithmetic               Special
+hi def link shCharClass                Identifier
+hi def link shSnglCase         Statement
+hi def link shCommandSub               Special
+hi def link shComment          Comment
+hi def link shConditional              Conditional
+hi def link shCtrlSeq          Special
+hi def link shExprRegion               Delimiter
+hi def link shFunctionKey              Function
+hi def link shFunctionName             Function
+hi def link shNumber           Number
+hi def link shOperator         Operator
+hi def link shRepeat           Repeat
+hi def link shSet              Statement
+hi def link shSetList          Identifier
+hi def link shShellVariables           PreProc
+hi def link shSpecial          Special
+hi def link shStatement                Statement
+hi def link shString           String
+hi def link shTodo             Todo
+hi def link shAlias            Identifier
+
+" Set Current Syntax: {{{1
+" ===================
+if exists("b:is_bash")
+ let b:current_syntax = "bash"
+elseif exists("b:is_kornshell")
+ let b:current_syntax = "ksh"
+else
+ let b:current_syntax = "sh"
+endif
+
+" vim: ts=16 fdm=marker
diff --git a/.vim/syntax/svkannotate.vim b/.vim/syntax/svkannotate.vim
new file mode 100644 (file)
index 0000000..d93e9dd
--- /dev/null
@@ -0,0 +1,42 @@
+" Vim syntax file
+" Language:    SVK annotate output
+" Maintainer:  Bob Hiestand <bob.hiestand@gmail.com>
+" Remark:      Used by the vcscommand plugin.
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+
+if exists("b:current_syntax")
+       finish
+endif
+
+syn match svkDate /\d\{4}-\d\{1,2}-\d\{1,2}/ skipwhite contained
+syn match svkName /(\s*\zs\S\+/ contained nextgroup=svkDate skipwhite
+syn match svkVer /^\s*\d\+/ contained nextgroup=svkName skipwhite
+syn region svkHead start=/^/ end="):" contains=svkVer,svkName,svkDate oneline
+
+if !exists("did_svkannotate_syntax_inits")
+       let did_svkannotate_syntax_inits = 1
+       hi link svkName Type
+       hi link svkDate Comment
+       hi link svkVer Statement
+endif
+
+let b:current_syntax="svkAnnotate"
diff --git a/.vim/syntax/svnannotate.vim b/.vim/syntax/svnannotate.vim
new file mode 100644 (file)
index 0000000..87a63ab
--- /dev/null
@@ -0,0 +1,40 @@
+" Vim syntax file
+" Language:    SVN annotate output
+" Maintainer:  Bob Hiestand <bob.hiestand@gmail.com>
+" Remark:      Used by the vcscommand plugin.
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+
+if exists("b:current_syntax")
+       finish
+endif
+
+syn match svnName /\S\+/ contained
+syn match svnVer /^\s*\zs\d\+/ contained nextgroup=svnName skipwhite
+syn match svnHead /^\s*\d\+\s\+\S\+/ contains=svnVer,svnName
+
+if !exists("did_svnannotate_syntax_inits")
+       let did_svnannotate_syntax_inits = 1
+       hi link svnName Type
+       hi link svnVer Statement
+endif
+
+let b:current_syntax="svnAnnotate"
diff --git a/.vim/syntax/vcscommit.vim b/.vim/syntax/vcscommit.vim
new file mode 100644 (file)
index 0000000..80b4c6e
--- /dev/null
@@ -0,0 +1,31 @@
+" Vim syntax file
+" Language:    VCS commit file
+" Maintainer:  Bob Hiestand (bob.hiestand@gmail.com)
+" License:
+" Copyright (c) Bob Hiestand
+"
+" Permission is hereby granted, free of charge, to any person obtaining a copy
+" of this software and associated documentation files (the "Software"), to
+" deal in the Software without restriction, including without limitation the
+" rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+" sell copies of the Software, and to permit persons to whom the Software is
+" furnished to do so, subject to the following conditions:
+"
+" The above copyright notice and this permission notice shall be included in
+" all copies or substantial portions of the Software.
+"
+" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+" FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+" IN THE SOFTWARE.
+
+if exists("b:current_syntax")
+       finish
+endif
+
+syntax region vcsComment start="^VCS: " end="$"
+highlight link vcsComment Comment
+let b:current_syntax = "vcscommit"
diff --git a/.vimrc b/.vimrc
new file mode 100644 (file)
index 0000000..1680999
--- /dev/null
+++ b/.vimrc
@@ -0,0 +1,434 @@
+" Note, this config is many years old and needs serious updating.
+
+" }}}
+" vim:sw=4 ts=4 sts=4 foldmethod=marker
+" MAPPINGS: {{{1
+set encoding=utf-8  "the world is big
+"
+"
+" set tab stops to 4
+set sw=4
+set ts=4
+set sts=4
+" mapping that should change: -, +, _
+" j and k should go to first non-whitespace.
+" |i_CTRL-Y|
+"
+"
+" paste unnamed register
+inoremap <C-e> <C-r>"
+
+" when pasting, don't yank the pasted over text
+vnoremap p "_dP
+
+" save
+inoremap <C-s> <C-o>:update<CR>
+
+
+"swap " and '
+noremap " '
+noremap ' "
+
+" Make ctrl-n and ctrl-p cycle through buffers in cmd mode
+nnoremap <C-N> :bn<Enter>
+nnoremap <C-P> :bp<Enter>
+" quick command line window.
+noremap m q:k
+" mark
+noremap <A-m> m
+noremap , q:i
+noremap Q ,
+noremap q: Q
+" yank to end of line
+map Y y$
+" Insert a single char
+nmap <Space> i<Space><Esc>r
+
+" Previously unmammed keys.
+
+" yank document to clipboard
+noremap - mzgg"+yG<C-o>'zzz
+" save
+noremap <C-s> :w<CR>
+" current_file:line_number
+nmap \x :let @"=expand("%:t") . ':' . line(".")<cr>
+" split common files
+noremap <A-q> :tabe ~/.vimrc<CR>
+noremap <A-k> :tabe ~/t<CR>
+" paste clipboard
+noremap <A-p> "+p
+noremap <A-P> "+P
+" alt-shift-letter has some terminal incompatibilities, so
+" this needs to find a new key:
+inoremap <A-p> <C-r>+
+
+" yank clipboard
+noremap <A-y> "+y:let@*=@+<CR>
+" easier window, tab and quickfix cycling
+noremap <A-w> <C-w>w
+noremap <A-n> :cn<CR>
+noremap <A-N> :cp<CR>
+" needs new key
+
+"nohl
+noremap <silent> <C-l> :nohl<CR><C-l>
+inoremap <C-l> <C-O>:nohl<CR>
+
+" make x and X not affect registers
+noremap x "_x
+noremap X "_X
+"stop # from unindenting newlines with smartcase
+inoremap # X\b#
+
+"make * and # in visual mode search for the selection, instead of word under cursor.
+vnoremap * :<C-u>call <SID>VSearch()<CR>/<CR>
+vnoremap # :<C-u>call <SID>VSearch()<CR>?<CR>
+function! s:VSearch()
+  let old = @"
+  norm! gvy
+  let @/ = '\V' . substitute(escape(@", '\'), '\n', '\\n', 'g')
+  let @" = old
+endfunction
+
+" remove trailing whitespace
+nnoremap <F3> :%s/\s\+$//<CR>:unlet! b:statusline_trailing_space_warning<CR>
+
+" easy vim.. use ctrl-o and ctrl-l to use normal mode.
+noremap <F4> :call <SID>easyVim()<CR>
+inoremap <F4> <C-o>:call <SID>easyVim()<CR>
+function! s:easyVim()
+if !&insertmode
+       set insertmode
+       "menu and button stuff.
+       set go+=m
+       set go+=T
+       " right scrollbar
+       set go+=r
+       " left scrollbar
+       set go+=L
+else
+       set noinsertmode
+       set go-=m
+       set go-=T
+       set go-=r
+       set go-=L
+endif
+endfunction
+
+
+" found this to compile the current buffer, took a bit from it
+"function Make()
+  "let curr_dir = expand('%:h')
+  "if curr_dir == ''
+    "let curr_dir = '.'
+  "endif
+  "echo curr_dir
+  "execute 'lcd ' . curr_dir
+  "execute 'make %:r.o'
+  "execute 'lcd -'
+"endfun
+"imap <A-b> <Esc>:wa<CR>:call Make()<CR>
+"nmap <A-b> :wa<CR>:call Make()<CR>
+
+" standard make
+" haven't been using it so its getting kicked out
+"inoremap <A-d> <Esc>:wa<CR>:make<CR>
+"nnoremap <A-d> :wa<CR>:make<CR>
+inoremap <A-d> <Esc>gT
+nnoremap <A-d> gT
+inoremap <A-e> <Esc>gt
+nnoremap <A-e> gt
+
+" used to have !silent, but if make fails without an error that vim can parse,
+" it doesn't show any failure, which is extremely confusing. I'd like
+" something that would be silent except for a failure.
+command! -bar -complete=file -nargs=* Make lcd %:h | make <args> | cw
+nnoremap <A-b> :wa<CR>:Make<CR>
+inoremap <A-b> <Esc>:wa<CR>:Make<CR>
+
+nnoremap <A-c> :!./run.sh<CR>
+nnoremap <A-t> :silent! !ctags -R .<CR>:redr!<CR>
+
+" Execute the current line.
+nnoremap <A-v> "pyy:@p<CR>
+vnoremap <A-v> :<C-u>call ExecRegion('vis')<CR>
+function! ExecRegion(type, ...)
+       if a:type == "vis"
+               " Yank and execute the visual region.
+               :'<,'> y p
+               @p
+       endif
+endfun
+
+
+" EX COMMANDS: {{{1
+
+
+" originally I was using a macro for this
+" do yy@0 on the next line
+" ggzn/^{{\rnyyddnkk::r !date +"\%I:\%M \%p \%A \%D"\r<<zm'0pA
+" now I have hte following command:
+command! J call JournalEntry()
+function JournalEntry()
+       normal ggzn/^{{\rnyyddnO\e:call setline(".", strftime("%I:%M %p %A %d/%m/%y"))\r<<ddkPzm'0pzvjO
+       startinsert!
+endfun
+
+
+
+" insert date in the format I like
+noremap <A-s> :call setline(".", strftime("%I:%M %p %A %d/%m/%y"))<CR>
+command! T :call setline(".", strftime("%I:%M %p %A %d/%m/%y"))<CR>
+" need to lookup the cross platform date for this one
+command! D :r !date +"\%D"<CR>
+
+" for my super simple version control.
+command! -nargs=* I w|!ii % <args>
+command! -nargs=* O w|!io % <args>
+
+
+
+" ABREVIATIONS: {{{1
+
+ab J) Ïåñ
+
+
+" SETTINGS: {{{1
+"taken or modified from gentoo
+set wildignore+=.info,.aux,.log,.dvi,.bbl,.out,.o,.lo "ignore some files for filename completion
+" Terminal fixes
+" todo: make this work like == screen*
+if &term ==? "xterm" || &term ==? "screen" || &term == "xterm-color" || &term == "screen-bce"
+       set t_Sb=^[4%dm
+       set t_Sf=^[3%dm
+       set ttymouse=xterm2
+       set term=xterm-color
+       set t_Co=256 "terminal colors
+endif
+set virtualedit=block "Allow visual block to move in empty space.
+set showbreak=\  "show a single character on wrapped lines, uses the non-text highlight.
+
+
+set ttimeoutlen=10     " 10 milliseconds for esc timeout
+set nocompatible
+" end of gentoo stuff
+
+" Allow split windows to shrink to this size
+set winminheight=0
+
+" show partial wrapped lines at the bottom
+set display=lastline
+
+" show menu for completion
+set wildmenu
+
+" was auto, but this caused new files created when writting which messes up
+" my aufs
+set bkc=yes
+
+" we can see what mode we are in from the cursor.
+set nomodeline
+
+"
+if v:version >= 701
+       set diffopt=filler,context:1,vertical,foldcolumn:1
+endif
+
+" grep will sometimes skip displaying the file name if you
+" search in a singe file. Set grep to always generate a file-name.
+set grepprg=grep\ -nH\ $*
+set report=0          " Show a 'N lines were changed' report always
+set shiftround    " < and > will hit indent levels instead of +-4 always
+" note: for some reason, windows gvim didn't like ~/tmp to be windows
+" directory link to ../m/tmp. I used a different dir.
+set backupdir=~/.editor-backups " keep backup files in one directory.
+set mouse=a " Enable the mouse in all terminals if possible
+set laststatus=2
+set statusline=\|%n\|\ \ %f%M%R%h%W:%l\ \ \|%v»\ %P\|%<\ \ %{getcwd()}
+set modeline " use modelines when root. security be damned.
+set listchars=tab:._  "show tabs.
+set foldignore=  " don't ignore # when using foldmethod=indent
+
+filetype plugin on
+filetype indent on
+set autoindent
+syntax enable
+set hlsearch
+"Tab settings, for reference.
+"set shiftwidth=2
+"set noexpandtab
+"set softtabstop=2
+"set tabstop=2
+"End tab settings.
+set number
+"viminfo settings
+" the viminfo file deletes symlinks, so i moved it to tmp/
+set vi='100,<1000,s10,h,n~/.viminfo
+set foldlevel=99
+set ic "set ignorecase
+set scs        "set smartcase
+" allow backspacing over everything in insert mode
+set backspace=indent,eol,start
+set backup             " keep a backup file
+set history=1000               " keep 1000 lines of command line history
+set showcmd            " display incomplete commands
+set incsearch          " do incremental searching
+colorscheme nightsky
+set foldmethod=indent
+
+" Just need to define it. Picked single char 'y' cuz its the smallest
+let c_no_comment_fold = 1
+let c_space_errors = 1
+"for folding C preprocessor if, from my syntax addition.
+"the problem is that sometime
+" let cpre
+
+"for easy x11 cutting and pasting, but it gets in the way.
+"note: exclude must be the last option.
+"set clipboard=autoselect,unnamed,exclude:cons\|linux
+
+
+" for syntax highlighting
+let is_bash = 1
+
+
+
+if $OS ==# 'Windows_NT'
+       set directory=p:\tmp,.
+endif
+
+" AUTOCOMMANDS: {{{1
+
+if !exists("autocommands_loaded")
+       let autocommands_loaded = 1
+
+
+
+       " au BufNewFile,BufRead /usr/src/linux*/**    set patchmode=.orig
+if v:version >= 701
+       au BufReadPre * set numberwidth=3
+endif
+       "au BufEnter * syntax sync fromstart
+       "
+       "my attempt to have foldlevel=0 but still show the cursor. gives errors
+       "when opening help files, so avoid that.
+       au BufWinEnter * if &ft != 'help' | exe "normal zv" | endif
+
+       " for highlighting c preprocessor conditionals
+       au BufNewFile,BufRead syn match prePConditional "^\s*#\s*\(ifndef\|else\|endif\|ifdef\)"
+       au BufNewFile,BufRead hi link prePConditional WarningMSG
+       " alternate version which highlights only certain things inside the main match
+       "syn match prePSub contained "[^#       ]*\>"
+       "syn match prePConditional contains=prePSub "^\s*#\s*\(ifndef\|else\|endif\|ifdef\)"
+       "hi link prePSub DiffText
+
+       " make shell scripts executable. todo: Make this into an ex command.
+       " au BufWritePost *.sh !/bin/bash -c 'if [ -x % ]; then exit; else /bin/chmod +x %; fi'
+
+       " When editing a file, always jump to the last cursor position
+       " this is in the reference manual.
+       autocmd BufReadPost *
+                               \ if ! exists("g:leave_my_cursor_position_alone") |
+                               \     if line("'\"") > 0 && line ("'\"") <= line("$") |
+                               \         exe "normal g'\"" |
+                               \     endif |
+                               \ endif
+endif
+
+autocmd FuncUndefined * exe 'runtime autoload/' . expand('<afile>') . '.vim'
+nnoremap <F6> :call SyntaxAttr()<CR>
+
+" HEX EDITING FUNCTION: {{{1
+
+if has("autocmd")
+       " vim -b : edit binary using xxd-format!
+
+       augroup Binary
+               au!
+               au BufReadPre *.bin,*.hex setlocal binary
+               au BufReadPost *
+                                       \ if &binary | exe "Hexmode" | endif
+               au BufWritePre *
+                                       \ if exists("b:editHex") && b:editHex && &binary |
+                                       \  exe "%!xxd -r" |
+                                       \ endif
+               au BufWritePost *
+                                       \ if exists("b:editHex") && b:editHex && &binary |
+                                       \  exe "%!xxd" |
+                                       \  exe "set nomod" |
+                                       \ endif
+       augroup END
+endif
+
+command Hexmode call ToggleHex()
+function ToggleHex()
+       " hex mode should be considered a read-only operation
+       " save values for modified and read-only for restoration later,
+       " and clear the read-only flag for now
+       let l:modified=&mod
+       let l:oldreadonly=&readonly
+       let &readonly=0
+       if !exists("b:editHex") || !b:editHex
+               " save old options
+               let b:oldft=&ft
+               let b:oldbin=&bin
+               " set new options
+               setlocal binary " make sure it overrides any textwidth, etc.
+               let &ft="xxd"
+               " set status
+               let b:editHex=1
+               " switch to hex editor
+               %!xxd
+       else
+               " restore old options
+               let &ft=b:oldft
+               if !b:oldbin
+                       setlocal nobinary
+               endif
+               " set status
+               let b:editHex=0
+               " return to normal editing
+               %!xxd -r
+       endif
+       " restore values for modified and read only state
+       let &mod=l:modified
+       let &readonly=l:oldreadonly
+endfunction
+
+" Copy matches function {{{1
+" Copy matches of the last search to a register (default is the clipboard).
+" Accepts a range (default is the current line).
+" Matches are appended to the register and each match is terminated by \n.
+" Usage: [RANGE]CopyMatches [REGISTER]
+command! -nargs=0 -range -register CopyMatches call s:CopyMatches(<line1>, <line2>, "<reg>")
+function! s:CopyMatches(line1, line2, reg)
+  let reg = a:reg != '' ? a:reg : '+'
+  for line in range(a:line1, a:line2)
+    let txt = getline(line)
+    let idx = match(txt, @/)
+    while idx > -1
+      exec "let @".reg." .= matchstr(txt, @/, idx) . \"\n\""
+      let end = matchend(txt, @/, idx)
+      let idx = match(txt, @/, end)
+    endwhile
+  endfor
+endfunction
+
+" Miscelany {{{1
+
+       " trailing whitespace warning
+       set statusline+=\ \ %{StatuslineTrailingSpaceWarning()}
+       "recalculate the trailing whitespace warning when idle, and after saving
+       autocmd cursorhold,bufwritepost * unlet! b:statusline_trailing_space_warning
+       "return '[\s]' if trailing white space is detected
+       "return '' otherwise
+       function! StatuslineTrailingSpaceWarning()
+               if !exists("b:statusline_trailing_space_warning")
+                       if search('\s\+$', 'nw') != 0
+                               let b:statusline_trailing_space_warning = '[\s]'
+                       else
+                               let b:statusline_trailing_space_warning = ''
+                       endif
+               endif
+               return b:statusline_trailing_space_warning
+       endfunction
diff --git a/.xbindkeysrc.scm b/.xbindkeysrc.scm
new file mode 100644 (file)
index 0000000..95f2b98
--- /dev/null
@@ -0,0 +1,80 @@
+
+;; left scroll = left/right, including alt/meta modifiers
+(xbindkey '(b:11) "echo KeyStrPress Left KeyStrRelease Left | xmacroplay -d 1 :0")
+(xbindkey '(b:10) "echo KeyStrPress Right KeyStrRelease Right | xmacroplay -d 1 :0")
+(xbindkey '(control b:11) "echo KeyStrPress Left KeyStrRelease Left | xmacroplay -d 1 :0")
+(xbindkey '(control b:10) "echo KeyStrPress Right KeyStrRelease Right | xmacroplay -d 1 :0")
+(xbindkey '(control alt b:11) "echo KeyStrPress Left KeyStrRelease Left | xmacroplay -d 1 :0")
+(xbindkey '(control alt b:10) "echo KeyStrPress Right KeyStrRelease Right | xmacroplay -d 1 :0")
+(xbindkey '(alt b:11) "echo KeyStrPress Left KeyStrRelease Left | xmacroplay -d 1 :0")
+(xbindkey '(alt b:10) "echo KeyStrPress Right KeyStrRelease Right | xmacroplay -d 1 :0")
+
+
+;; i chose delay of 1 without really testing, and delay of 2 because
+;; that is what worked with xdotool
+
+;; right mouse = up / down
+(xbindkey '(b:13) "echo KeyStrPress Up KeyStrRelease Up | xmacroplay -d 1 :0")
+(xbindkey '(b:14) "echo KeyStrPress Down KeyStrRelease Down | xmacroplay -d 1 :0")
+;; ctrl+alt = scroll
+(xbindkey '(control alt b:13) "echo KeyStrRelease Control_R KeyStrRelease Alt_L ButtonPress 4 ButtonRelease 4 KeyStrPress Control_R KeyStrPress Alt_L | xmacroplay -d 2 :0")
+(xbindkey '(control alt b:14) "echo KeyStrRelease Control_R KeyStrRelease Alt_L ButtonPress 5 ButtonRelease 5 KeyStrPress Control_R KeyStrPress Alt_L | xmacroplay -d 2 :0")
+;; ctrl = ctrl + up/down
+(xbindkey '(control b:13) "echo KeyStrPress Up KeyStrRelease Up | xmacroplay -d 1 :0")
+(xbindkey '(control b:14) "echo KeyStrPress Down KeyStrRelease Down | xmacroplay -d 1 :0")
+;; alt = alt+scroll
+(xbindkey '(alt b:13) "echo ButtonPress 4 ButtonRelease 4 | xmacroplay -d 1 :0")
+(xbindkey '(alt b:14) "echo ButtonPress 5 ButtonRelease 5 | xmacroplay -d 1 :0")
+
+
+
+
+;; disabled because xdotool is laggy on debian. worked on fedora. just using xmacro instead for now.
+;; ;; right mouse = up / down
+;; (xbindkey '(b:13) "xdotool key --delay 2 Up")
+;; (xbindkey '(b:14) "xdotool key --delay 2 Down")
+;; ;; ctrl+alt = scroll
+;; (xbindkey '(control alt b:13) "xdotool click --clearmodifiers --delay 1 4")
+;; (xbindkey '(control alt b:14) "xdotool click --clearmodifiers --delay 1 5")
+;; ;; ctrl = ctrl+scroll
+;; (xbindkey '(control b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(control b:14) "xdotool click --delay 2 5")
+;; ;; alt = alt+scroll
+;; (xbindkey '(alt b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(alt b:14) "xdotool click --delay 2 5")
+
+
+;; ;; left scroll = left/right, including alt/meta modifiers
+;; (xbindkey '(b:11) "xdotool key --delay 2 Left")
+;; (xbindkey '(b:10) "xdotool key --delay 2 Right")
+;; (xbindkey '(control b:11) "xdotool key --delay 2 Left")
+;; (xbindkey '(control b:10) "xdotool key --delay 2 Right")
+;; (xbindkey '(control alt b:11) "xdotool key --delay 2 Left")
+;; (xbindkey '(control alt b:10) "xdotool key --delay 2 Right")
+;; (xbindkey '(alt b:11) "xdotool key --delay 2 Left")
+;; (xbindkey '(alt b:10) "xdotool key --delay 2 Right")
+
+;; default delay is 12, i change to 5 or else some faster scrolls do not register
+
+
+;;started to define these, then figured there were so many of them,
+;;and the only point was so that some default scroll keybind would work,
+;;but I don't actually know of any, so disabled this
+;; (xbindkey '(shift b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(shift b:14) "xdotool click --delay 2 5")
+;; (xbindkey '(Mod4 b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(Mod4 b:14) "xdotool click --delay 2 5")
+;; (xbindkey '(alt shift b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(alt shift b:14) "xdotool click --delay 2 5")
+;; (xbindkey '(control shift b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(control shift b:14) "xdotool click --delay 2 5")
+;; (xbindkey '(Mod4 shift b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(Mod4 shift b:14) "xdotool click --delay 2 5")
+;; (xbindkey '(alt Mod4 b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(alt Mod4 b:14) "xdotool click --delay 2 5")
+;; (xbindkey '(control Mod4 b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(control Mod4 b:14) "xdotool click --delay 2 5")
+;; (xbindkey '(control alt shift b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(control alt shift b:14) "xdotool click --delay 2 5")
+;; (xbindkey '(alt shift b:13) "xdotool click --delay 2 4")
+;; (xbindkey '(alt shift b:14) "xdotool click --delay 2 5")
diff --git a/README b/README
index 9b8b6752a3a5b7bc7e1c73e7b40cd2b788a1d1e8..71f44188c5cba46ac08f87b71f539fec72fa6892 100644 (file)
--- a/README
+++ b/README
@@ -32,3 +32,11 @@ you can't back it up easily (for example, gvfs mountpoint in it breaks
 lots of things), and has things you don't want to backup. So, you could
 use a subdirectory. But typing /s is much faster than ~/s and in every
 root context, /home/username/s.
+
+
+Part of how I run GNU/Linux. I try fully automate my systems and store
+all configs and scripts, except private info at
+https://iankelling.org/git.
+
+Please email me if you have a patches, bugs, feedback, or republish this
+somewhere else: Ian Kelling <ian@iankelling.org>.
diff --git a/brc b/brc
new file mode 100644 (file)
index 0000000..53f8c5b
--- /dev/null
+++ b/brc
@@ -0,0 +1,2033 @@
+#!/bin/bash
+# this gets sourced. shebang is just for file mode detection
+
+# note, to catch errors in functions but not outside, do:
+# set -E -o pipefail
+# trap return ERR
+# trap 'trap ERR' RETURN
+
+
+
+############
+# settings #
+############
+
+CDPATH=.
+
+set -o pipefail
+
+# remove all aliases. aliases provided by the system tend to get in the way,
+# for example, error happens if I try to define a function the same name as an alias
+unalias -a
+
+# remove gnome keyring warning messages
+# there is probably a more proper way, but I didn't find any easily on google
+# now using xfce+xmonad instead of vanilla xmonad, so disabling this
+#unset GNOME_KEYRING_CONTROL
+
+# use extra globing features.
+shopt -s extglob
+# include .files when globbing, but ignore files name . and ..
+# setting this also sets dotglob.
+# Note, this doesn't work in bash 4.4 anymore, for paths with
+# more than 1 directory, like a/b/.foo, since * is fixed to not match /
+export GLOBIGNORE=*/.:*/..
+
+# broken with bash_completion package. Saw a bug for this once. Don't anymore.
+# still broken in wheezy
+# still buggered in latest stable from the web, version 2.1
+# perhaps its fixed in newer git version, which fails to make for me
+# this note is from 6-2014.
+# Also, enabling this before sourcing .bashrc makes PATH be empty.
+#shopt -s nullglob
+
+# make tab on an empty line do nothing
+shopt -s no_empty_cmd_completion
+
+# advanced completion
+# http://bash-completion.alioth.debian.org/
+# might be sourced by the system already, but I've noticed it not being sourced before
+if ! type _init_completion &> /dev/null && [[ -r "/usr/share/bash-completion/bash_completion" ]]; then
+    . /usr/share/bash-completion/bash_completion
+fi
+
+
+# fix spelling errors for cd, only in interactive shell
+shopt -s cdspell
+# append history instead of overwritting it
+shopt -s histappend
+# for compatibility, per gentoo/debian bashrc
+shopt -s checkwinsize
+# attempt to save multiline single commands as single history entries.
+shopt -s cmdhist
+# enable **
+shopt -s globstar
+
+
+# inside emcas fixes
+if [[ $RLC_INSIDE_EMACS ]]; then
+    # EMACS is used by bash on startup, but we don't need it anymore.
+    # plus I hit a bug in a makefile which inherited it
+    unset EMACS
+    export RLC_INSIDE_EMACS
+    export PAGER=cat
+    export MANPAGER=cat
+    # scp completion does not work, but this doesn't fix it. todo, figure this out
+    complete -r scp &> /dev/null
+    # todo, remote file completion fails, figure out how to turn it off
+    export NODE_DISABLE_COLORS=1
+    # This get's rid of ugly terminal escape chars in node repl
+    # sometime, I'd like to have completion working in emacs shell for node
+    # the offending chars can be found in lib/readline.js,
+    # things that do like:
+    # stream.write('\x1b[' + (x + 1) + 'G');
+    # We can remove them and keep readline, for example by doing this
+    # to start a repl:
+    #!/usr/bin/env nodejs
+    # var readline = require('readline');
+    # readline.cursorTo = function(a,b,c) {};
+    # readline.clearScreenDown = function(a) {};
+    # const repl = require('repl');
+    # var replServer = repl.start('');
+    #
+    # no prompt, or else readline complete seems to be confused, based
+    # on our column being different? node probably needs to send
+    # different kind of escape sequence that is not ugly. Anyways,
+    # completion doesn't work yet even with the ugly prompt, so whatever
+    #
+    export NODE_NO_READLINE=1
+
+fi
+
+
+if [[ $- == *i* ]]; then
+    # for readline-complete.el
+    if [[ $RLC_INSIDE_EMACS ]]; then
+        # all for readline-complete.el
+        stty echo
+        bind 'set horizontal-scroll-mode on'
+        bind 'set print-completions-horizontally on'
+        bind '"\C-i": self-insert'
+    else
+        # sakura == xterm-256color
+        # konsole == xterm
+        if [[ $TERM == "xterm" ]]; then
+            # control + arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash
+            bind '"\e[1;5C": shell-forward-word' 2>/dev/null
+            bind '"\e[1;5D": shell-backward-word' 2>/dev/null
+        else
+            # make ctrl-backspace work. for konsole, i fixed it through
+# /home/iank/.local/share/konsole/default.keytab
+            stty werase '^h'
+            bind '"\eOc": shell-forward-word'
+            bind '"\eOd": shell-backward-word'
+        fi
+        # i can't remember why i did this, probably to free up some keys to bind
+        # to other things in bash.
+        # other than C-c and C-z, the rest defined by stty -a are, at least in
+        # gnome-terminal, overridden by bash, or disabled by the system
+        stty  lnext undef stop undef start undef
+    fi
+
+fi
+
+
+# history number. History expansion is good.
+PS4='$LINENO+ '
+# history file size limit, set to unlimited.
+# this needs to be different from the default because
+# default HISTFILESIZE is 500 and could clobber our history
+HISTFILESIZE=
+# max commands 1 session can append/read from history
+HISTSIZE=100000
+# my own history size limit based on lines
+HISTFILELINES=1000000
+HISTFILE=$HOME/.bh
+# the time format display when doing the history command
+# also, setting this makes the history file record time
+# of each command as seconds from the epoch
+HISTTIMEFORMAT="%I:%M %p %m/%d "
+# consecutive duplicate lines don't go in history
+HISTCONTROL=ignoredups
+# works in addition to HISTCONTROL to do more flexible things
+# it could also do the same things as HISTCONTROL and thus replace it,
+# but meh. dunno why, but just " *" does glob expansion, so use [ ] to avoid it.
+HISTIGNORE='k *:[ ]*'
+
+export BC_LINE_LENGTH=0
+
+
+# note, if I use a machine I don't want files readable by all users, set
+# umask 077  # If fewer than 4 digits are entered, leading zeros are assumed
+
+C_DEFAULT_DIR=/a
+
+
+###################
+## include files ###
+###################
+for _x in /a/bin/distro-functions/src/* /a/bin/!(githtml)/*-function?(s); do
+    source "$_x"
+done
+unset _x
+# so I can share my bashrc
+for x in /a/bin/bash_unpublished/source-!(.#*); do source $x; done
+source $(dirname $(readlink -f $BASH_SOURCE))/path_add-function
+source /a/bin/log-quiet/logq-function
+path_add /a/exe
+path_add --ifexists --end /a/opt/adt-bundle*/tools /a/opt/adt-bundle*/platform-tools
+export WCDHOME=/a
+# based on readme.debian. dunno if this will break on other distros.
+_x=/usr/share/wcd/wcd-include.sh
+if [[ -e $_x ]]; then source $_x; fi
+
+
+###############
+### aliases ###
+###############
+
+# very few aliases, functions are always preferred.
+
+# ancient stuff.
+if [[ $OS == Windows_NT ]]; then
+    alias ffs='cygstart "/c/Program Files (x86)/Mozilla Firefox/firefox.exe" -P scratch'
+    export DISPLAY=nt
+    alias j='command cygpath'
+    alias t='command cygstart'
+    alias cygstart='echo be quick, use the alias "t" instead :\)'
+    alias cygpath='echo be quick, use the alias "j" instead :\)'
+fi
+
+
+
+# keep this in mind? good for safety.
+# alias cp='cp -i'
+# alias mv='mv -i'
+
+
+# remove any default aliases for these
+unalias ls ll grep &>/dev/null ||:
+
+
+
+
+
+
+
+
+
+#####################
+###  functions   ####
+#####################
+
+
+..() { c ..; }
+...() { c ../..; }
+....() { c ../../..; }
+.....() { c ../../../..; }
+......() { c ../../../../..; }
+
+
+# file cut copy and paste, like the text buffers :)
+# I havn't tested these.
+_fbufferinit() { # internal use by
+    ! [[ $my_f_tempdir ]] && my_f_tempdir=$(mktemp -d)
+    rm -rf "$my_f_tempdir"/*
+}
+fcp() { # file cp
+    _fbufferinit
+    cp "$@" "$my_f_tempdir"/
+}
+fct() { # file cut
+    _fbufferinit
+    mv "$@" "$my_f_tempdir"/
+}
+fpst() { # file paste
+    [[ $2 ]] && { echo too many arguments; return 1; }
+    target=${1:-.}
+    cp "$my_f_tempdir"/* "$target"
+}
+
+
+# todo, update this
+complete -F _longopt la lower low rlt rld rl lld ts ll dircp ex fcp fct fpst gr
+
+
+_cdiff-prep() {
+    # join options which are continued to multiples lines onto one line
+    local first=true
+    grep -vE '^([ \t]*#|^[ \t]*$)' "$1" | while IFS= read -r line; do
+        # remove leading spaces/tabs. assumes extglob
+        if [[ $line == "[      ]*" ]]; then
+            line="${line##+( )}"
+        fi
+        if $first; then
+            pastline="$line"
+            first=false
+        elif [[ $line == *=* ]]; then
+            echo "$pastline" >> "$2"
+            pastline="$line"
+        else
+            pastline="$pastline $line"
+        fi
+    done
+    echo "$pastline" >> "$2"
+}
+
+_khfix_common() {
+    local h=${1##*@}
+    local x
+    ssh-keygen -R $h -f $(readlink -f ~/.ssh/known_hosts)
+    x=$(timeout 1 ssh -oBatchMode=yes -oControlMaster=no -oControlPath=/ -v $1 |& sed -rn "s/debug1: Connecting to $h \[([^\]*)].*/\1/p")
+    if [[ ! $x ]]; then
+        echo "khfix: ssh failed"
+        return 1
+    fi
+    echo "khfix: removing key for $x"
+    ssh-keygen -R $x -f $(readlink -f ~/.ssh/known_hosts)
+}
+khfix() { # known hosts fix
+    _khfix_common "$@" || return 1
+    ssh $1 :
+}
+khcopy() {
+    _khfix_common "$@"
+    ssh-copy-id $1
+}
+
+a() {
+    beet "${@}"
+}
+
+ack() { ack-grep "$@"; }
+
+anki() {
+    if which anki &>/dev/null; then
+        command anki
+    else
+        schroot -c anki -- anki
+    fi
+}
+
+astudio() {
+    # googling android emulator libGL error: failed to load driver: r600
+    # lead to http://stackoverflow.com/a/36625175/14456
+    export ANDROID_EMULATOR_USE_SYSTEM_LIBS=1
+    /a/opt/android-studio/bin/studio.sh "$@" &r;
+}
+
+b() {
+    # backwards
+    c -
+}
+
+bkrun() {
+    # use -p from interactive shell
+    btrbk-run -p "$@"
+}
+
+bfg() { java -jar /a/opt/bfg-1.12.14.jar "$@"; }
+
+bigclock() {
+    xclock -digital -update 1 -face 'arial black-80:bold'
+}
+
+btc() {
+    local f=/etc/bitcoin/bitcoin.conf
+    # importprivkey will timeout if using the default of 15 mins.
+    # upped it to 1 hour.
+    bitcoin-cli -rpcclienttimeout=60000 -$(s grep rpcuser= $f) -$(s grep rpcpassword= $f) "$@"
+}
+
+btcusd() { # $1 btc in usd
+    local price
+    price="$(curl -s https://api.coinbase.com/v2/prices/BTC-USD/spot | jq -r .data.amount)"
+    printf "$%s\n" "$price"
+    if [[ $1 ]]; then
+        printf "$%.2f\n" "$(echo "scale=4; $price * $1"| bc -l)"
+    fi
+}
+usdbtc() { # $1 usd in btc
+    local price
+    price="$(curl -s https://api.coinbase.com/v2/prices/BTC-USD/spot | jq -r .data.amount)"
+    printf "$%s\n" "$price"
+    if [[ $1 ]]; then
+        # 100 mil satoshi / btc. 8 digits after the 1.
+        printf "%.8f btc\n" "$(echo "scale=10; $1 / $price "| bc -l)"
+    fi
+}
+satoshi() { # $1 satoshi in usd
+    local price
+    price="$(curl -s https://api.coinbase.com/v2/prices/BTC-USD/spot | jq -r .data.amount)"
+    price=$(echo "scale=10; $price * 0.00000001"| bc -l)
+    printf "$%f\n" "$price"
+    if [[ $1 ]]; then
+        printf "$%.2f\n" "$(echo "scale=10; $price * $1"| bc -l)"
+    fi
+}
+
+
+# c. better cd
+if [[ $RLC_INSIDE_EMACS ]]; then
+    c() { wcd -c -z 50 -o "$@"; }
+else
+    # lets see what the fancy terminal does from time to time
+    c() { wcd -c -z 50 "$@"; }
+fi
+
+caa() { git commit --amend --no-edit -a; }
+
+caf() {
+    find -L $1 -type f -not \( -name .svn -prune -o -name .git -prune \
+         -o -name .hg -prune -o -name .editor-backups -prune \
+         -o -name .undo-tree-history -prune \) \
+         -exec bash -lc 'hr; echo "$1"; hr; cat "$1"' _ {} \; 2>/dev/null
+
+}
+
+calc() { echo "scale=3; $*" | bc -l; }
+# no having to type quotes, but also no command history:
+clc() {
+    local x
+    read -r x
+    echo "scale=3; $x" | bc -l
+}
+
+cam() {
+    git commit -am "$*"
+}
+
+ccat () { # config cat. see a config without extra lines.
+    grep '^\s*[^;[:space:]#]' "$@"
+}
+
+cdiff() {
+    # diff config files,
+    # setup for format of postfix, eg:
+    # option = stuff[,]
+    #    [more stuff]
+    local pastline
+    local unified="$(mktemp)"
+    local f1="$(mktemp)"
+    local f2="$(mktemp)"
+    _cdiff-prep "$1" "$f1"
+    _cdiff-prep "$2" "$f2"
+    cat "$f1" "$f2" |  grep -Po '^[^=]+=' | sort | uniq > "$unified"
+    while IFS= read -r line; do
+        # the default bright red / blue doesn't work in emacs shell
+        dwdiff -cblue,red -A best -d "         ," <(grep "^$line" "$f1" || echo ) <(grep "^$line" "$f2" || echo ) | colordiff
+    done < "$unified"
+}
+
+cgpl()
+{
+    if (($#)); then
+        cp /a/bin/data/COPYING "$@"
+    else
+        cp /a/bin/data/COPYING .
+    fi
+}
+capache()
+{
+    if (($#)); then
+        cp /a/bin/data/LICENSE "$@"
+    else
+        cp /a/bin/data/LICENSE .
+    fi
+}
+chown() {
+    # makes it so chown -R symlink affects the symlink and its target.
+    if [[ $1 == -R ]]; then
+        shift
+        command chown -h "$@"
+        command chown -R "$@"
+    else
+        command chown "$@"
+    fi
+}
+
+cim() {
+    git commit -m "$*"
+}
+
+cl() {
+    # choose recent directory. cl = cd list
+    c =
+}
+
+chrome() {
+    if type -p chromium &>/dev/null; then
+        cmd=chromium
+    else
+        cd
+        cmd="schroot -c stretch chromium"
+        CHROMIUM_FLAGS='--enable-remote-extensions' $cmd &r
+    fi
+}
+
+d() { builtin bg; }
+complete -A stopped -P '"%' -S '"' d
+
+dat() { # do all tee, for more complex scripts
+    tee >(ssh frodo bash -l) >(bash -l) >(ssh x2 bash -l) >(ssh tp bash -l)
+}
+da() { # do all
+    local host
+    "$@"
+    for host in x2 tp treetowl; do
+        ssh $host "$@"
+    done
+}
+
+dc() {
+    diff --strip-trailing-cr -w "$@"   # diff content
+}
+
+debian_pick_mirror () {
+    # netselect-apt finds a fast mirror.
+    # but we need to replace the mirrors ourselves,
+    # because it doesn't do that. best it can do is
+    # output a basic sources file
+    # here we get the server it found, get the main server we use
+    # then substitute all instances of one for the other in the sources file
+    # and backup original to /etc/apt/sources.list-original.
+    # this is idempotent. the only way to identify debian sources is to
+    # note the original server, so we put it in a comment so we can
+    # identify it later.
+    local file=$(mktemp -d)/f # safe way to get file name without creating one
+    sudo netselect-apt -o "$file" || return 1
+    url=$(grep ^\\w $file | head -n1 | awk '{print $2}')
+    sudo cp -f /etc/apt/sources.list /etc/apt/sources.list-original
+    sudo sed -ri "/http.us.debian.org/ s@( *[^ #]+ +)[^ ]+([^#]+).*@\1$url\2# http.us.debian.org@" /etc/apt/sources.list
+    sudo apt-get update
+}
+
+despace() {
+    local x y
+    for x in "$@"; do
+        y="${x// /_}"
+        safe_rename "$x" "$y"
+    done
+}
+
+dt() {
+    date "+%A, %B %d, %r" "$@"
+}
+
+dus() { # du, sorted, default arg of
+    du -sh ${@:-*} | sort -h
+}
+
+
+
+e() { echo "$@"; }
+
+
+ediff() {
+    [[ ${#@} == 2 ]] || { echo "error: ediff requires 2 arguments"; return 1; }
+    emacs --eval "(ediff-files \"$1\" \"$2\")"
+}
+
+
+envload() { # load environment from a previous: export > file
+    local file=${1:-$HOME/.${USER}_env}
+    eval "$(export | sed 's/^declare -x/export -n/')"
+    while IFS= read -r line; do
+        # declare -x makes variables local to a function
+        eval ${line/#declare -x/export}
+    done < "$file"
+}
+
+# mail related
+etail() {
+    sudo tail -f /var/log/exim4/mainlog
+}
+
+f() {
+    # cd forward
+    c +
+}
+
+fa() {
+    # find array. make an array of file names found by find into $x
+    # argument: find arguments
+    # return: find results in an array $x
+    while read -rd ''; do
+        x+=("$REPLY");
+    done < <(find "$@" -print0);
+}
+
+faf() { # find all files
+    find -L $1 -type f -not \( -name .svn -prune -o -name .git -prune \
+         -o -name .hg -prune -o -name .editor-backups -prune \
+         -o -name .undo-tree-history -prune \) 2>/dev/null
+}
+
+# one that comes with distros is too old for newer devices
+fastboot() {
+    /a/opt/android-platform-tools/fastboot "$@";
+}
+
+
+# List of apps to install/update
+# Create from existing manually installed apps by doing
+# fdroidcl search -i, then manually removing
+# automatically installed/preinstalled apps
+
+# firefox updater. commented out, firefox depends on nonfree opengl.
+# de.marmaro.krt.ffupdater
+# # causes replicant to die on install and go into a boot loop
+# me.ccrama.redditslide
+#
+# # my attempt at recovering from boot loop:
+# # in that case, boot to recovery (volume up, home button, power, let go of power after samsun logo)
+# # then
+# mount /dev/block/mmcblk0p12 /data
+# cd /data
+# find -iname '*appname*'
+# rm -rf FOUND_DIRS
+# usually good enough to just rm -rf /data/app/APPNAME
+#
+# currently broken:
+#at.bitfire.davdroid
+fdroid_pkgs=(
+    com.alaskalinuxuser.justnotes
+    com.artifex.mupdfdemo
+    com.fsck.k9
+    com.ichi2.anki
+    com.jmstudios.redmoon
+    com.notecryptpro
+    com.nutomic.syncthingandroid
+    com.termux
+    com.zoffcc.applications.zanavi
+    cz.martykan.forecastie
+    de.danoeh.antennapod
+    im.vector.alpha # riot
+    info.papdt.blackblub
+    me.tripsit.tripmobile
+    net.gaast.giggity
+    net.osmand.plus
+    net.sourceforge.opencamera
+    org.dmfs.tasks # caldav tasks thing
+    org.fdroid.fdroid
+    org.isoron.uhabits
+    org.kde.kdeconnect_tp
+    org.quantumbadger.redreader
+    org.smssecure.smssecure
+    org.fedorahosted.freeotp
+)
+# https://forum.xda-developers.com/android/software-hacking/wip-selinux-capable-superuser-t3216394
+# for maru,
+#me.phh.superuser
+
+fdup() {
+    local -A installed updated
+    local p
+    fdroidcl update
+    for p in $(fdroidcl search -i| grep -o "^\S\+"); do
+        installed[$p]=true
+    done
+    for p in $(fdroidcl search -u| grep -o "^\S\+"); do
+        updated[$p]=false
+    done
+    for p in ${fdroid_pkgs[@]}; do
+        ${installed[$p]:-false} || fdroidcl install $p
+    done
+    for p in ${!installed[@]}; do
+        ${updated[$p]:-true} || fdroidcl upgrade $p
+    done
+}
+
+firefox-default-profile() {
+    key=Default value=1 section=$1
+    file=/p/c/subdir_files/.mozilla/firefox/profiles.ini
+    sed -ri "/^ *$key/d" "$file"
+    sed -ri "/ *\[$section\]/,/^ *\[[^]]+\]/{/^\s*$key[[:space:]=]/d};/ *\[$section\]/a $key=$value" "$file"
+}
+fdhome() { #firefox default home profile
+    firefox-default-profile Profile0
+}
+
+fdwork() {
+    firefox-default-profile Profile4
+}
+
+ff() {
+    if type -P firefox &>/dev/null; then
+        firefox "$@"
+    else
+        iceweasel "$@"
+    fi
+}
+
+
+
+fn() {
+    firefox -P alt "$@" >/dev/null 2>&1
+}
+
+
+fsdiff () {
+    local missing=false
+    local dname="${PWD##*/}"
+    local m="/a/tmp/$dname-missing"
+    local d="/a/tmp/$dname-diff"
+    [[ -e $d ]] && rm "$d"
+    [[ -e $m ]] && rm "$m"
+    local msize=0
+    local fsfile
+    while read -r line; do
+        fsfile="$1${line#.}"
+        if [[ -e "$fsfile" ]]; then
+            md5diff "$line" "$fsfile" && tee -a "/a/tmp/$dname-diff" <<< "$fsfile $line"
+        else
+            missing=true
+            echo "$line" >> "$m"
+            msize=$((msize + 1))
+        fi
+    done < <(find -type f )
+    if $missing; then
+        echo "$m"
+        (( msize <= 100 )) && cat $m
+    fi
+}
+fsdiff-test() {
+    # expected output, with different tmp dirs
+    # /tmp/tmp.HDPbwMqdC9/c/d ./c/d
+    # /a/tmp/tmp.qLDkYxBYPM-missing
+    # ./b
+    cd $(mktemp -d)
+    echo ok > a
+    echo nok > b
+    mkdir c
+    echo ok > c/d
+    local x=$(mktemp -d)
+    mkdir $x/c
+    echo different > $x/c/d
+    echo ok > $x/a
+    fsdiff $x
+}
+rename-test() {
+    # test whether missing files were renamed, generally for use with fsdiff
+    # $1 = fsdiff output file, $2 = directory to compare to. pwd = fsdiff dir
+    # echos non-renamed files
+    local x y found
+    unset sums
+    for x in "$2"/*; do
+        { sums+=( "$(md5sum < "$x")" ) ; } 2>/dev/null
+    done
+    while read -r line; do
+        { missing_sum=$(md5sum < "$line") ; } 2>/dev/null
+        renamed=false
+        for x in "${sums[@]}"; do
+            if [[ $missing_sum == "$x" ]]; then
+                renamed=true
+                break
+            fi
+        done
+        $renamed || echo "$line"
+    done < "$1"
+    return 0
+}
+
+feh() {
+    # F = fullscren, z = random, Z = auto zoom
+    command feh -FzZ "$@"
+}
+
+# mail related
+frozen() {
+    rm -rf /tmp/frozen
+    s mailq |gr frozen|awk '{print $3}' | while read -r id; do
+        s exim -Mvl $id
+        echo
+        s exim -Mvh $id
+        echo
+        s exim -Mvb $id
+        echo -e '\n\n##############################\n'
+    done | tee -a /tmp/frozen
+}
+frozenrm() {
+    local ids=()
+    while read -r line; do
+        printf '%s\n' "$line"
+        ids+=($(printf '%s\n' "$line" |gr frozen|awk '{print $3}'))
+    done < <(s mailq)
+    echo "sleeping for 2 in case you change your mind"
+    sleep 2
+    s exim -Mrm "${ids[@]}"
+}
+
+funce() {
+    # like -e for functions. returns on error.
+    # at the end of the function, disable with:
+    # trap ERR
+    trap 'echo "${BASH_COMMAND:+BASH_COMMAND=\"$BASH_COMMAND\" }
+${FUNCNAME:+FUNCNAME=\"$FUNCNAME\" }${LINENO:+LINENO=\"$LINENO\"  }\$?=$?"
+trap ERR
+return' ERR
+}
+
+
+fw() {
+    firefox -P default "$@" >/dev/null 2>&1
+}
+
+getdir () {
+    local help="Usage: getdir [--help] PATH
+Output the directory of PATH, or just PATH if it is a directory."
+    if [[ $1 == --help ]]; then
+        echo "$help"
+        return 0
+    fi
+    if [[ $# -ne 1 ]]; then
+        echo "getdir error: expected 1 argument, got $#"
+        return 1
+    fi
+    if [[ -d $1 ]]; then
+        echo "$1"
+    else
+        local dir="$(dirname "$1")"
+        if [[ -d $dir ]]; then
+            echo "$dir"
+        else
+            echo "getdir error: directory does not exist"
+            return 1
+        fi
+    fi
+}
+
+git_empty_branch() { # start an empty git branch. carefull, it deletes untracked files.
+    [[ $# == 1 ]] || { echo 'need a branch name!'; return 1;}
+    local gitroot
+    gitroot || return 1 # function to set gitroot
+    builtin cd "$gitroot"
+    git symbolic-ref HEAD refs/heads/$1
+    rm .git/index
+    git clean -fdx
+}
+
+gitroot() {
+    local help="Usage: gitroot [--help]
+Print the full path to the root of the current git repo
+
+Handles being within a .git directory, unlike git rev-parse --show-toplevel,
+and works in older versions of git which did not have that."
+    if [[ $1 == --help ]]; then
+        echo "$help"
+        return
+    fi
+    local p=$(git rev-parse --git-dir) || { echo "error: not in a git repo" ; return 1; }
+    [[ $p != /* ]] && p=$PWD
+    echo "${p%%/.git}"
+}
+
+gitian() {
+    git config user.email ian@iankelling.org
+}
+
+gmacs() {
+    # quit will prompt if the program crashes.
+    gdb -ex=r -ex=quit --args emacs "$@"; r;
+}
+
+gdkill() {
+    # kill the emacs daemon
+    pk1 emacs --daemon
+}
+
+gse() {
+    git send-email --notes '--envelope-sender=<ian@iankelling.org>' \
+        --suppress-cc=self "$@"
+}
+
+gr() {
+    grep -iIP --color=auto "$@"
+}
+
+grr() {
+    if [[ ${#@} == 1 ]]; then
+        grep -riIP --color=auto "$@" .
+    else
+        grep -riIP --color=auto "$@"
+    fi
+}
+
+hstatus() {
+    # do git status on published repos
+    cd /a/bin/githtml
+    for x in !(forks) forks/* ian-specific/*; do
+        cd `readlink -f $x`/..
+        hr
+        echo $x
+        i status
+        cd /a/bin/githtml
+    done
+}
+
+hl() { # history limit. Write extra history to archive file.
+    # todo: this is not working or not used currently
+    local max_lines linecount tempfile prune_lines x
+    local harchive="${HISTFILE}_archive"
+    for x in "$HISTFILE" "$harchive"; do
+        [[ -e $x ]] || { touch "$x" && echo "notice from hl(): creating $x"; }
+        if  [[ ! $x || ! -e $x || ! -w $x || $(stat -c "%u" "$x") != $EUID  ]];  then
+            echo "error in hl: history file \$x:$x no good"
+            return 1
+        fi
+    done
+    history -a # save history
+    max_lines=$HISTFILELINES
+    [[ $max_lines =~ ^[0-9]+$ ]] || { echo "error in hl: failed to get max line count"; return 1; }
+    linecount=$(wc -l < $HISTFILE) # pipe so it doesn't output a filename
+    [[ $linecount =~ ^[0-9]+$ ]] || { echo "error in hl: wc failed"; return 1; }
+    if (($linecount > $max_lines)); then
+        prune_lines=$(($linecount - $max_lines))
+        head -n $prune_lines "$HISTFILE" >> "$harchive" \
+            && sed --follow-symlinks -ie "1,${prune_lines}d"  $HISTFILE
+    fi
+}
+
+hr() { # horizontal row. used to break up output
+    printf "$(tput setaf 5)█$(tput sgr0)%.0s" $(seq ${COLUMNS:-60})
+    echo
+}
+
+hrcat() { local f; for f; do [[ -f $f ]] || continue; hr; echo "$f"; cat "$f"; done }
+
+
+i() { git "$@"; }
+# modified from ~/local/bin/git-completion.bash
+# other completion commands are mostly taken from bash_completion package
+complete -o bashdefault -o default -o nospace -F _git i 2>/dev/null \
+    || complete -o default -o nospace -F _git i
+
+if ! type service &>/dev/null; then
+    service() {
+        echo actually running: systemctl $2 $1
+        systemctl $2 $1
+    }
+fi
+
+ic() {
+    # fast commit all
+    git commit -am "$*"
+}
+
+idea() {
+    /a/opt/idea-IC-163.7743.44/bin/idea.sh "$@" &r
+}
+
+ifn() {
+    # insensitive find
+    find -L . -not \( -name .svn -prune -o -name .git -prune \
+         -o -name .hg -prune -o -name .editor-backups -prune \
+         -o -name .undo-tree-history -prune \) -iname "*$**" 2>/dev/null
+}
+
+
+if [[ $OS == Windows_NT ]]; then
+    # cygstart wrapper
+    cs() {
+        cygstart "$@" &
+    }
+    xp() {
+        explorer.exe .
+    }
+    # launch
+    o() {
+        local x=(*$1*)
+        (( ${#x[#]} > 1 )) && { echo "warning ${#x[#]} matches found"; sleep 1; }
+        cygstart *$1* &
+    }
+else
+    o() {
+        if type gvfs-open &> /dev/null ; then
+            gvfs-open "$@"
+        else
+            xdg-open "$@"
+        fi
+        # another alternative is run-mailcap
+    }
+fi
+
+net-dev-info() {
+    e "lspci -nnk|gr -iA2 net"
+    lspci -nnk|gr -iA2 net
+    hr
+    e "s lshw -C network"
+    hr
+    s lshw -C network
+
+}
+
+istext() {
+    grep -Il "" "$@" &>/dev/null
+}
+
+jtail() {
+    journalctl -n 10000 -f "$@" | grep -Evi "^(\S+\s+){4}(sudo|sshd|cron)"
+}
+
+kff() { # keyboardio firmware flash
+    pushd /a/opt/sketches/Model01-Firmware
+    yes $'\n' | make flash
+    popd
+}
+
+l() {
+    if [[ $PWD == /[iap] ]]; then
+        command ls -A --color=auto -I lost+found "$@"
+    else
+        command ls -A --color=auto "$@"
+    fi
+}
+
+
+lcn() { locate -i "*$**"; }
+
+lld() { ll -d "$@"; }
+
+low() {  # make filenames lowercase, remove bad chars
+    local f new
+    for f in "$@"; do
+        new="${f,,}" # downcase
+        new="${new//[^[:alnum:]._-]/_}" # sub bad chars
+        new="${new#"${new%%[[:alnum:]]*}"}" # remove leading/trailing non-alnum
+        new="${new%"${new##*[[:alnum:]]}"}"
+        # remove bad underscores, like __ and _._
+        new=$(echo $new | sed -r 's/__+/_/g;s/_+([.-])|([.-])_+/\1/g')
+        safe_rename "$f" "$new" || return 1
+    done
+    return 0
+}
+
+lower() { # make first letter of filenames lowercase.
+    local x
+    for x in "$@"; do
+        if [[ ${x::1} == [A-Z] ]]; then
+            y=$(tr "[A-Z]" "[a-z]" <<<"${x::1}")"${x:1}"
+            safe_rename "$x" "$y" || return 1
+        fi
+    done
+}
+
+
+k() { # history search
+    grep -P --binary-files=text "$@" ${HISTFILE:-~/.bash_history}  | tail -n 80;
+}
+
+ks() { # history search
+    grep -P --binary-files=text "$@" ${HISTFILE:-~/.bash_history}  | uniq;
+}
+
+
+make-targets() {
+    # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
+    make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
+}
+
+mbenable() {
+    mb=$1
+    dst=/m/4e/$1
+    src=/m/md/$1
+    set -x
+    mv -T $src $dst || { set +x; return 1; }
+    ln -s -T $dst $src
+    /a/exe/lnf /p/.mu ~
+    mu index --maildir=/m/4e
+    set +x
+}
+mbdisable() {
+    mb=$1
+    dst=/m/md/$1
+    src=/m/4e/$1
+    set -x
+    if [[ -L $dst ]]; then rm $dst; fi
+    mv -T $src $dst
+    set +x
+}
+
+mdt() {
+    markdown -o /tmp/mdtest.html "$1"
+    firefox /tmp/mdtest.html
+}
+
+
+mkc() {
+    mkdir "$1"
+    c "$1"
+}
+
+mkt() { # mkdir and touch file
+    local path="$1"
+    mkdir -p "$(dirname "$path")"
+    touch "$path"
+}
+
+mkdir() { command mkdir -p "$@"; }
+
+mo() { xset dpms force off; } # monitor off
+
+otp() {
+    oathtool --totp -b "$@" | xclip -selection clipboard
+}
+
+# pithosfly is broken due to bitrot.
+pithos() {
+    cd /
+    export PYTHONPATH=/a/opt/Pithosfly
+    python3 -m pithos&r
+}
+
+pakaraoke() {
+    # from http://askubuntu.com/questions/456021/remove-vocals-from-mp3-and-get-only-instrumentals
+    pactl load-module module-ladspa-sink sink_name=Karaoke master=alsa_output.usb-Audioengine_Audioengine_D1-00.analog-stereo plugin=karaoke_1409 label=karaoke control=-30
+}
+
+
+pfind() { #find *$1* in $PATH
+    [[ $# != 1 ]] && { echo requires 1 argument; return 1; }
+    local pathArray
+    IFS=: pathArray=($PATH); unset IFS
+    find "${pathArray[@]}" -iname "*$1*"
+}
+
+pk1() {
+    local pid
+    pid=($(pgrep -f "$*"))
+    case ${#pid[@]} in
+        1)
+            ps -F $pid
+            m kill $pid
+            ;;
+        0) echo "no pid found" ;;
+        *)
+            ps -F ${pid[@]}
+            ;;
+    esac
+}
+
+pick-trash() {
+    # trash-restore lists everything that has been trashed at or below CWD
+    # This picks out files just in CWD, not subdirectories,
+    # which also match grep $1, usually use $1 for a time string
+    # which you get from running restore-trash once first
+    local name x ask
+    local nth=1
+    # last condition is to not ask again for ones we skipped
+    while name="$( echo | restore-trash | gr "$PWD/[^/]\+$" | gr "$1" )" \
+            && [[ $name ]] && (( $(wc -l <<<"$name") >= nth )); do
+        name="$(echo "$name" | head -n $nth | tail -n 1 )"
+        read -p "$name [Y/n] " ask
+        if [[ ! $ask || $ask == [Yy] ]]; then
+            x=$( echo "$name"  | gr -o "^\s*[0-9]*" )
+            echo $x | restore-trash > /dev/null
+        elif [[ $ask == [Nn] ]]; then
+            nth=$((nth+1))
+        else
+            return
+        fi
+    done
+}
+
+ping8() { ping 8.8.8.8; }
+
+pub() {
+    rld /a/h/_site/ li:/var/www/iankelling.org/html
+}
+
+pubip() { curl -4s https://icanhazip.com; }
+whatismyip() { pubip; }
+
+pumpa() {
+    # fixes the menu bar in xmonad. this won\'t be needed when xmonad
+    # packages catches up on some changes in future (this is written in
+    # 4/2017)
+    #
+    # geekosaur: so you'll want to upgrade to xmonad 0.13 or else use a
+    # locally modified XMonad.Hooks.ManageDocks that doesn't set the
+    # work area; turns out it's impossible to set correctly if you are
+    # not a fully EWMH compliant desktop environment
+    #
+    # geekosaur: chrome shows one failure mode, qt/kde another, other
+    # gtk apps a third, ... I came up with a setting that works for me
+    # locally but apparently doesn't work for others, so we joined the
+    # other tiling window managers in giving up on setting it at all
+    #
+    xprop -root -remove _NET_WORKAREA
+    command pumpa &r
+}
+
+
+pwgen() {
+    # -m = min length
+    # -x = max length
+    # -t = print pronunciation
+    apg -m 12 -x 16 -t
+}
+
+pwlong() {
+    # -M CLN = use Caps, Lowercase, Numbers
+    # -n 1 = 1 password
+    # -a 1 = use random instead of pronounceable algorithm
+    apg -m 50 -x 70 -n 1 -a 1 -M CLN
+}
+
+
+q() { # start / launch a program in the backround and redir output to null
+    "$@" &> /dev/null &
+}
+
+r() {
+    exit "$@" 2>/dev/null
+}
+
+rbpipe() { rbt post -o --diff-filename=- "$@"; }
+rbp() { rbt post -o "$@"; }
+
+rl() {
+    # rsync, root is required to keep permissions right.
+    # rsync  --archive --human-readable --verbose --itemize-changes --checksum \(-ahvic\) \
+        #                                      --no-times --delete
+    # basically, make an exact copy, use checksums instead of file times to be more accurate
+    rsync -ahvic --delete "$@"
+}
+rld() {
+    # like rlu, but don't delete files on the target end which
+    # do not exist on the original end.
+    rsync -ahvic "$@"
+}
+complete -F _rsync -o nospace rld rl rlt
+
+rlt() {
+    # rl without preserving modification time.
+    rsync -ahvic --delete --no-t "$@"
+}
+
+rlu() { # [OPTS] HOST PATH
+    # eg. rlu -opts frodo /testpath
+    # relative paths will expanded with readlink -f.
+    # useful for selectively sending dirs which have been synced with unison,
+    # where the path is the same on both hosts.
+    opts=("${@:1:$#-2}") #  1 to last -2
+    path="${@:$#}" # last
+    host="${@:$#-1:1}" # last -1
+    if [[ $path == .* ]]; then
+        path=$(readlink -f $path)
+    fi
+    # rync here uses checksum instead of time so we don't mess with
+    # unison relying on time as much. g is for group, same reason
+    # to keep up with unison.
+    s rsync -rlpchviog --relative "${opts[@]}" "$path" "root@$host:/";
+}
+
+# only run on desktop. simpler to keep this on one system.
+r2eadd() { # usage: name url
+    # initial setup of rss2email:
+    # r2e new r2e@iankelling.org
+    # that initializes files, and sets default email.
+    # symlink to the config doesn't work, so I copied it to /p/c
+    # and then use cli option to specify explicit path.
+    # Only option changed from default config is to set
+    # force-from = True
+    #
+    # or else for a few feeds, the from address is set by the feed, and
+    # if I fail delivery, then I send a bounce message to that from
+    # address, which makes me be a spammer.
+
+    r2e add $1 "$2" $1@r2e.iankelling.org
+    # get up to date and don't send old entries now:
+    r2e run --no-send $1
+}
+r2e() { command r2e -d /p/c/rss2email.json -c /p/c/rss2email.cfg "$@"; }
+
+rspicy() { # usage: HOST DOMAIN
+    # connect to spice vm remote host. use vspicy for local host
+    local port=$(ssh $1<<EOF
+sudo virsh dumpxml $2|grep "<graphics.*type='spice'" | \
+       sed -rn "s/.*port='([0-9]+).*/\1/p"
+EOF
+          )
+    if [[ $port ]]; then
+        spicy -h $1 -p $port
+    else
+        echo "error: no port found. check that the domain is running."
+    fi
+}
+
+rmstrips() {
+    ssh fencepost head -n 300 /gd/gnuorg/EventAndTravelInfo/rms-current-trips.txt
+}
+
+s() {
+    # background
+    # I use a function because otherwise we can't use in a script,
+    # can't assign to variable.
+    #
+    # note: gksudo is recommended for X apps because it does not set the
+    # home directory to the same, and thus apps writing to ~ fuck things up
+    # with root owned files.
+    #
+    if [[ $EUID != 0 || $1 == -* ]]; then
+        SUDOD="$PWD" sudo -i "$@"
+    else
+        "$@"
+    fi
+}
+
+safe_rename() { # warn and don't rename if file exists.
+    # mv -n exists, but it's silent
+    if [[ $# != 2 ]]; then
+        echo safe_rename error: $# args, need 2 >2
+        return 1
+    fi
+    if [[ $1 != $2 ]]; then # yes, we want to silently ignore this
+        if [[ -e $2 || -L $2 ]]; then
+            echo "Cannot rename $1 to $2 as it already exists."
+        else
+            mv -vi "$1" "$2"
+        fi
+    fi
+}
+
+
+sb() { # sudo bash -c
+    # use sb instead of s is for sudo redirections,
+    # eg. sb 'echo "ok fine" > /etc/file'
+    local SUDOD="$PWD"
+    sudo -i bash -c "$@"
+}
+complete -F _root_command s sb
+
+scssl() {
+    # s gem install scss-lint
+    pushd /a/opt/thoughtbot-guides
+    git pull --stat
+    popd
+    scss-lint -c /a/opt/thoughtbot-guides/style/sass/.scss-lint.yml "$@"
+}
+
+ser() {
+    local s; [[ $EUID != 0 ]] && s=sudo
+    if type -p systemctl &>/dev/null; then
+        $s systemctl $1 $2
+    else
+        $s service $2 $1
+    fi
+}
+
+setini() { # set a value in a .ini style file
+    key="$1" value="$2" section="$3" file="$4"
+    if [[ -s $file ]]; then
+        sed -ri -f - "$file" <<EOF
+# remove existing keys
+/ *\[$section\]/,/^ *\[[^]]+\]/{/^\s*$key[[:space:]=]/d}
+# add key
+/^\s*\[$section\]/a $key=$value
+# from section to eof, do nothing
+/^\s*\[$section\]/,\$b
+# on the last line, if we haven't found section yet, add section and key
+\$a [$section]\\
+$key=$value
+EOF
+    else
+        cat >"$file" <<EOF
+[$section]
+$key=$value
+EOF
+    fi
+}
+
+sgo() { # service go
+    service=$1
+    ser restart $service || return 1
+    if type -p systemctl &>/dev/null; then
+        ser enable $service
+    fi
+}
+
+
+shellck() {
+    # 2086 = unquoted $var
+    # 2046 = unquoted $(cmd)
+    # i had -x as an arg, but debian testing(stretch) doesn\'t support it
+    shellcheck -e 2086,2046,2068,2006,2119 "$@"
+}
+
+skaraoke() {
+    local tmp out
+    in="$1"
+    out=${2:-${1%.*}.sh}
+    tmp=$(mktemp -d)
+    script -t -c "mpv --no-config --no-resume-playback --no-terminal --no-audio-display '$1'" $tmp/typescript 2>$tmp/timing
+    # todo, the current sleep seems pretty good, but it
+    # would be nice to have an empirical measurement, or
+    # some better wait to sync up.
+    #
+    # note: --loop-file=no prevents it from hanging if you have that
+    # set to inf the mpv config.
+    # --loop=no prevents it from exit code 3 due to stdin if you
+    # had it set to inf in mpv config.
+    #
+    # args go to mpv, for example --volume=80, 50%
+    cat >$out <<EOFOUTER
+#!/bin/bash
+trap "trap - TERM && kill 0" INT TERM ERR; set -e
+( sleep .2; scriptreplay <( cat <<'EOF'
+$(cat $tmp/timing)
+EOF
+) <( cat <<'EOF'
+$(cat $tmp/typescript)
+EOF
+))&
+base64 -d - <<'EOF'| mpv --loop=no --loop-file=no --no-terminal --no-audio-display "\$@" -
+$(base64 "$1")
+EOF
+kill 0
+EOFOUTER
+    rm -r $tmp
+    chmod +x $out
+}
+
+slog() {
+    # log with script. timing is $1.t and script is $1.s
+    # -l to save to ~/typescripts/
+    # -t to add a timestamp to the filenames
+    local logdir do_stamp arg_base
+    (( $# >= 1 )) || { echo "arguments wrong"; return 1; }
+    logdir="/a/dt/"
+    do_stamp=false
+    while getopts "lt" option
+    do
+        case $option in
+            l ) arg_base=$logdir ;;
+            t ) do_stamp=true ;;
+        esac
+    done
+    shift $(($OPTIND - 1))
+    arg_base+=$1
+    [[ -e $logdir ]] || mkdir -p $logdir
+    $do_stamp && arg_base+=$(date +%F.%T%z)
+    script -t $arg_base.s 2> $arg_base.t
+}
+splay() { # script replay
+    #logRoot="$HOME/typescripts/"
+    #scriptreplay "$logRoot$1.t" "$logRoot$1.s"
+    scriptreplay "$1.t" "$1.s"
+}
+
+spend() {
+    s systemctl suspend
+}
+
+sr() {
+    # sudo redo. be aware, this command may not work right on strange distros or earlier software
+    if [[ $# == 0 ]]; then
+        sudo -E bash -c -l "$(history -p '!!')"
+    else
+        echo this command redos last history item. no argument is accepted
+    fi
+}
+
+srm () {
+    # with -ll, less secure but faster.
+    command srm -ll "$@"
+}
+
+srun() {
+    scp $2 $1:/tmp
+    ssh $1 /tmp/${2##*/} "${@:2}"
+}
+
+sss() { # ssh solo
+    ssh -oControlMaster=no -oControlPath=/ "$@"
+}
+ssk() {
+    local -a opts=()
+    while [[ $1 == -* ]]; do
+        opts+=("$1")
+        shift
+    done
+    m pkill -f "^ssh: /tmp/ssh_mux_${USER}_${1#*@}_22_"
+    m ssh "${opts[@]}" "$@"
+}
+
+swap() {
+    local tmp
+    tmp=$(mktemp)
+    mv $1 $tmp
+    mv $2 $1
+    mv $tmp $2
+}
+
+t() {
+    local x
+    local -a args
+    if type -t trash-put >/dev/null; then
+        # skip args that don't exist, or else trash-put will have an error
+        for x in "$@"; do
+            if [[ -e $x || -L $x ]]; then
+                args+=("$x")
+            fi
+        done
+        [[ ! ${args[@]} ]] || trash-put "${args[@]}"
+                           else
+                               rm -rf "$@"
+                          fi
+}
+
+
+ tclock() {
+     clear
+     date +%l:%_M
+     len=60
+     # this goes to full width
+     #len=${1:-$((COLUMNS -7))}
+     x=1
+     while true; do
+         if (( x == len )); then
+             end=true
+             d="$(date +%l:%_M)   "
+         else
+             end=false
+             d=$(date +%l:%M:%_S)
+         fi
+         echo -en "\r"
+         echo -n "$d"
+         for ((i=0; i<x; i++)); do
+             if (( i % 6 )); then
+                 echo -n _
+             else
+                 echo -n .
+             fi
+         done
+         if $end; then
+             echo
+             x=1
+         else
+             x=$((x+1))
+         fi
+         sleep 5
+     done
+ }
+
+
+ te() {
+     # test existence / exists
+     local ret=0
+     for x in "$@"; do
+         [[ -e "$x" || -L "$x" ]] || ret=1
+     done
+     return $ret
+ }
+
+ # mail related
+ testmail() {
+     declare -gi _seq; _seq+=1
+     echo "test body" | m mail -s "test mail from $HOSTNAME, $_seq" "${@:-root@localhost}"
+     # for testing to send from an external address, you can do for example
+     # -aFrom:ian@iank.bid web-6fnbs@mail-tester.com
+     # note in exim, you can retry a deferred message
+     # s exim -M MSG_ID
+     # MSG_ID is in /var/log/exim4/mainlog, looks like 1ccdnD-0001nh-EN
+ }
+
+ # to test sieve, use below command. for fsf mail, see fsf-get-mail script.
+ # make modifications, then copy to live file, use -eW to actually modify mailbox
+ # cp /p/c/subdir_files/sieve/personal{test,}.sieve; testsievelist -eW INBOX
+ #
+ # Another option is to use sieve-test SCRIPT MAIL_FILE. note,
+ # sieve-test doesn't know about envelopes, I'm not sure if sieve-filter does.
+
+ # sieve with output filter. arg is mailbox, like INBOX.
+ # This depends on dovecot conf, notably mail_location in /etc/dovecot/conf.d/10-mail.conf
+
+ testsievelist() {
+     sieve-filter ~/sieve/maintest.sieve "$@" >/tmp/testsieve.log 2> >(tail) && sed -rn '/^Performed actions:/{n;n;p}' /tmp/testsieve.log | sort -u
+ }
+
+
+ # mail related
+ # plain sieve
+ testsieve() {
+     sieve-filter ~/sieve/main.sieve "$@"
+ }
+
+ # mail related
+ testexim() {
+     # testmail above calls sendmail, which is a link to exim/postfix.
+     # it's docs don't say a way of adding an argument
+     # to sendmail to turn on debug output. We could make a wrapper, but
+     # that is a pain. Exim debug args are documented here:
+     # http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_exim_command_line.html
+     #
+     # http://www.exim.org/exim-html-current/doc/html/spec_html/ch-building_and_installing_exim.html
+     # note, for exim daemon, you can turn on debug options by
+     # adding -d, etc to COMMONOPTIONS in
+     # /etc/default/exim4
+     # for testing external mail, you need the to address as final cmdline arg
+     exim -d+tls -t <<'EOF'
+From: root@frodo.lan
+To: ian@mail.iankelling.org
+Subject: Testing Exim
+
+This is a test message.
+EOF
+ }
+
+ tm() {
+     # timer in minutes
+     # --no-config
+     (sleep $(calc "$@ * 60") && mpv --no-config --volume 50 /a/bin/data/alarm.mp3) > /dev/null 2>&1 &
+ }
+
+ trg() { transmission-remote-gtk&r; }
+ trc() {
+     # example, set global upload limit to 100 kilobytes:
+     # trc -u 100
+     TR_AUTH=":$(jq -r .profiles[0].password ~/.config/transmission-remote-gtk/config.json)" transmission-remote transmission.lan -ne "$@"
+ }
+
+
+ tu() {
+     local s;
+     local dir="$(dirname "$1")"
+     if [[ -e $1 && ! -w $1 || ! -w $(dirname "$1") ]]; then
+         s=s;
+     fi
+     $s teeu "$@"
+ }
+
+ tx() { # toggle set -x, and the prompt so it doesn't spam
+        if [[ $- == *x* ]]; then
+            set +x
+            PROMPT_COMMAND=prompt-command
+        else
+            unset PROMPT_COMMAND
+            PS1="\w \$ "
+            set -x
+        fi
+}
+
+psnetns() {
+    # show all processes in the network namespace $1.
+    # blank entries appear to be subprocesses/threads
+    local x netns
+    netns=$1
+    ps -w | head -n 1
+    s find -L /proc/[1-9]*/task/*/ns/net -samefile /run/netns/$netns | cut -d/ -f5 | \
+        while read l; do
+            x=$(ps -w --no-headers -p $l);
+            if [[ $x ]]; then echo "$x"; else echo $l; fi;
+        done
+}
+
+m() { printf "%s\n" "$*";  "$@"; }
+
+
+vpncmd() {
+    m s nsenter -t $(pgrep -f "/usr/sbin/openvpn .* --config /etc/openvpn/client/client.conf") -n -m "$@"
+}
+vpnf() {
+    vpncmd gksudo -u ian "firefox -no-remote -P firefox-main-profile" &r
+}
+vpnbash() {
+    vpncmd bash
+}
+
+
+
+virshrm() {
+    for x in "$@"; do virsh destroy "$x"; virsh undefine "$x"; done
+}
+
+vm-set-listen(){
+    local t=$(mktemp)
+    local vm=$1
+    local ip=$2
+    s virsh dumpxml $vm | sed -r "s/(<listen.*address=')([^']+)/\1$ip/" | \
+        sed -r "s/listen='[^']+/listen='$ip/"> $t
+    s virsh undefine $vm
+    s virsh define $t
+}
+
+
+vmshare() {
+    vm-set-listen $1 0.0.0.0
+}
+
+
+vmunshare() {
+    vm-set-listen $1 127.0.0.1
+}
+
+
+vpn() {
+    if [[ -e /lib/systemd/system/openvpn-client@.service ]]; then
+        local vpn_service=openvpn-client
+    else
+        local vpn_service=openvpn
+    fi
+
+    [[ $1 ]] || { echo need arg; return 1; }
+    journalctl --unit=$vpn_service@$1 -f -n0 &
+    s systemctl start $vpn_service@$1
+    # sometimes the ask-password agent does not work and needs a delay.
+    sleep .5
+    # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=779240
+    # noticed around 8-2017 after update from around stretch release
+    # on debian testing, even though the bug is much older.
+    s systemd-tty-ask-password-agent
+}
+
+vpnoff() {
+    [[ $1 ]] || { echo need arg; return 1; }
+    if [[ -e /lib/systemd/system/openvpn-client@.service ]]; then
+        local vpn_service=openvpn-client
+    else
+        local vpn_service=openvpn
+    fi
+    s systemctl stop $vpn_service@$1
+}
+
+
+vrm() {
+    virsh destroy $1
+    virsh undefine $1
+}
+
+
+
+vspicy() { # usage: VIRSH_DOMAIN
+    # connect to vms made with virt-install
+    spicy -p $(sudo virsh dumpxml "$1"|grep "<graphics.*type='spice'"|\
+                   sed -r "s/.*port='([0-9]+).*/\1/")
+}
+
+
+wtr() { curl wttr.in/boston; }
+
+xl() {
+    if pgrep gnome-screensav &>/dev/null; then
+    # this command actually starts gnome-screensaver if it isn't running.
+    # lololol, what crap
+    gnome-screensaver-command --exit &>/dev/null
+    fi
+    mate-screensaver-command --exit &>/dev/null
+    if ! pidof xscreensaver; then
+        pushd /
+        xscreensaver &
+        popd
+        # 1 was not long enough
+        sleep 3
+    fi
+    xscreensaver-command -activate
+}
+
+#############################
+######### misc stuff ########
+#############################
+
+# from curl cheat.sh/:bash_completion
+_cheatsh_complete_curl()
+{
+    local cur prev opts
+    _get_comp_words_by_ref -n : cur
+
+    COMPREPLY=()
+    #cur="${COMP_WORDS[COMP_CWORD]}"
+    prev="${COMP_WORDS[COMP_CWORD-1]}"
+    opts="$(curl -s cheat.sh/:list | sed s@^@cheat.sh/@)"
+
+    if [[ ${cur} == cheat.sh/* ]] ; then
+       COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
+       __ltrim_colon_completions "$cur"
+        return 0
+    fi
+}
+complete -F _cheatsh_complete_curl curl
+
+
+if [[ $- == *i* ]]; then
+    # commands to run when bash exits normally
+    trap "hl" EXIT
+fi
+
+
+# temporary variables to test colorization
+# some copied from gentoo /etc/bash/bashrc,
+use_color=false
+# dircolors --print-database uses its own built-in database
+# instead of using /etc/DIR_COLORS.  Try to use the external file
+# first to take advantage of user additions.
+safe_term=${TERM//[^[:alnum:]]/?}   # sanitize TERM
+match_lhs=""
+[[ -f ~/.dir_colors   ]] && match_lhs="${match_lhs}$(<~/.dir_colors)"
+[[ -f /etc/DIR_COLORS ]] && match_lhs="${match_lhs}$(</etc/DIR_COLORS)"
+[[ -z ${match_lhs}    ]] \
+    && type -P dircolors >/dev/null \
+    && match_lhs=$(dircolors --print-database)
+# test if our $TERM is in the TERM values in dircolor
+[[ $'\n'${match_lhs} == *$'\n'"TERM "${safe_term}* ]] && use_color=true
+
+
+if ${use_color} && [[ $- == *i* ]]; then
+
+    if [[ $XTERM_VERSION == Cygwin* ]]; then
+        get_term_color() {
+            for x in "$@"; do
+                case $x in
+                   underl) echo -n $'\E[4m' ;;
+                   bold) echo -n $'\E[1m' ;;
+                   red) echo -n $'\E[31m' ;;
+                   green) echo -n $'\E[32m' ;;
+                   blue) echo -n $'\E[34m' ;;
+                   cyan) echo -n $'\E[36m' ;;
+                   yellow) echo -n $'\E[33m' ;;
+                   purple) echo -n $'\E[35m' ;;
+                   nocolor) echo -n $'\E(B\E[m' ;;
+                esac
+            done
+        }
+
+    else
+        get_term_color() {
+            for x in "$@"; do
+                case $x in
+                   underl) echo -n $(tput smul) ;;
+                   bold) echo -n $(tput bold) ;;
+                   red) echo -n $(tput setaf 1) ;;
+                   green) echo -n $(tput setaf 2) ;;
+                   blue) echo -n $(tput setaf 4) ;;
+                   cyan) echo -n $(tput setaf 6) ;;
+                   yellow) echo -n $(tput setaf 3) ;;
+                   purple) echo -n $(tput setaf 5) ;;
+                   nocolor) echo -n $(tput sgr0) ;; # no font attributes
+                esac
+            done
+        }
+    fi
+else
+    get_term_color() {
+        :
+    }
+fi
+# Try to keep environment pollution down, EPA loves us.
+unset safe_term match_lhs use_color
+
+
+
+
+
+
+###############
+# prompt ######
+###############
+
+
+if [[ $- == *i* ]]; then
+    # git branch/status prompt function
+    if [[ $OS != Windows_NT ]]; then
+        GIT_PS1_SHOWDIRTYSTATE=true
+    fi
+    # arch source lopip show -fcation
+    [[ -r /usr/share/git/git-prompt.sh ]] && source /usr/share/git/git-prompt.sh
+    # fedora/debian source
+    [[ -r /usr/share/git-core/contrib/completion/git-prompt.sh ]] && source /usr/share/git-core/contrib/completion/git-prompt.sh
+
+    # in case we didn't source git-prompt.sh
+    if ! declare -f __git_ps1 > /dev/null; then
+        __git_ps1() {
+            :
+        }
+    fi
+
+    # this needs to come before next ps1 stuff
+    # this stuff needs bash 4, feb 2009,
+    # old enough to no longer condition on $BASH_VERSION anymore
+    shopt -s autocd
+    shopt -s dirspell
+    PS1='\w'
+    if [[ $- == *i* ]]  && [[ ! $RLC_INSIDE_EMACS ]]; then
+        PROMPT_DIRTRIM=2
+       bind -m vi-command B:shell-backward-word
+       bind -m vi-command W:shell-forward-word
+    fi
+
+    if [[ $SSH_CLIENT ]]; then
+        PS1="\h $PS1"
+    fi
+
+
+
+
+    prompt-command() {
+        local return=$? # this MUST COME FIRST
+        local psc pst ps_char ps_color stale_subvol
+        unset IFS
+        history -a # save history
+
+
+
+        case $return in
+           0) ps_color="$(get_term_color blue)"
+               ps_char='\$'
+               ;;
+           1) ps_color="$(get_term_color green)"
+               ps_char="$return \\$"
+               ;;
+           *) ps_color="$(get_term_color yellow)"
+               ps_char="$return \\$"
+               ;;
+        esac
+        if [[ ! -O . ]]; then # not owner
+            if [[ -w . ]]; then # writable
+               ps_color="$(get_term_color bold red)"
+            else
+               ps_color="$(get_term_color bold green)"
+            fi
+        fi
+        # I would set nullglob, but bash has had bugs where that
+        # doesn't work if not in top level.
+        if [[ -e /nocow/btrfs-stale ]] && ((`ls -AUq /nocow/btrfs-stale|wc -l`)); then
+            ps_char="! $ps_char"
+        fi
+        PS1="${PS1%"${PS1#*[wW]}"} \[$ps_color\]$ps_char\[$(get_term_color nocolor)\] "
+        # emacs completion doesn't like the git prompt atm, so disabling it.
+        #PS1="${PS1%"${PS1#*[wW]}"}$(__git_ps1 ' (%s)') \[$ps_color\]$ps_char\[$(get_term_color nocolor)\] "
+    }
+    PROMPT_COMMAND=prompt-command
+
+    settitle () {
+        if [[ $TERM == screen* ]]; then
+            local title_escape="\033]..2;"
+        else
+            local title_escape="\033]0;"
+        fi
+        if [[ $* != prompt-command ]]; then
+           echo -ne "$title_escape$USER@$HOSTNAME ${PWD/#$HOME/~} $*\007"
+        fi
+    }
+
+    # for titlebar
+    # condition from the screen man page i think
+    if [[ $TERM == *(screen*|xterm*|rxvt*) ]]; then
+        trap 'settitle "$BASH_COMMAND"' DEBUG
+    else
+        trap DEBUG
+    fi
+
+fi
+
+reset-konsole() {
+    # we also have a file in /a/c/...konsole...
+    local f=$HOME/.config/konsolerc
+    setini DefaultProfile profileian.profile "Desktop Entry" $f
+    setini Favorites profileian.profile "Favorite Profiles" $f
+    setini ShowMenuBarByDefault false KonsoleWindow $f
+    setini TabBarPosition Top TabBar $f
+}
+
+reset-sakura() {
+    while read k v; do
+        setini $k $v sakura /a/c/subdir_files/.config/sakura/sakura.conf
+    done <<'EOF'
+colorset1_back rgb(33,37,39
+less_questions true
+audible_bell No
+visible_bell No
+disable_numbered_tabswitch true
+scroll_lines 10000000
+scrollbar true
+EOF
+}
+
+reset-xscreensaver() {
+    # except for spash, i set these by setting gui options in
+    # xscreensaver-command -demo
+    # then finding the corresponding option in .xscreensaver
+    # spash, i happened to notice in .xscreensaver
+    cat > /home/iank/.xscreensaver <<'EOF'
+mode:          blank
+dpmsEnabled:   True
+dpmsStandby:   0:01:00
+dpmsSuspend:   0:01:00
+dpmsOff:       0:02:00
+timeout:       0:01:00
+lock:           True
+lockTimeout:   0:02:00
+splash:                False
+EOF
+
+}
+
+
+###########################################
+# stuff that makes sense to be at the end #
+###########################################
+if [[ "$SUDOD" ]]; then
+    cd "$SUDOD"
+    unset SUDOD
+elif [[ -d /a ]] && [[ $PWD == $HOME ]] && [[ $- == *i* ]]; then
+    cd /a
+fi
+
+
+# best practice
+unset IFS
+
+
+# if someone exported $SOE, catch errors
+if [[ $SOE ]]; then
+    errcatch
+fi
+
+# I'd prefer to have system-wide, plus user ruby, due to bug in it
+# https://github.com/rubygems/rubygems/pull/1002
+# further problems: installing multi-user ruby and user ruby,
+# you don't get multi-user ruby when you sudo to root, unless its sudo -i.
+# There a third hybrid form, which passenger error suggested I use,
+# but it didn't actually work.
+
+# in cased I never need this
+# rvm for non-interactive shell: modified from https://rvm.io/rvm/basics
+#if [[ $(type -t rvm) == file && ! $(type -t ruby) ]]; then
+#    source $(rvm 1.9.3 do rvm env --path)
+#fi
+
+# based on warning from rvmsudo
+export rvmsudo_secure_path=1
+
+if [[ -s "/usr/local/rvm/scripts/rvm" ]]; then
+    source "/usr/local/rvm/scripts/rvm"
+elif [[ -s $HOME/.rvm/scripts/rvm ]]; then
+    source $HOME/.rvm/scripts/rvm
+fi
+
+export GOPATH=$HOME/go
+path_add $GOPATH/bin
+
+export ARDUINO_PATH=/a/opt/Arduino/build/linux/work
+
+path_add --end ~/.npm-global
+
+
+# didn't get drush working, if I did, this seems like the
+# only good thing to include for it.
+# Include Drush completion.
+# if [ -f "/home/ian/.drush/drush.complete.sh" ] ; then
+#     source /home/ian/.drush/drush.complete.sh
+# fi
+
+
+# https://wiki.archlinux.org/index.php/Xinitrc#Autostart_X_at_login
+# i added an extra condition as gentoo xorg guide says depending on
+# $DISPLAY is fragile.
+if [[ ! $DISPLAY && $XDG_VTNR == 1 ]] && shopt -q login_shell && isarch; then
+    exec startx
+fi
+
+
+# ensure no bad programs appending to this file will have an affect
+return 0
diff --git a/browser-home.html b/browser-home.html
new file mode 100644 (file)
index 0000000..4a5f64d
--- /dev/null
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <title>HOME</title>
+  </head>
+  <body style="background-color:#222">
+  </body>
+</html>
diff --git a/filesystem/etc/X11/Xsession.d/98iank b/filesystem/etc/X11/Xsession.d/98iank
new file mode 100644 (file)
index 0000000..fa21023
--- /dev/null
@@ -0,0 +1,10 @@
+# no screensaver for htpc machine
+if [[ $HOSTNAME != frodo ]]; then
+/usr/share/xscreensaver/xscreensaver-wrapper.sh &
+fi
+
+/a/exe/lnf -T /p/arbtt-capture.log ~/.arbtt/capture.log
+
+# docs say a sample rate less than 20 is probably not worth the data cost,
+# but it seems pretty small to me
+arbtt-capture --sample-rate=10 &
diff --git a/filesystem/etc/apt/apt.conf.d/02proxy b/filesystem/etc/apt/apt.conf.d/02proxy
new file mode 100644 (file)
index 0000000..4b2e39e
--- /dev/null
@@ -0,0 +1,5 @@
+# after discovering apt would fail if the proxy was down.
+# This overrides the hardcoded proxy setting done by fai.
+# Figured I'd leave it as post install so I could adjust the
+# detect script easier.
+Acquire::http::Proxy-Auto-Detect "/etc/apt/detect-http-proxy";
diff --git a/filesystem/etc/apt/detect-http-proxy b/filesystem/etc/apt/detect-http-proxy
new file mode 100755 (executable)
index 0000000..4bb8f3c
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/bash
+# based on
+# http://askubuntu.com/questions/53443/how-do-i-ignore-a-proxy-if-not-available
+# altered because testing a down proxy takes like 2 seconds,
+# and we would do it like 100 times in a row. so cache a failed result
+# locally for 10 minutes. Also, using newer option based on man apt.conf.
+#
+# Once when I was testing, it seemed I needed to have it output
+# DIRECT after toutputing the proxy url
+proxy_host=faiserver
+proxy_port=3142
+proxy_url=http://$proxy_host:$proxy_port/
+f=/tmp/apt_proxy_fail
+if [[ ! -w /tmp ]]; then
+    echo DIRECT
+    exit 0
+fi
+modtime=$(stat -c %Y $f 2>/dev/null ) ||:
+if [[ $modtime ]] && (( $(( $(date +%s) - modtime  )) < 60*10 )); then
+    echo DIRECT
+    exit 0
+else
+    rm -f $f
+fi
+if getent hosts $proxy_host &>/dev/null && timeout 1 nc -z $proxy_host $proxy_port &>/dev/null; then
+    echo $proxy_url
+else
+    if [[ ! -e $f || -w $f ]]; then
+        touch $f
+    fi
+fi
diff --git a/filesystem/etc/apt/preferences.d/radicale b/filesystem/etc/apt/preferences.d/radicale
new file mode 100644 (file)
index 0000000..073b78a
--- /dev/null
@@ -0,0 +1,3 @@
+Package: firefox
+Pin: release a=unstable
+Pin-Priority: 500
diff --git a/filesystem/etc/client.conf b/filesystem/etc/client.conf
new file mode 100644 (file)
index 0000000..d78e09b
--- /dev/null
@@ -0,0 +1,2 @@
+# note, this needs to be disabled to print elsewhere.
+ServerName printserver0.office.fsf.org
diff --git a/filesystem/etc/cron.d/ian b/filesystem/etc/cron.d/ian
new file mode 100755 (executable)
index 0000000..8327a14
--- /dev/null
@@ -0,0 +1,2 @@
+SHELL=/bin/bash
+*/10 * * * *   ian /a/exe/rootsshsync 2>&1 | /usr/local/bin/log-once -15
diff --git a/filesystem/etc/cups/client.conf b/filesystem/etc/cups/client.conf
new file mode 100644 (file)
index 0000000..b0f35e1
--- /dev/null
@@ -0,0 +1 @@
+ServerName printserver0.office.fsf.org
diff --git a/filesystem/etc/dnsmasq.d/iank.conf b/filesystem/etc/dnsmasq.d/iank.conf
new file mode 100644 (file)
index 0000000..520b8b2
--- /dev/null
@@ -0,0 +1 @@
+servers-file=/etc/dnsmasq-servers.conf
diff --git a/filesystem/etc/logrotate.d/mylogs b/filesystem/etc/logrotate.d/mylogs
new file mode 100644 (file)
index 0000000..f1ad4fe
--- /dev/null
@@ -0,0 +1,9 @@
+/var/log/debian-auto-update.log {
+    size 10M
+    rotate 10
+    compress
+    missingok
+    notifempty
+    sharedscripts
+    delaycompress
+}
diff --git a/filesystem/etc/modprobe.d/blacklist.conf b/filesystem/etc/modprobe.d/blacklist.conf
new file mode 100644 (file)
index 0000000..5318126
--- /dev/null
@@ -0,0 +1,2 @@
+blacklist pcspkr
+blacklist snd_pcsp
diff --git a/filesystem/etc/profile.d/environment.sh b/filesystem/etc/profile.d/environment.sh
new file mode 100644 (file)
index 0000000..95dce05
--- /dev/null
@@ -0,0 +1,71 @@
+if [ -f $HOME/path_add-function ]; then
+    . $HOME/path_add-function
+    path_add /usr/sbin /usr/local/sbin /sbin
+    path_add /a/exe /a/opt/bin $HOME/.cabal/bin
+
+    if [ -r /etc/alternatives/java_sdk ]; then
+        export JAVA_HOME=/etc/alternatives/java_sdk
+        path_add /etc/alternatives/java_sdk
+    fi
+fi
+
+export EDITOR="emacsclient"
+# this makes emacsclient file/-c start a server instance if none is running,
+# instead of some alternate editor logic
+export ALTERNATE_EDITOR=""
+
+
+# makes subsequent syscalls to localtime use cached timezone,
+# so basically restart the comp if you change time zones,
+# and avoid a few syscalls, which makes a tiny tiny perf difference.
+# I also set this in
+# /a/c/filesystem/etc/systemd/system.conf.d/tz.conf
+# https://blog.packagecloud.io/eng/2017/02/21/set-environment-variable-save-thousands-of-system-calls/
+export TZ=:/etc/localtime
+
+# ubuntu starts gpg agent automatically with  /etc/X11/Xsession.d/90gpg-agent.
+# fedora doesn't, which left me to figure this out, and google was no help.
+# fedora documentation is often quite bad :(
+# This is mostly copied from that file.
+# Main difference is that we eval the result of starting gpg-agent,
+# while that file executes it through xsession specific var.
+# Also make sourcing the pidfile make more sense.
+# End result should be the same afaik.
+# for gpg-agent to work when calling gpg from the command line,
+# we need an environment variable that is setup via the eval.
+# which is why we do this upon login, so it can propogate
+# It is also written to the file $HOME/.gnupg/gpg-agent-info-$(hostname)
+# I'm not aware if that is ever used, but just fyi.
+# I also added the bit about xmessaging the stderr,
+# because I'd like to know if the command fails
+if [ -f /etc/fedora-release ]; then
+    : ${GNUPGHOME=$HOME/.gnupg}
+
+    GPGAGENT=/usr/bin/gpg-agent
+    PID_FILE="$GNUPGHOME/gpg-agent-info-$(hostname)"
+
+    if ! $GPGAGENT 2>/dev/null; then
+        temp="$(mktemp)"
+        eval "$($GPGAGENT --homedir /p/do-not-delete --daemon --sh --write-env-file=$PID_FILE 2>$temp)"
+        temperr="$(<"$temp")"
+        [ -n "$temperr" ] && xmessage "gpg-agent stderr: $temperr"
+    elif [ -r "$PID_FILE" ]; then
+        . "$PID_FILE"
+        export GPG_AGENT_INFO
+    fi
+fi
+
+# ubuntu has 002 for non-system users, debian has 022.  002 makes groups
+# be rw instead of r. One security concern is where some unixes put
+# every user in a same group, so if you copy files there with exact
+# perms, that is probably not what you want. I don't use a system like
+# that. I don't care much either way, but the ubuntu one seems a bit
+# more user friendly, and copying files between them is a annoying with
+# different umasks. However, it also makes it so if you create a file as
+# a regular user then move it to become a system file, it's got slightly
+# wrong permissions, and sometimes thing break. So, this outweighs the
+# other things, stick with 022 umask always.
+umask 022
+# this is how we could test for non-system user
+
+#if test "$(id -u)" -ge 1000; then : fi
diff --git a/filesystem/etc/synergy.conf b/filesystem/etc/synergy.conf
new file mode 100644 (file)
index 0000000..8abb8de
--- /dev/null
@@ -0,0 +1,16 @@
+
+section: screens
+  treetowl:
+  frodo:
+  tp:
+end
+
+section: links
+  tp:
+    right = frodo
+  treetowl:
+    left = frodo
+  frodo:
+    left = tp
+    right = treetowl
+end
diff --git a/filesystem/etc/systemd/system.conf.d/tz.conf b/filesystem/etc/systemd/system.conf.d/tz.conf
new file mode 100644 (file)
index 0000000..dcfba45
--- /dev/null
@@ -0,0 +1,3 @@
+[Manager]
+DefaultEnvironment=TZ=:/etc/localtime
+# also set in profile.d/environment.sh with explanation there
diff --git a/filesystem/etc/systemd/system/btrfsmaint.service b/filesystem/etc/systemd/system/btrfsmaint.service
new file mode 100644 (file)
index 0000000..a751467
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=btrfsmaint
+After=multi-user.target
+
+[Service]
+Type=oneshot
+# about 24 hours of failures
+# it copies over its files without respecting symlinks, so
+# we pass options to use different location.
+ExecStart=/a/bin/log-quiet/sysd-mail-once -1 btrfsmaint /a/b/ds/btrfsmaint
diff --git a/filesystem/etc/systemd/system/btrfsmaint.timer b/filesystem/etc/systemd/system/btrfsmaint.timer
new file mode 100644 (file)
index 0000000..f07fffb
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=btrfsmaint
+
+[Timer]
+OnCalendar=Tue *-*-* 04:00:00
+
+[Install]
+WantedBy=timers.target
diff --git a/filesystem/etc/systemd/system/btrfsmaintstop.service b/filesystem/etc/systemd/system/btrfsmaintstop.service
new file mode 100644 (file)
index 0000000..e564e1c
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=btrfsmaintstop
+After=multi-user.target
+
+[Service]
+Type=oneshot
+ExecStart=/a/bin/log-quiet/sysd-mail-once -10 btrfsmaintstop /a/bin/ds/btrfsmaint check
diff --git a/filesystem/etc/systemd/system/btrfsmaintstop.timer b/filesystem/etc/systemd/system/btrfsmaintstop.timer
new file mode 100644 (file)
index 0000000..eebfc94
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=btrfsmaintstop
+
+[Timer]
+# for initial run. required.
+OnActiveSec=10
+# for subsequent runs.
+OnUnitInactiveSec=20
+
+[Install]
+WantedBy=timers.target
diff --git a/filesystem/etc/systemd/system/iptables.service b/filesystem/etc/systemd/system/iptables.service
new file mode 100644 (file)
index 0000000..2edf6c0
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=Packet Filtering Framework
+Before=network-pre.target
+Wants=network-pre.target
+
+[Service]
+Type=oneshot
+ExecStart=/sbin/iptables-restore /a/bin/distro-setup/transmission-firewall/default.rules
+ExecStop=/sbin/iptables-restore /a/bin/distro-setup/transmission-firewall/empty.rules
+RemainAfterExit=yes
+
+[Install]
+WantedBy=multi-user.target
diff --git a/filesystem/etc/systemd/system/openvpn-client-nn@.service b/filesystem/etc/systemd/system/openvpn-client-nn@.service
new file mode 100644 (file)
index 0000000..ab5ccba
--- /dev/null
@@ -0,0 +1,34 @@
+[Unit]
+Description=OpenVPN tunnel for %I
+After=syslog.target network-online.target
+Wants=network-online.target
+Documentation=man:openvpn(8)
+Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
+Documentation=https://community.openvpn.net/openvpn/wiki/HOWTO
+Requires=iptables.service
+
+[Service]
+Type=notify
+RuntimeDirectory=openvpn-client
+RuntimeDirectoryMode=0710
+WorkingDirectory=/etc/openvpn/client
+ExecStart=/usr/bin/nsenter --mount=/root/mount_namespaces/%i /usr/sbin/openvpn --suppress-timestamps --nobind --config /etc/openvpn/client/%i.conf
+# Ian: commented out these lines from upstream
+# until we get the next systemd version (233), which can do bind mounts
+# inside a mnt namespace via systemd, we have to setup our own, which requires
+# full priveledges.
+# when we enable CababilityBoundingSet, s/=/=+/ on these ExecStart/Stop lines.
+# systemd ver in flidas does not suppot =+
+#CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE
+LimitNPROC=10
+# DeviceAllow=/dev/null rw
+# DeviceAllow=/dev/net/tun rw
+
+ExecStartPre=/a/bin/newns/newns -n 10.173.0 start %i
+ExecStartPre=/sbin/iptables-restore /a/bin/distro-setup/transmission-firewall/netns.rules
+ExecStopPost=/a/bin/newns/newns stop %i
+PrivateNetwork=true
+
+
+[Install]
+WantedBy=multi-user.target
diff --git a/filesystem/etc/systemd/system/openvpn-nn@.service b/filesystem/etc/systemd/system/openvpn-nn@.service
new file mode 100644 (file)
index 0000000..c814a3f
--- /dev/null
@@ -0,0 +1,48 @@
+[Unit]
+
+
+Description=OpenVPN connection to %i
+PartOf=openvpn.service
+ReloadPropagatedFrom=openvpn.service
+
+# ian, this is starting too early, added this from stretch's openvpn-client@.service
+After=syslog.target network-online.target
+Wants=network-online.target
+# ian, commented out, we are starting too early, no need to be before anything.
+Before=systemd-user-sessions.service
+
+Documentation=man:openvpn(8)
+Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn23ManPage
+Documentation=https://community.openvpn.net/openvpn/wiki/HOWTO
+
+[Service]
+# these cause ExecStart to fail with "nsenter: reassociate to namespace
+# 'ns/mnt' failed: Invalid argument" on trisquel 8.  Probably because we
+# get put into some mount namespace and the one that I setup does not
+# exist within it or something stupid that systemd does not
+# document. Whatev.
+#PrivateTmp=true
+#ProtectSystem=yes
+
+KillMode=mixed
+Type=forking
+ExecStart=/usr/bin/nsenter --mount=/root/mount_namespaces/%i /usr/sbin/openvpn --daemon ovpn-%i --status /run/openvpn/%i.status 10 --cd /etc/openvpn --script-security 2 --config /etc/openvpn/client/%i.conf --writepid /run/openvpn/%i.pid
+PIDFile=/run/openvpn/%i.pid
+ExecReload=/bin/kill -HUP $MAINPID
+WorkingDirectory=/etc/openvpn
+# Ian: commented out these lines from upstream
+#CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_READ_SEARCH CAP_AUDIT_WRITE
+LimitNPROC=10
+#DeviceAllow=/dev/null rw
+#DeviceAllow=/dev/net/tun rw
+
+# ian: added just these  lines from upstream
+ExecStartPre=/a/bin/newns/newns -n 10.173.0 start %i
+ExecStartPre=/sbin/iptables-restore /a/bin/distro-setup/transmission-firewall/netns.rules
+# note, this doesnt get run on trisquel 8 when ExecStart command fails,
+# documentation says it should
+ExecStopPost=/a/bin/newns/newns stop %i
+PrivateNetwork=true
+
+[Install]
+WantedBy=multi-user.target
diff --git a/filesystem/etc/udev/rules.d/99-kaleidoscope.rules b/filesystem/etc/udev/rules.d/99-kaleidoscope.rules
new file mode 100644 (file)
index 0000000..9a67e74
--- /dev/null
@@ -0,0 +1,3 @@
+# https://github.com/keyboardio/Kaleidoscope/wiki/Install-Arduino-support-on-Linux
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2300", SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}="1"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2301", SYMLINK+="model01", ENV{ID_MM_DEVICE_IGNORE}="1"
diff --git a/filesystem/etc/updatedb.findutils.cron.local b/filesystem/etc/updatedb.findutils.cron.local
new file mode 100644 (file)
index 0000000..176ab1e
--- /dev/null
@@ -0,0 +1,4 @@
+# noticed find slowing down my system,
+# pstree lead back to /etc/cron.daily/locate, which
+# says to consult /usr/share/doc/locate/README.Debian
+PRUNEPATHS="$PRUNEPATHS /mnt"
diff --git a/filesystem/usr/share/gitweb/gitweb.cgi b/filesystem/usr/share/gitweb/gitweb.cgi
new file mode 100755 (executable)
index 0000000..ea4250f
--- /dev/null
@@ -0,0 +1,8418 @@
+#!/usr/bin/perl
+
+# gitweb - simple web interface to track changes in git repositories
+#
+# (C) 2005-2006, Kay Sievers <kay.sievers@vrfy.org>
+# (C) 2005, Christian Gierke
+#
+# This program is licensed under the GPLv2
+
+use 5.008;
+use strict;
+use warnings;
+use CGI qw(:standard :escapeHTML -nosticky);
+use CGI::Util qw(unescape);
+use CGI::Carp qw(fatalsToBrowser set_message);
+use Encode;
+use Fcntl ':mode';
+use File::Find qw();
+use File::Basename qw(basename);
+use Time::HiRes qw(gettimeofday tv_interval);
+binmode STDOUT, ':utf8';
+
+if (!defined($CGI::VERSION) || $CGI::VERSION < 4.08) {
+       eval 'sub CGI::multi_param { CGI::param(@_) }'
+}
+
+our $t0 = [ gettimeofday() ];
+our $number_of_git_cmds = 0;
+
+BEGIN {
+       CGI->compile() if $ENV{'MOD_PERL'};
+}
+
+our $version = "2.8.1";
+
+our ($my_url, $my_uri, $base_url, $path_info, $home_link);
+sub evaluate_uri {
+       our $cgi;
+
+       our $my_url = $cgi->url();
+       our $my_uri = $cgi->url(-absolute => 1);
+
+       # Base URL for relative URLs in gitweb ($logo, $favicon, ...),
+       # needed and used only for URLs with nonempty PATH_INFO
+       our $base_url = $my_url;
+
+       # When the script is used as DirectoryIndex, the URL does not contain the name
+       # of the script file itself, and $cgi->url() fails to strip PATH_INFO, so we
+       # have to do it ourselves. We make $path_info global because it's also used
+       # later on.
+       #
+       # Another issue with the script being the DirectoryIndex is that the resulting
+       # $my_url data is not the full script URL: this is good, because we want
+       # generated links to keep implying the script name if it wasn't explicitly
+       # indicated in the URL we're handling, but it means that $my_url cannot be used
+       # as base URL.
+       # Therefore, if we needed to strip PATH_INFO, then we know that we have
+       # to build the base URL ourselves:
+       our $path_info = decode_utf8($ENV{"PATH_INFO"});
+       if ($path_info) {
+               # $path_info has already been URL-decoded by the web server, but
+               # $my_url and $my_uri have not. URL-decode them so we can properly
+               # strip $path_info.
+               $my_url = unescape($my_url);
+               $my_uri = unescape($my_uri);
+               if ($my_url =~ s,\Q$path_info\E$,, &&
+                   $my_uri =~ s,\Q$path_info\E$,, &&
+                   defined $ENV{'SCRIPT_NAME'}) {
+                       $base_url = $cgi->url(-base => 1) . $ENV{'SCRIPT_NAME'};
+               }
+       }
+
+       # target of the home link on top of all pages
+       our $home_link = $my_uri || "/";
+}
+
+# core git executable to use
+# this can just be "git" if your webserver has a sensible PATH
+our $GIT = "/usr/bin/git";
+
+# absolute fs-path which will be prepended to the project path
+#our $projectroot = "/pub/scm";
+our $projectroot = "/pub/git";
+
+# fs traversing limit for getting project list
+# the number is relative to the projectroot
+our $project_maxdepth = 2007;
+
+# string of the home link on top of all pages
+our $home_link_str = "projects";
+
+# extra breadcrumbs preceding the home link
+our @extra_breadcrumbs = ();
+
+# name of your site or organization to appear in page titles
+# replace this with something more descriptive for clearer bookmarks
+our $site_name = ""
+                 || ($ENV{'SERVER_NAME'} || "Untitled") . " Git";
+
+# html snippet to include in the <head> section of each page
+our $site_html_head_string = "";
+# filename of html text to include at top of each page
+our $site_header = "";
+# html text to include at home page
+our $home_text = "indextext.html";
+# filename of html text to include at bottom of each page
+our $site_footer = "";
+
+# URI of stylesheets
+our @stylesheets = ("static/gitweb.css");
+# URI of a single stylesheet, which can be overridden in GITWEB_CONFIG.
+our $stylesheet = undef;
+# URI of GIT logo (72x27 size)
+our $logo = "static/git-logo.png";
+# URI of GIT favicon, assumed to be image/png type
+our $favicon = "static/git-favicon.png";
+# URI of gitweb.js (JavaScript code for gitweb)
+our $javascript = "static/gitweb.js";
+
+# URI and label (title) of GIT logo link
+#our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
+#our $logo_label = "git documentation";
+our $logo_url = "http://git-scm.com/";
+our $logo_label = "git homepage";
+
+# source of projects list
+our $projects_list = "";
+
+# the width (in characters) of the projects list "Description" column
+our $projects_list_description_width = 25;
+
+# group projects by category on the projects list
+# (enabled if this variable evaluates to true)
+our $projects_list_group_categories = 0;
+
+# default category if none specified
+# (leave the empty string for no category)
+our $project_list_default_category = "";
+
+# default order of projects list
+# valid values are none, project, descr, owner, and age
+our $default_projects_order = "project";
+
+# show repository only if this file exists
+# (only effective if this variable evaluates to true)
+our $export_ok = "";
+
+# don't generate age column on the projects list page
+our $omit_age_column = 0;
+
+# don't generate information about owners of repositories
+our $omit_owner=0;
+
+# show repository only if this subroutine returns true
+# when given the path to the project, for example:
+#    sub { return -e "$_[0]/git-daemon-export-ok"; }
+our $export_auth_hook = undef;
+
+# only allow viewing of repositories also shown on the overview page
+our $strict_export = "";
+
+# list of git base URLs used for URL to where fetch project from,
+# i.e. full URL is "$git_base_url/$project"
+our @git_base_url_list = grep { $_ ne '' } ("");
+
+# default blob_plain mimetype and default charset for text/plain blob
+our $default_blob_plain_mimetype = 'text/plain';
+our $default_text_plain_charset  = undef;
+
+# file to use for guessing MIME types before trying /etc/mime.types
+# (relative to the current git repository)
+our $mimetypes_file = undef;
+
+# assume this charset if line contains non-UTF-8 characters;
+# it should be valid encoding (see Encoding::Supported(3pm) for list),
+# for which encoding all byte sequences are valid, for example
+# 'iso-8859-1' aka 'latin1' (it is decoded without checking, so it
+# could be even 'utf-8' for the old behavior)
+our $fallback_encoding = 'latin1';
+
+# rename detection options for git-diff and git-diff-tree
+# - default is '-M', with the cost proportional to
+#   (number of removed files) * (number of new files).
+# - more costly is '-C' (which implies '-M'), with the cost proportional to
+#   (number of changed files + number of removed files) * (number of new files)
+# - even more costly is '-C', '--find-copies-harder' with cost
+#   (number of files in the original tree) * (number of new files)
+# - one might want to include '-B' option, e.g. '-B', '-M'
+our @diff_opts = ('-M'); # taken from git_commit
+
+# Disables features that would allow repository owners to inject script into
+# the gitweb domain.
+our $prevent_xss = 0;
+
+# Path to the highlight executable to use (must be the one from
+# http://www.andre-simon.de due to assumptions about parameters and output).
+# Useful if highlight is not installed on your webserver's PATH.
+# [Default: highlight]
+our $highlight_bin = "highlight";
+
+# information about snapshot formats that gitweb is capable of serving
+our %known_snapshot_formats = (
+       # name => {
+       #       'display' => display name,
+       #       'type' => mime type,
+       #       'suffix' => filename suffix,
+       #       'format' => --format for git-archive,
+       #       'compressor' => [compressor command and arguments]
+       #                       (array reference, optional)
+       #       'disabled' => boolean (optional)}
+       #
+       'tgz' => {
+               'display' => 'tar.gz',
+               'type' => 'application/x-gzip',
+               'suffix' => '.tar.gz',
+               'format' => 'tar',
+               'compressor' => ['gzip', '-n']},
+
+       'tbz2' => {
+               'display' => 'tar.bz2',
+               'type' => 'application/x-bzip2',
+               'suffix' => '.tar.bz2',
+               'format' => 'tar',
+               'compressor' => ['bzip2']},
+
+       'txz' => {
+               'display' => 'tar.xz',
+               'type' => 'application/x-xz',
+               'suffix' => '.tar.xz',
+               'format' => 'tar',
+               'compressor' => ['xz'],
+               'disabled' => 1},
+
+       'zip' => {
+               'display' => 'zip',
+               'type' => 'application/x-zip',
+               'suffix' => '.zip',
+               'format' => 'zip'},
+);
+
+# Aliases so we understand old gitweb.snapshot values in repository
+# configuration.
+our %known_snapshot_format_aliases = (
+       'gzip'  => 'tgz',
+       'bzip2' => 'tbz2',
+       'xz'    => 'txz',
+
+       # backward compatibility: legacy gitweb config support
+       'x-gzip' => undef, 'gz' => undef,
+       'x-bzip2' => undef, 'bz2' => undef,
+       'x-zip' => undef, '' => undef,
+);
+
+# Pixel sizes for icons and avatars. If the default font sizes or lineheights
+# are changed, it may be appropriate to change these values too via
+# $GITWEB_CONFIG.
+our %avatar_size = (
+       'default' => 16,
+       'double'  => 32
+);
+
+# Used to set the maximum load that we will still respond to gitweb queries.
+# If server load exceed this value then return "503 server busy" error.
+# If gitweb cannot determined server load, it is taken to be 0.
+# Leave it undefined (or set to 'undef') to turn off load checking.
+our $maxload = 300;
+
+# configuration for 'highlight' (http://www.andre-simon.de/)
+# match by basename
+our %highlight_basename = (
+       #'Program' => 'py',
+       #'Library' => 'py',
+       'SConstruct' => 'py', # SCons equivalent of Makefile
+       'Makefile' => 'make',
+);
+# match by extension
+our %highlight_ext = (
+       # main extensions, defining name of syntax;
+       # see files in /usr/share/highlight/langDefs/ directory
+       (map { $_ => $_ } qw(py rb java css js tex bib xml awk bat ini spec tcl sql)),
+       # alternate extensions, see /etc/highlight/filetypes.conf
+       (map { $_ => 'c'   } qw(c h)),
+       (map { $_ => 'sh'  } qw(sh bash zsh ksh)),
+       (map { $_ => 'cpp' } qw(cpp cxx c++ cc)),
+       (map { $_ => 'php' } qw(php php3 php4 php5 phps)),
+       (map { $_ => 'pl'  } qw(pl perl pm)), # perhaps also 'cgi'
+       (map { $_ => 'make'} qw(make mak mk)),
+       (map { $_ => 'xml' } qw(xml xhtml html htm)),
+);
+
+# You define site-wide feature defaults here; override them with
+# $GITWEB_CONFIG as necessary.
+our %feature = (
+       # feature => {
+       #       'sub' => feature-sub (subroutine),
+       #       'override' => allow-override (boolean),
+       #       'default' => [ default options...] (array reference)}
+       #
+       # if feature is overridable (it means that allow-override has true value),
+       # then feature-sub will be called with default options as parameters;
+       # return value of feature-sub indicates if to enable specified feature
+       #
+       # if there is no 'sub' key (no feature-sub), then feature cannot be
+       # overridden
+       #
+       # use gitweb_get_feature(<feature>) to retrieve the <feature> value
+       # (an array) or gitweb_check_feature(<feature>) to check if <feature>
+       # is enabled
+
+       # Enable the 'blame' blob view, showing the last commit that modified
+       # each line in the file. This can be very CPU-intensive.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'blame'}{'default'} = [1];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'blame'}{'override'} = 1;
+       # and in project config gitweb.blame = 0|1;
+       'blame' => {
+               'sub' => sub { feature_bool('blame', @_) },
+               'override' => 0,
+               'default' => [0]},
+
+       # Enable the 'snapshot' link, providing a compressed archive of any
+       # tree. This can potentially generate high traffic if you have large
+       # project.
+
+       # Value is a list of formats defined in %known_snapshot_formats that
+       # you wish to offer.
+       # To disable system wide have in $GITWEB_CONFIG
+       # $feature{'snapshot'}{'default'} = [];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'snapshot'}{'override'} = 1;
+       # and in project config, a comma-separated list of formats or "none"
+       # to disable.  Example: gitweb.snapshot = tbz2,zip;
+       'snapshot' => {
+               'sub' => \&feature_snapshot,
+               'override' => 0,
+               'default' => ['tgz']},
+
+       # Enable text search, which will list the commits which match author,
+       # committer or commit text to a given string.  Enabled by default.
+       # Project specific override is not supported.
+       #
+       # Note that this controls all search features, which means that if
+       # it is disabled, then 'grep' and 'pickaxe' search would also be
+       # disabled.
+       'search' => {
+               'override' => 0,
+               'default' => [1]},
+
+       # Enable grep search, which will list the files in currently selected
+       # tree containing the given string. Enabled by default. This can be
+       # potentially CPU-intensive, of course.
+       # Note that you need to have 'search' feature enabled too.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'grep'}{'default'} = [1];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'grep'}{'override'} = 1;
+       # and in project config gitweb.grep = 0|1;
+       'grep' => {
+               'sub' => sub { feature_bool('grep', @_) },
+               'override' => 0,
+               'default' => [1]},
+
+       # Enable the pickaxe search, which will list the commits that modified
+       # a given string in a file. This can be practical and quite faster
+       # alternative to 'blame', but still potentially CPU-intensive.
+       # Note that you need to have 'search' feature enabled too.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'pickaxe'}{'default'} = [1];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'pickaxe'}{'override'} = 1;
+       # and in project config gitweb.pickaxe = 0|1;
+       'pickaxe' => {
+               'sub' => sub { feature_bool('pickaxe', @_) },
+               'override' => 0,
+               'default' => [1]},
+
+       # Enable showing size of blobs in a 'tree' view, in a separate
+       # column, similar to what 'ls -l' does.  This cost a bit of IO.
+
+       # To disable system wide have in $GITWEB_CONFIG
+       # $feature{'show-sizes'}{'default'} = [0];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'show-sizes'}{'override'} = 1;
+       # and in project config gitweb.showsizes = 0|1;
+       'show-sizes' => {
+               'sub' => sub { feature_bool('showsizes', @_) },
+               'override' => 0,
+               'default' => [1]},
+
+       # Make gitweb use an alternative format of the URLs which can be
+       # more readable and natural-looking: project name is embedded
+       # directly in the path and the query string contains other
+       # auxiliary information. All gitweb installations recognize
+       # URL in either format; this configures in which formats gitweb
+       # generates links.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'pathinfo'}{'default'} = [1];
+       # Project specific override is not supported.
+
+       # Note that you will need to change the default location of CSS,
+       # favicon, logo and possibly other files to an absolute URL. Also,
+       # if gitweb.cgi serves as your indexfile, you will need to force
+       # $my_uri to contain the script name in your $GITWEB_CONFIG.
+       'pathinfo' => {
+               'override' => 0,
+               'default' => [0]},
+
+       # Make gitweb consider projects in project root subdirectories
+       # to be forks of existing projects. Given project $projname.git,
+       # projects matching $projname/*.git will not be shown in the main
+       # projects list, instead a '+' mark will be added to $projname
+       # there and a 'forks' view will be enabled for the project, listing
+       # all the forks. If project list is taken from a file, forks have
+       # to be listed after the main project.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'forks'}{'default'} = [1];
+       # Project specific override is not supported.
+       'forks' => {
+               'override' => 0,
+               'default' => [0]},
+
+       # Insert custom links to the action bar of all project pages.
+       # This enables you mainly to link to third-party scripts integrating
+       # into gitweb; e.g. git-browser for graphical history representation
+       # or custom web-based repository administration interface.
+
+       # The 'default' value consists of a list of triplets in the form
+       # (label, link, position) where position is the label after which
+       # to insert the link and link is a format string where %n expands
+       # to the project name, %f to the project path within the filesystem,
+       # %h to the current hash (h gitweb parameter) and %b to the current
+       # hash base (hb gitweb parameter); %% expands to %.
+
+       # To enable system wide have in $GITWEB_CONFIG e.g.
+       # $feature{'actions'}{'default'} = [('graphiclog',
+       #       '/git-browser/by-commit.html?r=%n', 'summary')];
+       # Project specific override is not supported.
+       'actions' => {
+               'override' => 0,
+               'default' => []},
+
+       # Allow gitweb scan project content tags of project repository,
+       # and display the popular Web 2.0-ish "tag cloud" near the projects
+       # list.  Note that this is something COMPLETELY different from the
+       # normal Git tags.
+
+       # gitweb by itself can show existing tags, but it does not handle
+       # tagging itself; you need to do it externally, outside gitweb.
+       # The format is described in git_get_project_ctags() subroutine.
+       # You may want to install the HTML::TagCloud Perl module to get
+       # a pretty tag cloud instead of just a list of tags.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'ctags'}{'default'} = [1];
+       # Project specific override is not supported.
+
+       # In the future whether ctags editing is enabled might depend
+       # on the value, but using 1 should always mean no editing of ctags.
+       'ctags' => {
+               'override' => 0,
+               'default' => [0]},
+
+       # The maximum number of patches in a patchset generated in patch
+       # view. Set this to 0 or undef to disable patch view, or to a
+       # negative number to remove any limit.
+
+       # To disable system wide have in $GITWEB_CONFIG
+       # $feature{'patches'}{'default'} = [0];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'patches'}{'override'} = 1;
+       # and in project config gitweb.patches = 0|n;
+       # where n is the maximum number of patches allowed in a patchset.
+       'patches' => {
+               'sub' => \&feature_patches,
+               'override' => 0,
+               'default' => [16]},
+
+       # Avatar support. When this feature is enabled, views such as
+       # shortlog or commit will display an avatar associated with
+       # the email of the committer(s) and/or author(s).
+
+       # Currently available providers are gravatar and picon.
+       # If an unknown provider is specified, the feature is disabled.
+
+       # Gravatar depends on Digest::MD5.
+       # Picon currently relies on the indiana.edu database.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'avatar'}{'default'} = ['<provider>'];
+       # where <provider> is either gravatar or picon.
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'avatar'}{'override'} = 1;
+       # and in project config gitweb.avatar = <provider>;
+       'avatar' => {
+               'sub' => \&feature_avatar,
+               'override' => 0,
+               'default' => ['']},
+
+       # Enable displaying how much time and how many git commands
+       # it took to generate and display page.  Disabled by default.
+       # Project specific override is not supported.
+       'timed' => {
+               'override' => 0,
+               'default' => [0]},
+
+       # Enable turning some links into links to actions which require
+       # JavaScript to run (like 'blame_incremental').  Not enabled by
+       # default.  Project specific override is currently not supported.
+       'javascript-actions' => {
+               'override' => 0,
+               'default' => [0]},
+
+       # Enable and configure ability to change common timezone for dates
+       # in gitweb output via JavaScript.  Enabled by default.
+       # Project specific override is not supported.
+       'javascript-timezone' => {
+               'override' => 0,
+               'default' => [
+                       'local',     # default timezone: 'utc', 'local', or '(-|+)HHMM' format,
+                                    # or undef to turn off this feature
+                       'gitweb_tz', # name of cookie where to store selected timezone
+                       'datetime',  # CSS class used to mark up dates for manipulation
+               ]},
+
+       # Syntax highlighting support. This is based on Daniel Svensson's
+       # and Sham Chukoury's work in gitweb-xmms2.git.
+       # It requires the 'highlight' program present in $PATH,
+       # and therefore is disabled by default.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'highlight'}{'default'} = [1];
+
+       'highlight' => {
+               'sub' => sub { feature_bool('highlight', @_) },
+               'override' => 0,
+               'default' => [0]},
+
+       # Enable displaying of remote heads in the heads list
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'remote_heads'}{'default'} = [1];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'remote_heads'}{'override'} = 1;
+       # and in project config gitweb.remoteheads = 0|1;
+       'remote_heads' => {
+               'sub' => sub { feature_bool('remote_heads', @_) },
+               'override' => 0,
+               'default' => [0]},
+
+       # Enable showing branches under other refs in addition to heads
+
+       # To set system wide extra branch refs have in $GITWEB_CONFIG
+       # $feature{'extra-branch-refs'}{'default'} = ['dirs', 'of', 'choice'];
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'extra-branch-refs'}{'override'} = 1;
+       # and in project config gitweb.extrabranchrefs = dirs of choice
+       # Every directory is separated with whitespace.
+
+       'extra-branch-refs' => {
+               'sub' => \&feature_extra_branch_refs,
+               'override' => 0,
+               'default' => []},
+);
+
+sub gitweb_get_feature {
+       my ($name) = @_;
+       return unless exists $feature{$name};
+       my ($sub, $override, @defaults) = (
+               $feature{$name}{'sub'},
+               $feature{$name}{'override'},
+               @{$feature{$name}{'default'}});
+       # project specific override is possible only if we have project
+       our $git_dir; # global variable, declared later
+       if (!$override || !defined $git_dir) {
+               return @defaults;
+       }
+       if (!defined $sub) {
+               warn "feature $name is not overridable";
+               return @defaults;
+       }
+       return $sub->(@defaults);
+}
+
+# A wrapper to check if a given feature is enabled.
+# With this, you can say
+#
+#   my $bool_feat = gitweb_check_feature('bool_feat');
+#   gitweb_check_feature('bool_feat') or somecode;
+#
+# instead of
+#
+#   my ($bool_feat) = gitweb_get_feature('bool_feat');
+#   (gitweb_get_feature('bool_feat'))[0] or somecode;
+#
+sub gitweb_check_feature {
+       return (gitweb_get_feature(@_))[0];
+}
+
+
+sub feature_bool {
+       my $key = shift;
+       my ($val) = git_get_project_config($key, '--bool');
+
+       if (!defined $val) {
+               return ($_[0]);
+       } elsif ($val eq 'true') {
+               return (1);
+       } elsif ($val eq 'false') {
+               return (0);
+       }
+}
+
+sub feature_snapshot {
+       my (@fmts) = @_;
+
+       my ($val) = git_get_project_config('snapshot');
+
+       if ($val) {
+               @fmts = ($val eq 'none' ? () : split /\s*[,\s]\s*/, $val);
+       }
+
+       return @fmts;
+}
+
+sub feature_patches {
+       my @val = (git_get_project_config('patches', '--int'));
+
+       if (@val) {
+               return @val;
+       }
+
+       return ($_[0]);
+}
+
+sub feature_avatar {
+       my @val = (git_get_project_config('avatar'));
+
+       return @val ? @val : @_;
+}
+
+sub feature_extra_branch_refs {
+       my (@branch_refs) = @_;
+       my $values = git_get_project_config('extrabranchrefs');
+
+       if ($values) {
+               $values = config_to_multi ($values);
+               @branch_refs = ();
+               foreach my $value (@{$values}) {
+                       push @branch_refs, split /\s+/, $value;
+               }
+       }
+
+       return @branch_refs;
+}
+
+# checking HEAD file with -e is fragile if the repository was
+# initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed
+# and then pruned.
+sub check_head_link {
+       my ($dir) = @_;
+       my $headfile = "$dir/HEAD";
+       return ((-e $headfile) ||
+               (-l $headfile && readlink($headfile) =~ /^refs\/heads\//));
+}
+
+sub check_export_ok {
+       my ($dir) = @_;
+       return (check_head_link($dir) &&
+               (!$export_ok || -e "$dir/$export_ok") &&
+               (!$export_auth_hook || $export_auth_hook->($dir)));
+}
+
+# process alternate names for backward compatibility
+# filter out unsupported (unknown) snapshot formats
+sub filter_snapshot_fmts {
+       my @fmts = @_;
+
+       @fmts = map {
+               exists $known_snapshot_format_aliases{$_} ?
+                      $known_snapshot_format_aliases{$_} : $_} @fmts;
+       @fmts = grep {
+               exists $known_snapshot_formats{$_} &&
+               !$known_snapshot_formats{$_}{'disabled'}} @fmts;
+}
+
+sub filter_and_validate_refs {
+       my @refs = @_;
+       my %unique_refs = ();
+
+       foreach my $ref (@refs) {
+               die_error(500, "Invalid ref '$ref' in 'extra-branch-refs' feature") unless (is_valid_ref_format($ref));
+               # 'heads' are added implicitly in get_branch_refs().
+               $unique_refs{$ref} = 1 if ($ref ne 'heads');
+       }
+       return sort keys %unique_refs;
+}
+
+# If it is set to code reference, it is code that it is to be run once per
+# request, allowing updating configurations that change with each request,
+# while running other code in config file only once.
+#
+# Otherwise, if it is false then gitweb would process config file only once;
+# if it is true then gitweb config would be run for each request.
+our $per_request_config = 1;
+
+# read and parse gitweb config file given by its parameter.
+# returns true on success, false on recoverable error, allowing
+# to chain this subroutine, using first file that exists.
+# dies on errors during parsing config file, as it is unrecoverable.
+sub read_config_file {
+       my $filename = shift;
+       return unless defined $filename;
+       # die if there are errors parsing config file
+       if (-e $filename) {
+               do $filename;
+               die $@ if $@;
+               return 1;
+       }
+       return;
+}
+
+our ($GITWEB_CONFIG, $GITWEB_CONFIG_SYSTEM, $GITWEB_CONFIG_COMMON);
+sub evaluate_gitweb_config {
+       our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "gitweb_config.perl";
+       our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "/etc/gitweb.conf";
+       our $GITWEB_CONFIG_COMMON = $ENV{'GITWEB_CONFIG_COMMON'} || "/etc/gitweb-common.conf";
+
+       # Protect against duplications of file names, to not read config twice.
+       # Only one of $GITWEB_CONFIG and $GITWEB_CONFIG_SYSTEM is used, so
+       # there possibility of duplication of filename there doesn't matter.
+       $GITWEB_CONFIG = ""        if ($GITWEB_CONFIG eq $GITWEB_CONFIG_COMMON);
+       $GITWEB_CONFIG_SYSTEM = "" if ($GITWEB_CONFIG_SYSTEM eq $GITWEB_CONFIG_COMMON);
+
+       # Common system-wide settings for convenience.
+       # Those settings can be ovverriden by GITWEB_CONFIG or GITWEB_CONFIG_SYSTEM.
+       read_config_file($GITWEB_CONFIG_COMMON);
+
+       # Use first config file that exists.  This means use the per-instance
+       # GITWEB_CONFIG if exists, otherwise use GITWEB_SYSTEM_CONFIG.
+       read_config_file($GITWEB_CONFIG) and return;
+       read_config_file($GITWEB_CONFIG_SYSTEM);
+}
+
+# Get loadavg of system, to compare against $maxload.
+# Currently it requires '/proc/loadavg' present to get loadavg;
+# if it is not present it returns 0, which means no load checking.
+sub get_loadavg {
+       if( -e '/proc/loadavg' ){
+               open my $fd, '<', '/proc/loadavg'
+                       or return 0;
+               my @load = split(/\s+/, scalar <$fd>);
+               close $fd;
+
+               # The first three columns measure CPU and IO utilization of the last one,
+               # five, and 10 minute periods.  The fourth column shows the number of
+               # currently running processes and the total number of processes in the m/n
+               # format.  The last column displays the last process ID used.
+               return $load[0] || 0;
+       }
+       # additional checks for load average should go here for things that don't export
+       # /proc/loadavg
+
+       return 0;
+}
+
+# version of the core git binary
+our $git_version;
+sub evaluate_git_version {
+       our $git_version = qx("$GIT" --version) =~ m/git version (.*)$/ ? $1 : "unknown";
+       $number_of_git_cmds++;
+}
+
+sub check_loadavg {
+       if (defined $maxload && get_loadavg() > $maxload) {
+               die_error(503, "The load average on the server is too high");
+       }
+}
+
+# ======================================================================
+# input validation and dispatch
+
+# input parameters can be collected from a variety of sources (presently, CGI
+# and PATH_INFO), so we define an %input_params hash that collects them all
+# together during validation: this allows subsequent uses (e.g. href()) to be
+# agnostic of the parameter origin
+
+our %input_params = ();
+
+# input parameters are stored with the long parameter name as key. This will
+# also be used in the href subroutine to convert parameters to their CGI
+# equivalent, and since the href() usage is the most frequent one, we store
+# the name -> CGI key mapping here, instead of the reverse.
+#
+# XXX: Warning: If you touch this, check the search form for updating,
+# too.
+
+our @cgi_param_mapping = (
+       project => "p",
+       action => "a",
+       file_name => "f",
+       file_parent => "fp",
+       hash => "h",
+       hash_parent => "hp",
+       hash_base => "hb",
+       hash_parent_base => "hpb",
+       page => "pg",
+       order => "o",
+       searchtext => "s",
+       searchtype => "st",
+       snapshot_format => "sf",
+       extra_options => "opt",
+       search_use_regexp => "sr",
+       ctag => "by_tag",
+       diff_style => "ds",
+       project_filter => "pf",
+       # this must be last entry (for manipulation from JavaScript)
+       javascript => "js"
+);
+our %cgi_param_mapping = @cgi_param_mapping;
+
+# we will also need to know the possible actions, for validation
+our %actions = (
+       "blame" => \&git_blame,
+       "blame_incremental" => \&git_blame_incremental,
+       "blame_data" => \&git_blame_data,
+       "blobdiff" => \&git_blobdiff,
+       "blobdiff_plain" => \&git_blobdiff_plain,
+       "blob" => \&git_blob,
+       "blob_plain" => \&git_blob_plain,
+       "commitdiff" => \&git_commitdiff,
+       "commitdiff_plain" => \&git_commitdiff_plain,
+       "commit" => \&git_commit,
+       "forks" => \&git_forks,
+       "heads" => \&git_heads,
+       "history" => \&git_history,
+       "log" => \&git_log,
+       "patch" => \&git_patch,
+       "patches" => \&git_patches,
+       "remotes" => \&git_remotes,
+       "rss" => \&git_rss,
+       "atom" => \&git_atom,
+       "search" => \&git_search,
+       "search_help" => \&git_search_help,
+       "shortlog" => \&git_shortlog,
+       "summary" => \&git_summary,
+       "tag" => \&git_tag,
+       "tags" => \&git_tags,
+       "tree" => \&git_tree,
+       "snapshot" => \&git_snapshot,
+       "object" => \&git_object,
+       # those below don't need $project
+       "opml" => \&git_opml,
+       "project_list" => \&git_project_list,
+       "project_index" => \&git_project_index,
+);
+
+# finally, we have the hash of allowed extra_options for the commands that
+# allow them
+our %allowed_options = (
+       "--no-merges" => [ qw(rss atom log shortlog history) ],
+);
+
+# fill %input_params with the CGI parameters. All values except for 'opt'
+# should be single values, but opt can be an array. We should probably
+# build an array of parameters that can be multi-valued, but since for the time
+# being it's only this one, we just single it out
+sub evaluate_query_params {
+       our $cgi;
+
+       while (my ($name, $symbol) = each %cgi_param_mapping) {
+               if ($symbol eq 'opt') {
+                       $input_params{$name} = [ map { decode_utf8($_) } $cgi->multi_param($symbol) ];
+               } else {
+                       $input_params{$name} = decode_utf8($cgi->param($symbol));
+               }
+       }
+}
+
+# now read PATH_INFO and update the parameter list for missing parameters
+sub evaluate_path_info {
+       return if defined $input_params{'project'};
+       return if !$path_info;
+       $path_info =~ s,^/+,,;
+       return if !$path_info;
+
+       # find which part of PATH_INFO is project
+       my $project = $path_info;
+       $project =~ s,/+$,,;
+       while ($project && !check_head_link("$projectroot/$project")) {
+               $project =~ s,/*[^/]*$,,;
+       }
+       return unless $project;
+       $input_params{'project'} = $project;
+
+       # do not change any parameters if an action is given using the query string
+       return if $input_params{'action'};
+       $path_info =~ s,^\Q$project\E/*,,;
+
+       # next, check if we have an action
+       my $action = $path_info;
+       $action =~ s,/.*$,,;
+       if (exists $actions{$action}) {
+               $path_info =~ s,^$action/*,,;
+               $input_params{'action'} = $action;
+       }
+
+       # list of actions that want hash_base instead of hash, but can have no
+       # pathname (f) parameter
+       my @wants_base = (
+               'tree',
+               'history',
+       );
+
+       # we want to catch, among others
+       # [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name]
+       my ($parentrefname, $parentpathname, $refname, $pathname) =
+               ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?([^:]+?)?(?::(.+))?$/);
+
+       # first, analyze the 'current' part
+       if (defined $pathname) {
+               # we got "branch:filename" or "branch:dir/"
+               # we could use git_get_type(branch:pathname), but:
+               # - it needs $git_dir
+               # - it does a git() call
+               # - the convention of terminating directories with a slash
+               #   makes it superfluous
+               # - embedding the action in the PATH_INFO would make it even
+               #   more superfluous
+               $pathname =~ s,^/+,,;
+               if (!$pathname || substr($pathname, -1) eq "/") {
+                       $input_params{'action'} ||= "tree";
+                       $pathname =~ s,/$,,;
+               } else {
+                       # the default action depends on whether we had parent info
+                       # or not
+                       if ($parentrefname) {
+                               $input_params{'action'} ||= "blobdiff_plain";
+                       } else {
+                               $input_params{'action'} ||= "blob_plain";
+                       }
+               }
+               $input_params{'hash_base'} ||= $refname;
+               $input_params{'file_name'} ||= $pathname;
+       } elsif (defined $refname) {
+               # we got "branch". In this case we have to choose if we have to
+               # set hash or hash_base.
+               #
+               # Most of the actions without a pathname only want hash to be
+               # set, except for the ones specified in @wants_base that want
+               # hash_base instead. It should also be noted that hand-crafted
+               # links having 'history' as an action and no pathname or hash
+               # set will fail, but that happens regardless of PATH_INFO.
+               if (defined $parentrefname) {
+                       # if there is parent let the default be 'shortlog' action
+                       # (for http://git.example.com/repo.git/A..B links); if there
+                       # is no parent, dispatch will detect type of object and set
+                       # action appropriately if required (if action is not set)
+                       $input_params{'action'} ||= "shortlog";
+               }
+               if ($input_params{'action'} &&
+                   grep { $_ eq $input_params{'action'} } @wants_base) {
+                       $input_params{'hash_base'} ||= $refname;
+               } else {
+                       $input_params{'hash'} ||= $refname;
+               }
+       }
+
+       # next, handle the 'parent' part, if present
+       if (defined $parentrefname) {
+               # a missing pathspec defaults to the 'current' filename, allowing e.g.
+               # someproject/blobdiff/oldrev..newrev:/filename
+               if ($parentpathname) {
+                       $parentpathname =~ s,^/+,,;
+                       $parentpathname =~ s,/$,,;
+                       $input_params{'file_parent'} ||= $parentpathname;
+               } else {
+                       $input_params{'file_parent'} ||= $input_params{'file_name'};
+               }
+               # we assume that hash_parent_base is wanted if a path was specified,
+               # or if the action wants hash_base instead of hash
+               if (defined $input_params{'file_parent'} ||
+                       grep { $_ eq $input_params{'action'} } @wants_base) {
+                       $input_params{'hash_parent_base'} ||= $parentrefname;
+               } else {
+                       $input_params{'hash_parent'} ||= $parentrefname;
+               }
+       }
+
+       # for the snapshot action, we allow URLs in the form
+       # $project/snapshot/$hash.ext
+       # where .ext determines the snapshot and gets removed from the
+       # passed $refname to provide the $hash.
+       #
+       # To be able to tell that $refname includes the format extension, we
+       # require the following two conditions to be satisfied:
+       # - the hash input parameter MUST have been set from the $refname part
+       #   of the URL (i.e. they must be equal)
+       # - the snapshot format MUST NOT have been defined already (e.g. from
+       #   CGI parameter sf)
+       # It's also useless to try any matching unless $refname has a dot,
+       # so we check for that too
+       if (defined $input_params{'action'} &&
+               $input_params{'action'} eq 'snapshot' &&
+               defined $refname && index($refname, '.') != -1 &&
+               $refname eq $input_params{'hash'} &&
+               !defined $input_params{'snapshot_format'}) {
+               # We loop over the known snapshot formats, checking for
+               # extensions. Allowed extensions are both the defined suffix
+               # (which includes the initial dot already) and the snapshot
+               # format key itself, with a prepended dot
+               while (my ($fmt, $opt) = each %known_snapshot_formats) {
+                       my $hash = $refname;
+                       unless ($hash =~ s/(\Q$opt->{'suffix'}\E|\Q.$fmt\E)$//) {
+                               next;
+                       }
+                       my $sfx = $1;
+                       # a valid suffix was found, so set the snapshot format
+                       # and reset the hash parameter
+                       $input_params{'snapshot_format'} = $fmt;
+                       $input_params{'hash'} = $hash;
+                       # we also set the format suffix to the one requested
+                       # in the URL: this way a request for e.g. .tgz returns
+                       # a .tgz instead of a .tar.gz
+                       $known_snapshot_formats{$fmt}{'suffix'} = $sfx;
+                       last;
+               }
+       }
+}
+
+our ($action, $project, $file_name, $file_parent, $hash, $hash_parent, $hash_base,
+     $hash_parent_base, @extra_options, $page, $searchtype, $search_use_regexp,
+     $searchtext, $search_regexp, $project_filter);
+sub evaluate_and_validate_params {
+       our $action = $input_params{'action'};
+       if (defined $action) {
+               if (!is_valid_action($action)) {
+                       die_error(400, "Invalid action parameter");
+               }
+       }
+
+       # parameters which are pathnames
+       our $project = $input_params{'project'};
+       if (defined $project) {
+               if (!is_valid_project($project)) {
+                       undef $project;
+                       die_error(404, "No such project");
+               }
+       }
+
+       our $project_filter = $input_params{'project_filter'};
+       if (defined $project_filter) {
+               if (!is_valid_pathname($project_filter)) {
+                       die_error(404, "Invalid project_filter parameter");
+               }
+       }
+
+       our $file_name = $input_params{'file_name'};
+       if (defined $file_name) {
+               if (!is_valid_pathname($file_name)) {
+                       die_error(400, "Invalid file parameter");
+               }
+       }
+
+       our $file_parent = $input_params{'file_parent'};
+       if (defined $file_parent) {
+               if (!is_valid_pathname($file_parent)) {
+                       die_error(400, "Invalid file parent parameter");
+               }
+       }
+
+       # parameters which are refnames
+       our $hash = $input_params{'hash'};
+       if (defined $hash) {
+               if (!is_valid_refname($hash)) {
+                       die_error(400, "Invalid hash parameter");
+               }
+       }
+
+       our $hash_parent = $input_params{'hash_parent'};
+       if (defined $hash_parent) {
+               if (!is_valid_refname($hash_parent)) {
+                       die_error(400, "Invalid hash parent parameter");
+               }
+       }
+
+       our $hash_base = $input_params{'hash_base'};
+       if (defined $hash_base) {
+               if (!is_valid_refname($hash_base)) {
+                       die_error(400, "Invalid hash base parameter");
+               }
+       }
+
+       our @extra_options = @{$input_params{'extra_options'}};
+       # @extra_options is always defined, since it can only be (currently) set from
+       # CGI, and $cgi->param() returns the empty array in array context if the param
+       # is not set
+       foreach my $opt (@extra_options) {
+               if (not exists $allowed_options{$opt}) {
+                       die_error(400, "Invalid option parameter");
+               }
+               if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
+                       die_error(400, "Invalid option parameter for this action");
+               }
+       }
+
+       our $hash_parent_base = $input_params{'hash_parent_base'};
+       if (defined $hash_parent_base) {
+               if (!is_valid_refname($hash_parent_base)) {
+                       die_error(400, "Invalid hash parent base parameter");
+               }
+       }
+
+       # other parameters
+       our $page = $input_params{'page'};
+       if (defined $page) {
+               if ($page =~ m/[^0-9]/) {
+                       die_error(400, "Invalid page parameter");
+               }
+       }
+
+       our $searchtype = $input_params{'searchtype'};
+       if (defined $searchtype) {
+               if ($searchtype =~ m/[^a-z]/) {
+                       die_error(400, "Invalid searchtype parameter");
+               }
+       }
+
+       our $search_use_regexp = $input_params{'search_use_regexp'};
+
+       our $searchtext = $input_params{'searchtext'};
+       our $search_regexp = undef;
+       if (defined $searchtext) {
+               if (length($searchtext) < 2) {
+                       die_error(403, "At least two characters are required for search parameter");
+               }
+               if ($search_use_regexp) {
+                       $search_regexp = $searchtext;
+                       if (!eval { qr/$search_regexp/; 1; }) {
+                               (my $error = $@) =~ s/ at \S+ line \d+.*\n?//;
+                               die_error(400, "Invalid search regexp '$search_regexp'",
+                                         esc_html($error));
+                       }
+               } else {
+                       $search_regexp = quotemeta $searchtext;
+               }
+       }
+}
+
+# path to the current git repository
+our $git_dir;
+sub evaluate_git_dir {
+       our $git_dir = "$projectroot/$project" if $project;
+}
+
+our (@snapshot_fmts, $git_avatar, @extra_branch_refs);
+sub configure_gitweb_features {
+       # list of supported snapshot formats
+       our @snapshot_fmts = gitweb_get_feature('snapshot');
+       @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
+
+       # check that the avatar feature is set to a known provider name,
+       # and for each provider check if the dependencies are satisfied.
+       # if the provider name is invalid or the dependencies are not met,
+       # reset $git_avatar to the empty string.
+       our ($git_avatar) = gitweb_get_feature('avatar');
+       if ($git_avatar eq 'gravatar') {
+               $git_avatar = '' unless (eval { require Digest::MD5; 1; });
+       } elsif ($git_avatar eq 'picon') {
+               # no dependencies
+       } else {
+               $git_avatar = '';
+       }
+
+       our @extra_branch_refs = gitweb_get_feature('extra-branch-refs');
+       @extra_branch_refs = filter_and_validate_refs (@extra_branch_refs);
+}
+
+sub get_branch_refs {
+       return ('heads', @extra_branch_refs);
+}
+
+# custom error handler: 'die <message>' is Internal Server Error
+sub handle_errors_html {
+       my $msg = shift; # it is already HTML escaped
+
+       # to avoid infinite loop where error occurs in die_error,
+       # change handler to default handler, disabling handle_errors_html
+       set_message("Error occurred when inside die_error:\n$msg");
+
+       # you cannot jump out of die_error when called as error handler;
+       # the subroutine set via CGI::Carp::set_message is called _after_
+       # HTTP headers are already written, so it cannot write them itself
+       die_error(undef, undef, $msg, -error_handler => 1, -no_http_header => 1);
+}
+set_message(\&handle_errors_html);
+
+# dispatch
+sub dispatch {
+       if (!defined $action) {
+               if (defined $hash) {
+                       $action = git_get_type($hash);
+                       $action or die_error(404, "Object does not exist");
+               } elsif (defined $hash_base && defined $file_name) {
+                       $action = git_get_type("$hash_base:$file_name");
+                       $action or die_error(404, "File or directory does not exist");
+               } elsif (defined $project) {
+                       $action = 'summary';
+               } else {
+                       $action = 'project_list';
+               }
+       }
+       if (!defined($actions{$action})) {
+               die_error(400, "Unknown action");
+       }
+       if ($action !~ m/^(?:opml|project_list|project_index)$/ &&
+           !$project) {
+               die_error(400, "Project needed");
+       }
+       $actions{$action}->();
+}
+
+sub reset_timer {
+       our $t0 = [ gettimeofday() ]
+               if defined $t0;
+       our $number_of_git_cmds = 0;
+}
+
+our $first_request = 1;
+sub run_request {
+       reset_timer();
+
+       evaluate_uri();
+       if ($first_request) {
+               evaluate_gitweb_config();
+               evaluate_git_version();
+       }
+       if ($per_request_config) {
+               if (ref($per_request_config) eq 'CODE') {
+                       $per_request_config->();
+               } elsif (!$first_request) {
+                       evaluate_gitweb_config();
+               }
+       }
+       check_loadavg();
+
+       # $projectroot and $projects_list might be set in gitweb config file
+       $projects_list ||= $projectroot;
+
+       evaluate_query_params();
+       evaluate_path_info();
+       evaluate_and_validate_params();
+       evaluate_git_dir();
+
+       configure_gitweb_features();
+
+       dispatch();
+}
+
+our $is_last_request = sub { 1 };
+our ($pre_dispatch_hook, $post_dispatch_hook, $pre_listen_hook);
+our $CGI = 'CGI';
+our $cgi;
+sub configure_as_fcgi {
+       require CGI::Fast;
+       our $CGI = 'CGI::Fast';
+
+       my $request_number = 0;
+       # let each child service 100 requests
+       our $is_last_request = sub { ++$request_number > 100 };
+}
+sub evaluate_argv {
+       my $script_name = $ENV{'SCRIPT_NAME'} || $ENV{'SCRIPT_FILENAME'} || __FILE__;
+       configure_as_fcgi()
+               if $script_name =~ /\.fcgi$/;
+
+       return unless (@ARGV);
+
+       require Getopt::Long;
+       Getopt::Long::GetOptions(
+               'fastcgi|fcgi|f' => \&configure_as_fcgi,
+               'nproc|n=i' => sub {
+                       my ($arg, $val) = @_;
+                       return unless eval { require FCGI::ProcManager; 1; };
+                       my $proc_manager = FCGI::ProcManager->new({
+                               n_processes => $val,
+                       });
+                       our $pre_listen_hook    = sub { $proc_manager->pm_manage()        };
+                       our $pre_dispatch_hook  = sub { $proc_manager->pm_pre_dispatch()  };
+                       our $post_dispatch_hook = sub { $proc_manager->pm_post_dispatch() };
+               },
+       );
+}
+
+sub run {
+       evaluate_argv();
+
+       $first_request = 1;
+       $pre_listen_hook->()
+               if $pre_listen_hook;
+
+ REQUEST:
+       while ($cgi = $CGI->new()) {
+               $pre_dispatch_hook->()
+                       if $pre_dispatch_hook;
+
+               run_request();
+
+               $post_dispatch_hook->()
+                       if $post_dispatch_hook;
+               $first_request = 0;
+
+               last REQUEST if ($is_last_request->());
+       }
+
+ DONE_GITWEB:
+       1;
+}
+
+run();
+
+if (defined caller) {
+       # wrapped in a subroutine processing requests,
+       # e.g. mod_perl with ModPerl::Registry, or PSGI with Plack::App::WrapCGI
+       return;
+} else {
+       # pure CGI script, serving single request
+       exit;
+}
+
+## ======================================================================
+## action links
+
+# possible values of extra options
+# -full => 0|1      - use absolute/full URL ($my_uri/$my_url as base)
+# -replay => 1      - start from a current view (replay with modifications)
+# -path_info => 0|1 - don't use/use path_info URL (if possible)
+# -anchor => ANCHOR - add #ANCHOR to end of URL, implies -replay if used alone
+sub href {
+       my %params = @_;
+       # default is to use -absolute url() i.e. $my_uri
+       my $href = $params{-full} ? $my_url : $my_uri;
+
+       # implicit -replay, must be first of implicit params
+       $params{-replay} = 1 if (keys %params == 1 && $params{-anchor});
+
+       $params{'project'} = $project unless exists $params{'project'};
+
+       if ($params{-replay}) {
+               while (my ($name, $symbol) = each %cgi_param_mapping) {
+                       if (!exists $params{$name}) {
+                               $params{$name} = $input_params{$name};
+                       }
+               }
+       }
+
+       my $use_pathinfo = gitweb_check_feature('pathinfo');
+       if (defined $params{'project'} &&
+           (exists $params{-path_info} ? $params{-path_info} : $use_pathinfo)) {
+               # try to put as many parameters as possible in PATH_INFO:
+               #   - project name
+               #   - action
+               #   - hash_parent or hash_parent_base:/file_parent
+               #   - hash or hash_base:/filename
+               #   - the snapshot_format as an appropriate suffix
+
+               # When the script is the root DirectoryIndex for the domain,
+               # $href here would be something like http://gitweb.example.com/
+               # Thus, we strip any trailing / from $href, to spare us double
+               # slashes in the final URL
+               $href =~ s,/$,,;
+
+               # Then add the project name, if present
+               $href .= "/".esc_path_info($params{'project'});
+               delete $params{'project'};
+
+               # since we destructively absorb parameters, we keep this
+               # boolean that remembers if we're handling a snapshot
+               my $is_snapshot = $params{'action'} eq 'snapshot';
+
+               # Summary just uses the project path URL, any other action is
+               # added to the URL
+               if (defined $params{'action'}) {
+                       $href .= "/".esc_path_info($params{'action'})
+                               unless $params{'action'} eq 'summary';
+                       delete $params{'action'};
+               }
+
+               # Next, we put hash_parent_base:/file_parent..hash_base:/file_name,
+               # stripping nonexistent or useless pieces
+               $href .= "/" if ($params{'hash_base'} || $params{'hash_parent_base'}
+                       || $params{'hash_parent'} || $params{'hash'});
+               if (defined $params{'hash_base'}) {
+                       if (defined $params{'hash_parent_base'}) {
+                               $href .= esc_path_info($params{'hash_parent_base'});
+                               # skip the file_parent if it's the same as the file_name
+                               if (defined $params{'file_parent'}) {
+                                       if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) {
+                                               delete $params{'file_parent'};
+                                       } elsif ($params{'file_parent'} !~ /\.\./) {
+                                               $href .= ":/".esc_path_info($params{'file_parent'});
+                                               delete $params{'file_parent'};
+                                       }
+                               }
+                               $href .= "..";
+                               delete $params{'hash_parent'};
+                               delete $params{'hash_parent_base'};
+                       } elsif (defined $params{'hash_parent'}) {
+                               $href .= esc_path_info($params{'hash_parent'}). "..";
+                               delete $params{'hash_parent'};
+                       }
+
+                       $href .= esc_path_info($params{'hash_base'});
+                       if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) {
+                               $href .= ":/".esc_path_info($params{'file_name'});
+                               delete $params{'file_name'};
+                       }
+                       delete $params{'hash'};
+                       delete $params{'hash_base'};
+               } elsif (defined $params{'hash'}) {
+                       $href .= esc_path_info($params{'hash'});
+                       delete $params{'hash'};
+               }
+
+               # If the action was a snapshot, we can absorb the
+               # snapshot_format parameter too
+               if ($is_snapshot) {
+                       my $fmt = $params{'snapshot_format'};
+                       # snapshot_format should always be defined when href()
+                       # is called, but just in case some code forgets, we
+                       # fall back to the default
+                       $fmt ||= $snapshot_fmts[0];
+                       $href .= $known_snapshot_formats{$fmt}{'suffix'};
+                       delete $params{'snapshot_format'};
+               }
+       }
+
+       # now encode the parameters explicitly
+       my @result = ();
+       for (my $i = 0; $i < @cgi_param_mapping; $i += 2) {
+               my ($name, $symbol) = ($cgi_param_mapping[$i], $cgi_param_mapping[$i+1]);
+               if (defined $params{$name}) {
+                       if (ref($params{$name}) eq "ARRAY") {
+                               foreach my $par (@{$params{$name}}) {
+                                       push @result, $symbol . "=" . esc_param($par);
+                               }
+                       } else {
+                               push @result, $symbol . "=" . esc_param($params{$name});
+                       }
+               }
+       }
+       $href .= "?" . join(';', @result) if scalar @result;
+
+       # final transformation: trailing spaces must be escaped (URI-encoded)
+       $href =~ s/(\s+)$/CGI::escape($1)/e;
+
+       if ($params{-anchor}) {
+               $href .= "#".esc_param($params{-anchor});
+       }
+
+       return $href;
+}
+
+
+## ======================================================================
+## validation, quoting/unquoting and escaping
+
+sub is_valid_action {
+       my $input = shift;
+       return undef unless exists $actions{$input};
+       return 1;
+}
+
+sub is_valid_project {
+       my $input = shift;
+
+       return unless defined $input;
+       if (!is_valid_pathname($input) ||
+               !(-d "$projectroot/$input") ||
+               !check_export_ok("$projectroot/$input") ||
+               ($strict_export && !project_in_list($input))) {
+               return undef;
+       } else {
+               return 1;
+       }
+}
+
+sub is_valid_pathname {
+       my $input = shift;
+
+       return undef unless defined $input;
+       # no '.' or '..' as elements of path, i.e. no '.' or '..'
+       # at the beginning, at the end, and between slashes.
+       # also this catches doubled slashes
+       if ($input =~ m!(^|/)(|\.|\.\.)(/|$)!) {
+               return undef;
+       }
+       # no null characters
+       if ($input =~ m!\0!) {
+               return undef;
+       }
+       return 1;
+}
+
+sub is_valid_ref_format {
+       my $input = shift;
+
+       return undef unless defined $input;
+       # restrictions on ref name according to git-check-ref-format
+       if ($input =~ m!(/\.|\.\.|[\000-\040\177 ~^:?*\[]|/$)!) {
+               return undef;
+       }
+       return 1;
+}
+
+sub is_valid_refname {
+       my $input = shift;
+
+       return undef unless defined $input;
+       # textual hashes are O.K.
+       if ($input =~ m/^[0-9a-fA-F]{40}$/) {
+               return 1;
+       }
+       # it must be correct pathname
+       is_valid_pathname($input) or return undef;
+       # check git-check-ref-format restrictions
+       is_valid_ref_format($input) or return undef;
+       return 1;
+}
+
+# decode sequences of octets in utf8 into Perl's internal form,
+# which is utf-8 with utf8 flag set if needed.  gitweb writes out
+# in utf-8 thanks to "binmode STDOUT, ':utf8'" at beginning
+sub to_utf8 {
+       my $str = shift;
+       return undef unless defined $str;
+
+       if (utf8::is_utf8($str) || utf8::decode($str)) {
+               return $str;
+       } else {
+               return decode($fallback_encoding, $str, Encode::FB_DEFAULT);
+       }
+}
+
+# quote unsafe chars, but keep the slash, even when it's not
+# correct, but quoted slashes look too horrible in bookmarks
+sub esc_param {
+       my $str = shift;
+       return undef unless defined $str;
+       $str =~ s/([^A-Za-z0-9\-_.~()\/:@ ]+)/CGI::escape($1)/eg;
+       $str =~ s/ /\+/g;
+       return $str;
+}
+
+# the quoting rules for path_info fragment are slightly different
+sub esc_path_info {
+       my $str = shift;
+       return undef unless defined $str;
+
+       # path_info doesn't treat '+' as space (specially), but '?' must be escaped
+       $str =~ s/([^A-Za-z0-9\-_.~();\/;:@&= +]+)/CGI::escape($1)/eg;
+
+       return $str;
+}
+
+# quote unsafe chars in whole URL, so some characters cannot be quoted
+sub esc_url {
+       my $str = shift;
+       return undef unless defined $str;
+       $str =~ s/([^A-Za-z0-9\-_.~();\/;?:@&= ]+)/CGI::escape($1)/eg;
+       $str =~ s/ /\+/g;
+       return $str;
+}
+
+# quote unsafe characters in HTML attributes
+sub esc_attr {
+
+       # for XHTML conformance escaping '"' to '&quot;' is not enough
+       return esc_html(@_);
+}
+
+# replace invalid utf8 character with SUBSTITUTION sequence
+sub esc_html {
+       my $str = shift;
+       my %opts = @_;
+
+       return undef unless defined $str;
+
+       $str = to_utf8($str);
+       $str = $cgi->escapeHTML($str);
+       if ($opts{'-nbsp'}) {
+               $str =~ s/ /&nbsp;/g;
+       }
+       $str =~ s|([[:cntrl:]])|(($1 ne "\t") ? quot_cec($1) : $1)|eg;
+       return $str;
+}
+
+# quote control characters and escape filename to HTML
+sub esc_path {
+       my $str = shift;
+       my %opts = @_;
+
+       return undef unless defined $str;
+
+       $str = to_utf8($str);
+       $str = $cgi->escapeHTML($str);
+       if ($opts{'-nbsp'}) {
+               $str =~ s/ /&nbsp;/g;
+       }
+       $str =~ s|([[:cntrl:]])|quot_cec($1)|eg;
+       return $str;
+}
+
+# Sanitize for use in XHTML + application/xml+xhtm (valid XML 1.0)
+sub sanitize {
+       my $str = shift;
+
+       return undef unless defined $str;
+
+       $str = to_utf8($str);
+       $str =~ s|([[:cntrl:]])|(index("\t\n\r", $1) != -1 ? $1 : quot_cec($1))|eg;
+       return $str;
+}
+
+# Make control characters "printable", using character escape codes (CEC)
+sub quot_cec {
+       my $cntrl = shift;
+       my %opts = @_;
+       my %es = ( # character escape codes, aka escape sequences
+               "\t" => '\t',   # tab            (HT)
+               "\n" => '\n',   # line feed      (LF)
+               "\r" => '\r',   # carrige return (CR)
+               "\f" => '\f',   # form feed      (FF)
+               "\b" => '\b',   # backspace      (BS)
+               "\a" => '\a',   # alarm (bell)   (BEL)
+               "\e" => '\e',   # escape         (ESC)
+               "\013" => '\v', # vertical tab   (VT)
+               "\000" => '\0', # nul character  (NUL)
+       );
+       my $chr = ( (exists $es{$cntrl})
+                   ? $es{$cntrl}
+                   : sprintf('\%2x', ord($cntrl)) );
+       if ($opts{-nohtml}) {
+               return $chr;
+       } else {
+               return "<span class=\"cntrl\">$chr</span>";
+       }
+}
+
+# Alternatively use unicode control pictures codepoints,
+# Unicode "printable representation" (PR)
+sub quot_upr {
+       my $cntrl = shift;
+       my %opts = @_;
+
+       my $chr = sprintf('&#%04d;', 0x2400+ord($cntrl));
+       if ($opts{-nohtml}) {
+               return $chr;
+       } else {
+               return "<span class=\"cntrl\">$chr</span>";
+       }
+}
+
+# git may return quoted and escaped filenames
+sub unquote {
+       my $str = shift;
+
+       sub unq {
+               my $seq = shift;
+               my %es = ( # character escape codes, aka escape sequences
+                       't' => "\t",   # tab            (HT, TAB)
+                       'n' => "\n",   # newline        (NL)
+                       'r' => "\r",   # return         (CR)
+                       'f' => "\f",   # form feed      (FF)
+                       'b' => "\b",   # backspace      (BS)
+                       'a' => "\a",   # alarm (bell)   (BEL)
+                       'e' => "\e",   # escape         (ESC)
+                       'v' => "\013", # vertical tab   (VT)
+               );
+
+               if ($seq =~ m/^[0-7]{1,3}$/) {
+                       # octal char sequence
+                       return chr(oct($seq));
+               } elsif (exists $es{$seq}) {
+                       # C escape sequence, aka character escape code
+                       return $es{$seq};
+               }
+               # quoted ordinary character
+               return $seq;
+       }
+
+       if ($str =~ m/^"(.*)"$/) {
+               # needs unquoting
+               $str = $1;
+               $str =~ s/\\([^0-7]|[0-7]{1,3})/unq($1)/eg;
+       }
+       return $str;
+}
+
+# escape tabs (convert tabs to spaces)
+sub untabify {
+       my $line = shift;
+
+       while ((my $pos = index($line, "\t")) != -1) {
+               if (my $count = (8 - ($pos % 8))) {
+                       my $spaces = ' ' x $count;
+                       $line =~ s/\t/$spaces/;
+               }
+       }
+
+       return $line;
+}
+
+sub project_in_list {
+       my $project = shift;
+       my @list = git_get_projects_list();
+       return @list && scalar(grep { $_->{'path'} eq $project } @list);
+}
+
+## ----------------------------------------------------------------------
+## HTML aware string manipulation
+
+# Try to chop given string on a word boundary between position
+# $len and $len+$add_len. If there is no word boundary there,
+# chop at $len+$add_len. Do not chop if chopped part plus ellipsis
+# (marking chopped part) would be longer than given string.
+sub chop_str {
+       my $str = shift;
+       my $len = shift;
+       my $add_len = shift || 10;
+       my $where = shift || 'right'; # 'left' | 'center' | 'right'
+
+       # Make sure perl knows it is utf8 encoded so we don't
+       # cut in the middle of a utf8 multibyte char.
+       $str = to_utf8($str);
+
+       # allow only $len chars, but don't cut a word if it would fit in $add_len
+       # if it doesn't fit, cut it if it's still longer than the dots we would add
+       # remove chopped character entities entirely
+
+       # when chopping in the middle, distribute $len into left and right part
+       # return early if chopping wouldn't make string shorter
+       if ($where eq 'center') {
+               return $str if ($len + 5 >= length($str)); # filler is length 5
+               $len = int($len/2);
+       } else {
+               return $str if ($len + 4 >= length($str)); # filler is length 4
+       }
+
+       # regexps: ending and beginning with word part up to $add_len
+       my $endre = qr/.{$len}\w{0,$add_len}/;
+       my $begre = qr/\w{0,$add_len}.{$len}/;
+
+       if ($where eq 'left') {
+               $str =~ m/^(.*?)($begre)$/;
+               my ($lead, $body) = ($1, $2);
+               if (length($lead) > 4) {
+                       $lead = " ...";
+               }
+               return "$lead$body";
+
+       } elsif ($where eq 'center') {
+               $str =~ m/^($endre)(.*)$/;
+               my ($left, $str)  = ($1, $2);
+               $str =~ m/^(.*?)($begre)$/;
+               my ($mid, $right) = ($1, $2);
+               if (length($mid) > 5) {
+                       $mid = " ... ";
+               }
+               return "$left$mid$right";
+
+       } else {
+               $str =~ m/^($endre)(.*)$/;
+               my $body = $1;
+               my $tail = $2;
+               if (length($tail) > 4) {
+                       $tail = "... ";
+               }
+               return "$body$tail";
+       }
+}
+
+# takes the same arguments as chop_str, but also wraps a <span> around the
+# result with a title attribute if it does get chopped. Additionally, the
+# string is HTML-escaped.
+sub chop_and_escape_str {
+       my ($str) = @_;
+
+       my $chopped = chop_str(@_);
+       $str = to_utf8($str);
+       if ($chopped eq $str) {
+               return esc_html($chopped);
+       } else {
+               $str =~ s/[[:cntrl:]]/?/g;
+               return $cgi->span({-title=>$str}, esc_html($chopped));
+       }
+}
+
+# Highlight selected fragments of string, using given CSS class,
+# and escape HTML.  It is assumed that fragments do not overlap.
+# Regions are passed as list of pairs (array references).
+#
+# Example: esc_html_hl_regions("foobar", "mark", [ 0, 3 ]) returns
+# '<span class="mark">foo</span>bar'
+sub esc_html_hl_regions {
+       my ($str, $css_class, @sel) = @_;
+       my %opts = grep { ref($_) ne 'ARRAY' } @sel;
+       @sel     = grep { ref($_) eq 'ARRAY' } @sel;
+       return esc_html($str, %opts) unless @sel;
+
+       my $out = '';
+       my $pos = 0;
+
+       for my $s (@sel) {
+               my ($begin, $end) = @$s;
+
+               # Don't create empty <span> elements.
+               next if $end <= $begin;
+
+               my $escaped = esc_html(substr($str, $begin, $end - $begin),
+                                      %opts);
+
+               $out .= esc_html(substr($str, $pos, $begin - $pos), %opts)
+                       if ($begin - $pos > 0);
+               $out .= $cgi->span({-class => $css_class}, $escaped);
+
+               $pos = $end;
+       }
+       $out .= esc_html(substr($str, $pos), %opts)
+               if ($pos < length($str));
+
+       return $out;
+}
+
+# return positions of beginning and end of each match
+sub matchpos_list {
+       my ($str, $regexp) = @_;
+       return unless (defined $str && defined $regexp);
+
+       my @matches;
+       while ($str =~ /$regexp/g) {
+               push @matches, [$-[0], $+[0]];
+       }
+       return @matches;
+}
+
+# highlight match (if any), and escape HTML
+sub esc_html_match_hl {
+       my ($str, $regexp) = @_;
+       return esc_html($str) unless defined $regexp;
+
+       my @matches = matchpos_list($str, $regexp);
+       return esc_html($str) unless @matches;
+
+       return esc_html_hl_regions($str, 'match', @matches);
+}
+
+
+# highlight match (if any) of shortened string, and escape HTML
+sub esc_html_match_hl_chopped {
+       my ($str, $chopped, $regexp) = @_;
+       return esc_html_match_hl($str, $regexp) unless defined $chopped;
+
+       my @matches = matchpos_list($str, $regexp);
+       return esc_html($chopped) unless @matches;
+
+       # filter matches so that we mark chopped string
+       my $tail = "... "; # see chop_str
+       unless ($chopped =~ s/\Q$tail\E$//) {
+               $tail = '';
+       }
+       my $chop_len = length($chopped);
+       my $tail_len = length($tail);
+       my @filtered;
+
+       for my $m (@matches) {
+               if ($m->[0] > $chop_len) {
+                       push @filtered, [ $chop_len, $chop_len + $tail_len ] if ($tail_len > 0);
+                       last;
+               } elsif ($m->[1] > $chop_len) {
+                       push @filtered, [ $m->[0], $chop_len + $tail_len ];
+                       last;
+               }
+               push @filtered, $m;
+       }
+
+       return esc_html_hl_regions($chopped . $tail, 'match', @filtered);
+}
+
+## ----------------------------------------------------------------------
+## functions returning short strings
+
+# CSS class for given age value (in seconds)
+sub age_class {
+       my $age = shift;
+
+       if (!defined $age) {
+               return "noage";
+       } elsif ($age < 60*60*2) {
+               return "age0";
+       } elsif ($age < 60*60*24*2) {
+               return "age1";
+       } else {
+               return "age2";
+       }
+}
+
+# convert age in seconds to "nn units ago" string
+sub age_string {
+       my $age = shift;
+       my $age_str;
+
+       if ($age > 60*60*24*365*2) {
+               $age_str = (int $age/60/60/24/365);
+               $age_str .= " years ago";
+       } elsif ($age > 60*60*24*(365/12)*2) {
+               $age_str = int $age/60/60/24/(365/12);
+               $age_str .= " months ago";
+       } elsif ($age > 60*60*24*7*2) {
+               $age_str = int $age/60/60/24/7;
+               $age_str .= " weeks ago";
+       } elsif ($age > 60*60*24*2) {
+               $age_str = int $age/60/60/24;
+               $age_str .= " days ago";
+       } elsif ($age > 60*60*2) {
+               $age_str = int $age/60/60;
+               $age_str .= " hours ago";
+       } elsif ($age > 60*2) {
+               $age_str = int $age/60;
+               $age_str .= " min ago";
+       } elsif ($age > 2) {
+               $age_str = int $age;
+               $age_str .= " sec ago";
+       } else {
+               $age_str .= " right now";
+       }
+       return $age_str;
+}
+
+use constant {
+       S_IFINVALID => 0030000,
+       S_IFGITLINK => 0160000,
+};
+
+# submodule/subproject, a commit object reference
+sub S_ISGITLINK {
+       my $mode = shift;
+
+       return (($mode & S_IFMT) == S_IFGITLINK)
+}
+
+# convert file mode in octal to symbolic file mode string
+sub mode_str {
+       my $mode = oct shift;
+
+       if (S_ISGITLINK($mode)) {
+               return 'm---------';
+       } elsif (S_ISDIR($mode & S_IFMT)) {
+               return 'drwxr-xr-x';
+       } elsif (S_ISLNK($mode)) {
+               return 'lrwxrwxrwx';
+       } elsif (S_ISREG($mode)) {
+               # git cares only about the executable bit
+               if ($mode & S_IXUSR) {
+                       return '-rwxr-xr-x';
+               } else {
+                       return '-rw-r--r--';
+               };
+       } else {
+               return '----------';
+       }
+}
+
+# convert file mode in octal to file type string
+sub file_type {
+       my $mode = shift;
+
+       if ($mode !~ m/^[0-7]+$/) {
+               return $mode;
+       } else {
+               $mode = oct $mode;
+       }
+
+       if (S_ISGITLINK($mode)) {
+               return "submodule";
+       } elsif (S_ISDIR($mode & S_IFMT)) {
+               return "directory";
+       } elsif (S_ISLNK($mode)) {
+               return "symlink";
+       } elsif (S_ISREG($mode)) {
+               return "file";
+       } else {
+               return "unknown";
+       }
+}
+
+# convert file mode in octal to file type description string
+sub file_type_long {
+       my $mode = shift;
+
+       if ($mode !~ m/^[0-7]+$/) {
+               return $mode;
+       } else {
+               $mode = oct $mode;
+       }
+
+       if (S_ISGITLINK($mode)) {
+               return "submodule";
+       } elsif (S_ISDIR($mode & S_IFMT)) {
+               return "directory";
+       } elsif (S_ISLNK($mode)) {
+               return "symlink";
+       } elsif (S_ISREG($mode)) {
+               if ($mode & S_IXUSR) {
+                       return "executable";
+               } else {
+                       return "file";
+               };
+       } else {
+               return "unknown";
+       }
+}
+
+
+## ----------------------------------------------------------------------
+## functions returning short HTML fragments, or transforming HTML fragments
+## which don't belong to other sections
+
+# format line of commit message.
+sub format_log_line_html {
+       my $line = shift;
+
+       $line = esc_html($line, -nbsp=>1);
+       $line =~ s{\b([0-9a-fA-F]{8,40})\b}{
+               $cgi->a({-href => href(action=>"object", hash=>$1),
+                                       -class => "text"}, $1);
+       }eg;
+
+       return $line;
+}
+
+# format marker of refs pointing to given object
+
+# the destination action is chosen based on object type and current context:
+# - for annotated tags, we choose the tag view unless it's the current view
+#   already, in which case we go to shortlog view
+# - for other refs, we keep the current view if we're in history, shortlog or
+#   log view, and select shortlog otherwise
+sub format_ref_marker {
+       my ($refs, $id) = @_;
+       my $markers = '';
+
+       if (defined $refs->{$id}) {
+               foreach my $ref (@{$refs->{$id}}) {
+                       # this code exploits the fact that non-lightweight tags are the
+                       # only indirect objects, and that they are the only objects for which
+                       # we want to use tag instead of shortlog as action
+                       my ($type, $name) = qw();
+                       my $indirect = ($ref =~ s/\^\{\}$//);
+                       # e.g. tags/v2.6.11 or heads/next
+                       if ($ref =~ m!^(.*?)s?/(.*)$!) {
+                               $type = $1;
+                               $name = $2;
+                       } else {
+                               $type = "ref";
+                               $name = $ref;
+                       }
+
+                       my $class = $type;
+                       $class .= " indirect" if $indirect;
+
+                       my $dest_action = "shortlog";
+
+                       if ($indirect) {
+                               $dest_action = "tag" unless $action eq "tag";
+                       } elsif ($action =~ /^(history|(short)?log)$/) {
+                               $dest_action = $action;
+                       }
+
+                       my $dest = "";
+                       $dest .= "refs/" unless $ref =~ m!^refs/!;
+                       $dest .= $ref;
+
+                       my $link = $cgi->a({
+                               -href => href(
+                                       action=>$dest_action,
+                                       hash=>$dest
+                               )}, $name);
+
+                       $markers .= " <span class=\"".esc_attr($class)."\" title=\"".esc_attr($ref)."\">" .
+                               $link . "</span>";
+               }
+       }
+
+       if ($markers) {
+               return ' <span class="refs">'. $markers . '</span>';
+       } else {
+               return "";
+       }
+}
+
+# format, perhaps shortened and with markers, title line
+sub format_subject_html {
+       my ($long, $short, $href, $extra) = @_;
+       $extra = '' unless defined($extra);
+
+       if (length($short) < length($long)) {
+               $long =~ s/[[:cntrl:]]/?/g;
+               return $cgi->a({-href => $href, -class => "list subject",
+                               -title => to_utf8($long)},
+                      esc_html($short)) . $extra;
+       } else {
+               return $cgi->a({-href => $href, -class => "list subject"},
+                      esc_html($long)) . $extra;
+       }
+}
+
+# Rather than recomputing the url for an email multiple times, we cache it
+# after the first hit. This gives a visible benefit in views where the avatar
+# for the same email is used repeatedly (e.g. shortlog).
+# The cache is shared by all avatar engines (currently gravatar only), which
+# are free to use it as preferred. Since only one avatar engine is used for any
+# given page, there's no risk for cache conflicts.
+our %avatar_cache = ();
+
+# Compute the picon url for a given email, by using the picon search service over at
+# http://www.cs.indiana.edu/picons/search.html
+sub picon_url {
+       my $email = lc shift;
+       if (!$avatar_cache{$email}) {
+               my ($user, $domain) = split('@', $email);
+               $avatar_cache{$email} =
+                       "//www.cs.indiana.edu/cgi-pub/kinzler/piconsearch.cgi/" .
+                       "$domain/$user/" .
+                       "users+domains+unknown/up/single";
+       }
+       return $avatar_cache{$email};
+}
+
+# Compute the gravatar url for a given email, if it's not in the cache already.
+# Gravatar stores only the part of the URL before the size, since that's the
+# one computationally more expensive. This also allows reuse of the cache for
+# different sizes (for this particular engine).
+sub gravatar_url {
+       my $email = lc shift;
+       my $size = shift;
+       $avatar_cache{$email} ||=
+               "//www.gravatar.com/avatar/" .
+                       Digest::MD5::md5_hex($email) . "?s=";
+       return $avatar_cache{$email} . $size;
+}
+
+# Insert an avatar for the given $email at the given $size if the feature
+# is enabled.
+sub git_get_avatar {
+       my ($email, %opts) = @_;
+       my $pre_white  = ($opts{-pad_before} ? "&nbsp;" : "");
+       my $post_white = ($opts{-pad_after}  ? "&nbsp;" : "");
+       $opts{-size} ||= 'default';
+       my $size = $avatar_size{$opts{-size}} || $avatar_size{'default'};
+       my $url = "";
+       if ($git_avatar eq 'gravatar') {
+               $url = gravatar_url($email, $size);
+       } elsif ($git_avatar eq 'picon') {
+               $url = picon_url($email);
+       }
+       # Other providers can be added by extending the if chain, defining $url
+       # as needed. If no variant puts something in $url, we assume avatars
+       # are completely disabled/unavailable.
+       if ($url) {
+               return $pre_white .
+                      "<img width=\"$size\" " .
+                           "class=\"avatar\" " .
+                           "src=\"".esc_url($url)."\" " .
+                           "alt=\"\" " .
+                      "/>" . $post_white;
+       } else {
+               return "";
+       }
+}
+
+sub format_search_author {
+       my ($author, $searchtype, $displaytext) = @_;
+       my $have_search = gitweb_check_feature('search');
+
+       if ($have_search) {
+               my $performed = "";
+               if ($searchtype eq 'author') {
+                       $performed = "authored";
+               } elsif ($searchtype eq 'committer') {
+                       $performed = "committed";
+               }
+
+               return $cgi->a({-href => href(action=>"search", hash=>$hash,
+                               searchtext=>$author,
+                               searchtype=>$searchtype), class=>"list",
+                               title=>"Search for commits $performed by $author"},
+                               $displaytext);
+
+       } else {
+               return $displaytext;
+       }
+}
+
+# format the author name of the given commit with the given tag
+# the author name is chopped and escaped according to the other
+# optional parameters (see chop_str).
+sub format_author_html {
+       my $tag = shift;
+       my $co = shift;
+       my $author = chop_and_escape_str($co->{'author_name'}, @_);
+       return "<$tag class=\"author\">" .
+              format_search_author($co->{'author_name'}, "author",
+                      git_get_avatar($co->{'author_email'}, -pad_after => 1) .
+                      $author) .
+              "</$tag>";
+}
+
+# format git diff header line, i.e. "diff --(git|combined|cc) ..."
+sub format_git_diff_header_line {
+       my $line = shift;
+       my $diffinfo = shift;
+       my ($from, $to) = @_;
+
+       if ($diffinfo->{'nparents'}) {
+               # combined diff
+               $line =~ s!^(diff (.*?) )"?.*$!$1!;
+               if ($to->{'href'}) {
+                       $line .= $cgi->a({-href => $to->{'href'}, -class => "path"},
+                                        esc_path($to->{'file'}));
+               } else { # file was deleted (no href)
+                       $line .= esc_path($to->{'file'});
+               }
+       } else {
+               # "ordinary" diff
+               $line =~ s!^(diff (.*?) )"?a/.*$!$1!;
+               if ($from->{'href'}) {
+                       $line .= $cgi->a({-href => $from->{'href'}, -class => "path"},
+                                        'a/' . esc_path($from->{'file'}));
+               } else { # file was added (no href)
+                       $line .= 'a/' . esc_path($from->{'file'});
+               }
+               $line .= ' ';
+               if ($to->{'href'}) {
+                       $line .= $cgi->a({-href => $to->{'href'}, -class => "path"},
+                                        'b/' . esc_path($to->{'file'}));
+               } else { # file was deleted
+                       $line .= 'b/' . esc_path($to->{'file'});
+               }
+       }
+
+       return "<div class=\"diff header\">$line</div>\n";
+}
+
+# format extended diff header line, before patch itself
+sub format_extended_diff_header_line {
+       my $line = shift;
+       my $diffinfo = shift;
+       my ($from, $to) = @_;
+
+       # match <path>
+       if ($line =~ s!^((copy|rename) from ).*$!$1! && $from->{'href'}) {
+               $line .= $cgi->a({-href=>$from->{'href'}, -class=>"path"},
+                                      esc_path($from->{'file'}));
+       }
+       if ($line =~ s!^((copy|rename) to ).*$!$1! && $to->{'href'}) {
+               $line .= $cgi->a({-href=>$to->{'href'}, -class=>"path"},
+                                esc_path($to->{'file'}));
+       }
+       # match single <mode>
+       if ($line =~ m/\s(\d{6})$/) {
+               $line .= '<span class="info"> (' .
+                        file_type_long($1) .
+                        ')</span>';
+       }
+       # match <hash>
+       if ($line =~ m/^index [0-9a-fA-F]{40},[0-9a-fA-F]{40}/) {
+               # can match only for combined diff
+               $line = 'index ';
+               for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
+                       if ($from->{'href'}[$i]) {
+                               $line .= $cgi->a({-href=>$from->{'href'}[$i],
+                                                 -class=>"hash"},
+                                                substr($diffinfo->{'from_id'}[$i],0,7));
+                       } else {
+                               $line .= '0' x 7;
+                       }
+                       # separator
+                       $line .= ',' if ($i < $diffinfo->{'nparents'} - 1);
+               }
+               $line .= '..';
+               if ($to->{'href'}) {
+                       $line .= $cgi->a({-href=>$to->{'href'}, -class=>"hash"},
+                                        substr($diffinfo->{'to_id'},0,7));
+               } else {
+                       $line .= '0' x 7;
+               }
+
+       } elsif ($line =~ m/^index [0-9a-fA-F]{40}..[0-9a-fA-F]{40}/) {
+               # can match only for ordinary diff
+               my ($from_link, $to_link);
+               if ($from->{'href'}) {
+                       $from_link = $cgi->a({-href=>$from->{'href'}, -class=>"hash"},
+                                            substr($diffinfo->{'from_id'},0,7));
+               } else {
+                       $from_link = '0' x 7;
+               }
+               if ($to->{'href'}) {
+                       $to_link = $cgi->a({-href=>$to->{'href'}, -class=>"hash"},
+                                          substr($diffinfo->{'to_id'},0,7));
+               } else {
+                       $to_link = '0' x 7;
+               }
+               my ($from_id, $to_id) = ($diffinfo->{'from_id'}, $diffinfo->{'to_id'});
+               $line =~ s!$from_id\.\.$to_id!$from_link..$to_link!;
+       }
+
+       return $line . "<br/>\n";
+}
+
+# format from-file/to-file diff header
+sub format_diff_from_to_header {
+       my ($from_line, $to_line, $diffinfo, $from, $to, @parents) = @_;
+       my $line;
+       my $result = '';
+
+       $line = $from_line;
+       #assert($line =~ m/^---/) if DEBUG;
+       # no extra formatting for "^--- /dev/null"
+       if (! $diffinfo->{'nparents'}) {
+               # ordinary (single parent) diff
+               if ($line =~ m!^--- "?a/!) {
+                       if ($from->{'href'}) {
+                               $line = '--- a/' .
+                                       $cgi->a({-href=>$from->{'href'}, -class=>"path"},
+                                               esc_path($from->{'file'}));
+                       } else {
+                               $line = '--- a/' .
+                                       esc_path($from->{'file'});
+                       }
+               }
+               $result .= qq!<div class="diff from_file">$line</div>\n!;
+
+       } else {
+               # combined diff (merge commit)
+               for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
+                       if ($from->{'href'}[$i]) {
+                               $line = '--- ' .
+                                       $cgi->a({-href=>href(action=>"blobdiff",
+                                                            hash_parent=>$diffinfo->{'from_id'}[$i],
+                                                            hash_parent_base=>$parents[$i],
+                                                            file_parent=>$from->{'file'}[$i],
+                                                            hash=>$diffinfo->{'to_id'},
+                                                            hash_base=>$hash,
+                                                            file_name=>$to->{'file'}),
+                                                -class=>"path",
+                                                -title=>"diff" . ($i+1)},
+                                               $i+1) .
+                                       '/' .
+                                       $cgi->a({-href=>$from->{'href'}[$i], -class=>"path"},
+                                               esc_path($from->{'file'}[$i]));
+                       } else {
+                               $line = '--- /dev/null';
+                       }
+                       $result .= qq!<div class="diff from_file">$line</div>\n!;
+               }
+       }
+
+       $line = $to_line;
+       #assert($line =~ m/^\+\+\+/) if DEBUG;
+       # no extra formatting for "^+++ /dev/null"
+       if ($line =~ m!^\+\+\+ "?b/!) {
+               if ($to->{'href'}) {
+                       $line = '+++ b/' .
+                               $cgi->a({-href=>$to->{'href'}, -class=>"path"},
+                                       esc_path($to->{'file'}));
+               } else {
+                       $line = '+++ b/' .
+                               esc_path($to->{'file'});
+               }
+       }
+       $result .= qq!<div class="diff to_file">$line</div>\n!;
+
+       return $result;
+}
+
+# create note for patch simplified by combined diff
+sub format_diff_cc_simplified {
+       my ($diffinfo, @parents) = @_;
+       my $result = '';
+
+       $result .= "<div class=\"diff header\">" .
+                  "diff --cc ";
+       if (!is_deleted($diffinfo)) {
+               $result .= $cgi->a({-href => href(action=>"blob",
+                                                 hash_base=>$hash,
+                                                 hash=>$diffinfo->{'to_id'},
+                                                 file_name=>$diffinfo->{'to_file'}),
+                                   -class => "path"},
+                                  esc_path($diffinfo->{'to_file'}));
+       } else {
+               $result .= esc_path($diffinfo->{'to_file'});
+       }
+       $result .= "</div>\n" . # class="diff header"
+                  "<div class=\"diff nodifferences\">" .
+                  "Simple merge" .
+                  "</div>\n"; # class="diff nodifferences"
+
+       return $result;
+}
+
+sub diff_line_class {
+       my ($line, $from, $to) = @_;
+
+       # ordinary diff
+       my $num_sign = 1;
+       # combined diff
+       if ($from && $to && ref($from->{'href'}) eq "ARRAY") {
+               $num_sign = scalar @{$from->{'href'}};
+       }
+
+       my @diff_line_classifier = (
+               { regexp => qr/^\@\@{$num_sign} /, class => "chunk_header"},
+               { regexp => qr/^\\/,               class => "incomplete"  },
+               { regexp => qr/^ {$num_sign}/,     class => "ctx" },
+               # classifier for context must come before classifier add/rem,
+               # or we would have to use more complicated regexp, for example
+               # qr/(?= {0,$m}\+)[+ ]{$num_sign}/, where $m = $num_sign - 1;
+               { regexp => qr/^[+ ]{$num_sign}/,   class => "add" },
+               { regexp => qr/^[- ]{$num_sign}/,   class => "rem" },
+       );
+       for my $clsfy (@diff_line_classifier) {
+               return $clsfy->{'class'}
+                       if ($line =~ $clsfy->{'regexp'});
+       }
+
+       # fallback
+       return "";
+}
+
+# assumes that $from and $to are defined and correctly filled,
+# and that $line holds a line of chunk header for unified diff
+sub format_unidiff_chunk_header {
+       my ($line, $from, $to) = @_;
+
+       my ($from_text, $from_start, $from_lines, $to_text, $to_start, $to_lines, $section) =
+               $line =~ m/^\@{2} (-(\d+)(?:,(\d+))?) (\+(\d+)(?:,(\d+))?) \@{2}(.*)$/;
+
+       $from_lines = 0 unless defined $from_lines;
+       $to_lines   = 0 unless defined $to_lines;
+
+       if ($from->{'href'}) {
+               $from_text = $cgi->a({-href=>"$from->{'href'}#l$from_start",
+                                    -class=>"list"}, $from_text);
+       }
+       if ($to->{'href'}) {
+               $to_text   = $cgi->a({-href=>"$to->{'href'}#l$to_start",
+                                    -class=>"list"}, $to_text);
+       }
+       $line = "<span class=\"chunk_info\">@@ $from_text $to_text @@</span>" .
+               "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
+       return $line;
+}
+
+# assumes that $from and $to are defined and correctly filled,
+# and that $line holds a line of chunk header for combined diff
+sub format_cc_diff_chunk_header {
+       my ($line, $from, $to) = @_;
+
+       my ($prefix, $ranges, $section) = $line =~ m/^(\@+) (.*?) \@+(.*)$/;
+       my (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines);
+
+       @from_text = split(' ', $ranges);
+       for (my $i = 0; $i < @from_text; ++$i) {
+               ($from_start[$i], $from_nlines[$i]) =
+                       (split(',', substr($from_text[$i], 1)), 0);
+       }
+
+       $to_text   = pop @from_text;
+       $to_start  = pop @from_start;
+       $to_nlines = pop @from_nlines;
+
+       $line = "<span class=\"chunk_info\">$prefix ";
+       for (my $i = 0; $i < @from_text; ++$i) {
+               if ($from->{'href'}[$i]) {
+                       $line .= $cgi->a({-href=>"$from->{'href'}[$i]#l$from_start[$i]",
+                                         -class=>"list"}, $from_text[$i]);
+               } else {
+                       $line .= $from_text[$i];
+               }
+               $line .= " ";
+       }
+       if ($to->{'href'}) {
+               $line .= $cgi->a({-href=>"$to->{'href'}#l$to_start",
+                                 -class=>"list"}, $to_text);
+       } else {
+               $line .= $to_text;
+       }
+       $line .= " $prefix</span>" .
+                "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
+       return $line;
+}
+
+# process patch (diff) line (not to be used for diff headers),
+# returning HTML-formatted (but not wrapped) line.
+# If the line is passed as a reference, it is treated as HTML and not
+# esc_html()'ed.
+sub format_diff_line {
+       my ($line, $diff_class, $from, $to) = @_;
+
+       if (ref($line)) {
+               $line = $$line;
+       } else {
+               chomp $line;
+               $line = untabify($line);
+
+               if ($from && $to && $line =~ m/^\@{2} /) {
+                       $line = format_unidiff_chunk_header($line, $from, $to);
+               } elsif ($from && $to && $line =~ m/^\@{3}/) {
+                       $line = format_cc_diff_chunk_header($line, $from, $to);
+               } else {
+                       $line = esc_html($line, -nbsp=>1);
+               }
+       }
+
+       my $diff_classes = "diff";
+       $diff_classes .= " $diff_class" if ($diff_class);
+       $line = "<div class=\"$diff_classes\">$line</div>\n";
+
+       return $line;
+}
+
+# Generates undef or something like "_snapshot_" or "snapshot (_tbz2_ _zip_)",
+# linked.  Pass the hash of the tree/commit to snapshot.
+sub format_snapshot_links {
+       my ($hash) = @_;
+       my $num_fmts = @snapshot_fmts;
+       if ($num_fmts > 1) {
+               # A parenthesized list of links bearing format names.
+               # e.g. "snapshot (_tar.gz_ _zip_)"
+               return "snapshot (" . join(' ', map
+                       $cgi->a({
+                               -href => href(
+                                       action=>"snapshot",
+                                       hash=>$hash,
+                                       snapshot_format=>$_
+                               )
+                       }, $known_snapshot_formats{$_}{'display'})
+               , @snapshot_fmts) . ")";
+       } elsif ($num_fmts == 1) {
+               # A single "snapshot" link whose tooltip bears the format name.
+               # i.e. "_snapshot_"
+               my ($fmt) = @snapshot_fmts;
+               return
+                       $cgi->a({
+                               -href => href(
+                                       action=>"snapshot",
+                                       hash=>$hash,
+                                       snapshot_format=>$fmt
+                               ),
+                               -title => "in format: $known_snapshot_formats{$fmt}{'display'}"
+                       }, "snapshot");
+       } else { # $num_fmts == 0
+               return undef;
+       }
+}
+
+## ......................................................................
+## functions returning values to be passed, perhaps after some
+## transformation, to other functions; e.g. returning arguments to href()
+
+# returns hash to be passed to href to generate gitweb URL
+# in -title key it returns description of link
+sub get_feed_info {
+       my $format = shift || 'Atom';
+       my %res = (action => lc($format));
+       my $matched_ref = 0;
+
+       # feed links are possible only for project views
+       return unless (defined $project);
+       # some views should link to OPML, or to generic project feed,
+       # or don't have specific feed yet (so they should use generic)
+       return if (!$action || $action =~ /^(?:tags|heads|forks|tag|search)$/x);
+
+       my $branch = undef;
+       # branches refs uses 'refs/' + $get_branch_refs()[x] + '/' prefix
+       # (fullname) to differentiate from tag links; this also makes
+       # possible to detect branch links
+       for my $ref (get_branch_refs()) {
+               if ((defined $hash_base && $hash_base =~ m!^refs/\Q$ref\E/(.*)$!) ||
+                   (defined $hash      && $hash      =~ m!^refs/\Q$ref\E/(.*)$!)) {
+                       $branch = $1;
+                       $matched_ref = $ref;
+                       last;
+               }
+       }
+       # find log type for feed description (title)
+       my $type = 'log';
+       if (defined $file_name) {
+               $type  = "history of $file_name";
+               $type .= "/" if ($action eq 'tree');
+               $type .= " on '$branch'" if (defined $branch);
+       } else {
+               $type = "log of $branch" if (defined $branch);
+       }
+
+       $res{-title} = $type;
+       $res{'hash'} = (defined $branch ? "refs/$matched_ref/$branch" : undef);
+       $res{'file_name'} = $file_name;
+
+       return %res;
+}
+
+## ----------------------------------------------------------------------
+## git utility subroutines, invoking git commands
+
+# returns path to the core git executable and the --git-dir parameter as list
+sub git_cmd {
+       $number_of_git_cmds++;
+       return $GIT, '--git-dir='.$git_dir;
+}
+
+# quote the given arguments for passing them to the shell
+# quote_command("command", "arg 1", "arg with ' and ! characters")
+# => "'command' 'arg 1' 'arg with '\'' and '\!' characters'"
+# Try to avoid using this function wherever possible.
+sub quote_command {
+       return join(' ',
+               map { my $a = $_; $a =~ s/(['!])/'\\$1'/g; "'$a'" } @_ );
+}
+
+# get HEAD ref of given project as hash
+sub git_get_head_hash {
+       return git_get_full_hash(shift, 'HEAD');
+}
+
+sub git_get_full_hash {
+       return git_get_hash(@_);
+}
+
+sub git_get_short_hash {
+       return git_get_hash(@_, '--short=7');
+}
+
+sub git_get_hash {
+       my ($project, $hash, @options) = @_;
+       my $o_git_dir = $git_dir;
+       my $retval = undef;
+       $git_dir = "$projectroot/$project";
+       if (open my $fd, '-|', git_cmd(), 'rev-parse',
+           '--verify', '-q', @options, $hash) {
+               $retval = <$fd>;
+               chomp $retval if defined $retval;
+               close $fd;
+       }
+       if (defined $o_git_dir) {
+               $git_dir = $o_git_dir;
+       }
+       return $retval;
+}
+
+# get type of given object
+sub git_get_type {
+       my $hash = shift;
+
+       open my $fd, "-|", git_cmd(), "cat-file", '-t', $hash or return;
+       my $type = <$fd>;
+       close $fd or return;
+       chomp $type;
+       return $type;
+}
+
+# repository configuration
+our $config_file = '';
+our %config;
+
+# store multiple values for single key as anonymous array reference
+# single values stored directly in the hash, not as [ <value> ]
+sub hash_set_multi {
+       my ($hash, $key, $value) = @_;
+
+       if (!exists $hash->{$key}) {
+               $hash->{$key} = $value;
+       } elsif (!ref $hash->{$key}) {
+               $hash->{$key} = [ $hash->{$key}, $value ];
+       } else {
+               push @{$hash->{$key}}, $value;
+       }
+}
+
+# return hash of git project configuration
+# optionally limited to some section, e.g. 'gitweb'
+sub git_parse_project_config {
+       my $section_regexp = shift;
+       my %config;
+
+       local $/ = "\0";
+
+       open my $fh, "-|", git_cmd(), "config", '-z', '-l',
+               or return;
+
+       while (my $keyval = <$fh>) {
+               chomp $keyval;
+               my ($key, $value) = split(/\n/, $keyval, 2);
+
+               hash_set_multi(\%config, $key, $value)
+                       if (!defined $section_regexp || $key =~ /^(?:$section_regexp)\./o);
+       }
+       close $fh;
+
+       return %config;
+}
+
+# convert config value to boolean: 'true' or 'false'
+# no value, number > 0, 'true' and 'yes' values are true
+# rest of values are treated as false (never as error)
+sub config_to_bool {
+       my $val = shift;
+
+       return 1 if !defined $val;             # section.key
+
+       # strip leading and trailing whitespace
+       $val =~ s/^\s+//;
+       $val =~ s/\s+$//;
+
+       return (($val =~ /^\d+$/ && $val) ||   # section.key = 1
+               ($val =~ /^(?:true|yes)$/i));  # section.key = true
+}
+
+# convert config value to simple decimal number
+# an optional value suffix of 'k', 'm', or 'g' will cause the value
+# to be multiplied by 1024, 1048576, or 1073741824
+sub config_to_int {
+       my $val = shift;
+
+       # strip leading and trailing whitespace
+       $val =~ s/^\s+//;
+       $val =~ s/\s+$//;
+
+       if (my ($num, $unit) = ($val =~ /^([0-9]*)([kmg])$/i)) {
+               $unit = lc($unit);
+               # unknown unit is treated as 1
+               return $num * ($unit eq 'g' ? 1073741824 :
+                              $unit eq 'm' ?    1048576 :
+                              $unit eq 'k' ?       1024 : 1);
+       }
+       return $val;
+}
+
+# convert config value to array reference, if needed
+sub config_to_multi {
+       my $val = shift;
+
+       return ref($val) ? $val : (defined($val) ? [ $val ] : []);
+}
+
+sub git_get_project_config {
+       my ($key, $type) = @_;
+
+       return unless defined $git_dir;
+
+       # key sanity check
+       return unless ($key);
+       # only subsection, if exists, is case sensitive,
+       # and not lowercased by 'git config -z -l'
+       if (my ($hi, $mi, $lo) = ($key =~ /^([^.]*)\.(.*)\.([^.]*)$/)) {
+               $lo =~ s/_//g;
+               $key = join(".", lc($hi), $mi, lc($lo));
+               return if ($lo =~ /\W/ || $hi =~ /\W/);
+       } else {
+               $key = lc($key);
+               $key =~ s/_//g;
+               return if ($key =~ /\W/);
+       }
+       $key =~ s/^gitweb\.//;
+
+       # type sanity check
+       if (defined $type) {
+               $type =~ s/^--//;
+               $type = undef
+                       unless ($type eq 'bool' || $type eq 'int');
+       }
+
+       # get config
+       if (!defined $config_file ||
+           $config_file ne "$git_dir/config") {
+               %config = git_parse_project_config('gitweb');
+               $config_file = "$git_dir/config";
+       }
+
+       # check if config variable (key) exists
+       return unless exists $config{"gitweb.$key"};
+
+       # ensure given type
+       if (!defined $type) {
+               return $config{"gitweb.$key"};
+       } elsif ($type eq 'bool') {
+               # backward compatibility: 'git config --bool' returns true/false
+               return config_to_bool($config{"gitweb.$key"}) ? 'true' : 'false';
+       } elsif ($type eq 'int') {
+               return config_to_int($config{"gitweb.$key"});
+       }
+       return $config{"gitweb.$key"};
+}
+
+# get hash of given path at given ref
+sub git_get_hash_by_path {
+       my $base = shift;
+       my $path = shift || return undef;
+       my $type = shift;
+
+       $path =~ s,/+$,,;
+
+       open my $fd, "-|", git_cmd(), "ls-tree", $base, "--", $path
+               or die_error(500, "Open git-ls-tree failed");
+       my $line = <$fd>;
+       close $fd or return undef;
+
+       if (!defined $line) {
+               # there is no tree or hash given by $path at $base
+               return undef;
+       }
+
+       #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa  panic.c'
+       $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/;
+       if (defined $type && $type ne $2) {
+               # type doesn't match
+               return undef;
+       }
+       return $3;
+}
+
+# get path of entry with given hash at given tree-ish (ref)
+# used to get 'from' filename for combined diff (merge commit) for renames
+sub git_get_path_by_hash {
+       my $base = shift || return;
+       my $hash = shift || return;
+
+       local $/ = "\0";
+
+       open my $fd, "-|", git_cmd(), "ls-tree", '-r', '-t', '-z', $base
+               or return undef;
+       while (my $line = <$fd>) {
+               chomp $line;
+
+               #'040000 tree 595596a6a9117ddba9fe379b6b012b558bac8423  gitweb'
+               #'100644 blob e02e90f0429be0d2a69b76571101f20b8f75530f  gitweb/README'
+               if ($line =~ m/(?:[0-9]+) (?:.+) $hash\t(.+)$/) {
+                       close $fd;
+                       return $1;
+               }
+       }
+       close $fd;
+       return undef;
+}
+
+## ......................................................................
+## git utility functions, directly accessing git repository
+
+# get the value of config variable either from file named as the variable
+# itself in the repository ($GIT_DIR/$name file), or from gitweb.$name
+# configuration variable in the repository config file.
+sub git_get_file_or_project_config {
+       my ($path, $name) = @_;
+
+       $git_dir = "$projectroot/$path";
+       open my $fd, '<', "$git_dir/$name"
+               or return git_get_project_config($name);
+       my $conf = <$fd>;
+       close $fd;
+       if (defined $conf) {
+               chomp $conf;
+       }
+       return $conf;
+}
+
+sub git_get_project_description {
+       my $path = shift;
+       return git_get_file_or_project_config($path, 'description');
+}
+
+sub git_get_project_category {
+       my $path = shift;
+       return git_get_file_or_project_config($path, 'category');
+}
+
+
+# supported formats:
+# * $GIT_DIR/ctags/<tagname> file (in 'ctags' subdirectory)
+#   - if its contents is a number, use it as tag weight,
+#   - otherwise add a tag with weight 1
+# * $GIT_DIR/ctags file, each line is a tag (with weight 1)
+#   the same value multiple times increases tag weight
+# * `gitweb.ctag' multi-valued repo config variable
+sub git_get_project_ctags {
+       my $project = shift;
+       my $ctags = {};
+
+       $git_dir = "$projectroot/$project";
+       if (opendir my $dh, "$git_dir/ctags") {
+               my @files = grep { -f $_ } map { "$git_dir/ctags/$_" } readdir($dh);
+               foreach my $tagfile (@files) {
+                       open my $ct, '<', $tagfile
+                               or next;
+                       my $val = <$ct>;
+                       chomp $val if $val;
+                       close $ct;
+
+                       (my $ctag = $tagfile) =~ s#.*/##;
+                       if ($val =~ /^\d+$/) {
+                               $ctags->{$ctag} = $val;
+                       } else {
+                               $ctags->{$ctag} = 1;
+                       }
+               }
+               closedir $dh;
+
+       } elsif (open my $fh, '<', "$git_dir/ctags") {
+               while (my $line = <$fh>) {
+                       chomp $line;
+                       $ctags->{$line}++ if $line;
+               }
+               close $fh;
+
+       } else {
+               my $taglist = config_to_multi(git_get_project_config('ctag'));
+               foreach my $tag (@$taglist) {
+                       $ctags->{$tag}++;
+               }
+       }
+
+       return $ctags;
+}
+
+# return hash, where keys are content tags ('ctags'),
+# and values are sum of weights of given tag in every project
+sub git_gather_all_ctags {
+       my $projects = shift;
+       my $ctags = {};
+
+       foreach my $p (@$projects) {
+               foreach my $ct (keys %{$p->{'ctags'}}) {
+                       $ctags->{$ct} += $p->{'ctags'}->{$ct};
+               }
+       }
+
+       return $ctags;
+}
+
+sub git_populate_project_tagcloud {
+       my $ctags = shift;
+
+       # First, merge different-cased tags; tags vote on casing
+       my %ctags_lc;
+       foreach (keys %$ctags) {
+               $ctags_lc{lc $_}->{count} += $ctags->{$_};
+               if (not $ctags_lc{lc $_}->{topcount}
+                   or $ctags_lc{lc $_}->{topcount} < $ctags->{$_}) {
+                       $ctags_lc{lc $_}->{topcount} = $ctags->{$_};
+                       $ctags_lc{lc $_}->{topname} = $_;
+               }
+       }
+
+       my $cloud;
+       my $matched = $input_params{'ctag'};
+       if (eval { require HTML::TagCloud; 1; }) {
+               $cloud = HTML::TagCloud->new;
+               foreach my $ctag (sort keys %ctags_lc) {
+                       # Pad the title with spaces so that the cloud looks
+                       # less crammed.
+                       my $title = esc_html($ctags_lc{$ctag}->{topname});
+                       $title =~ s/ /&nbsp;/g;
+                       $title =~ s/^/&nbsp;/g;
+                       $title =~ s/$/&nbsp;/g;
+                       if (defined $matched && $matched eq $ctag) {
+                               $title = qq(<span class="match">$title</span>);
+                       }
+                       $cloud->add($title, href(project=>undef, ctag=>$ctag),
+                                   $ctags_lc{$ctag}->{count});
+               }
+       } else {
+               $cloud = {};
+               foreach my $ctag (keys %ctags_lc) {
+                       my $title = esc_html($ctags_lc{$ctag}->{topname}, -nbsp=>1);
+                       if (defined $matched && $matched eq $ctag) {
+                               $title = qq(<span class="match">$title</span>);
+                       }
+                       $cloud->{$ctag}{count} = $ctags_lc{$ctag}->{count};
+                       $cloud->{$ctag}{ctag} =
+                               $cgi->a({-href=>href(project=>undef, ctag=>$ctag)}, $title);
+               }
+       }
+       return $cloud;
+}
+
+sub git_show_project_tagcloud {
+       my ($cloud, $count) = @_;
+       if (ref $cloud eq 'HTML::TagCloud') {
+               return $cloud->html_and_css($count);
+       } else {
+               my @tags = sort { $cloud->{$a}->{'count'} <=> $cloud->{$b}->{'count'} } keys %$cloud;
+               return
+                       '<div id="htmltagcloud"'.($project ? '' : ' align="center"').'>' .
+                       join (', ', map {
+                               $cloud->{$_}->{'ctag'}
+                       } splice(@tags, 0, $count)) .
+                       '</div>';
+       }
+}
+
+sub git_get_project_url_list {
+       my $path = shift;
+
+       $git_dir = "$projectroot/$path";
+       open my $fd, '<', "$git_dir/cloneurl"
+               or return wantarray ?
+               @{ config_to_multi(git_get_project_config('url')) } :
+                  config_to_multi(git_get_project_config('url'));
+       my @git_project_url_list = map { chomp; $_ } <$fd>;
+       close $fd;
+
+       return wantarray ? @git_project_url_list : \@git_project_url_list;
+}
+
+sub git_get_projects_list {
+       my $filter = shift || '';
+       my $paranoid = shift;
+       my @list;
+
+       if (-d $projects_list) {
+               # search in directory
+               my $dir = $projects_list;
+               # remove the trailing "/"
+               $dir =~ s!/+$!!;
+               my $pfxlen = length("$dir");
+               my $pfxdepth = ($dir =~ tr!/!!);
+               # when filtering, search only given subdirectory
+               if ($filter && !$paranoid) {
+                       $dir .= "/$filter";
+                       $dir =~ s!/+$!!;
+               }
+
+               File::Find::find({
+                       follow_fast => 1, # follow symbolic links
+                       follow_skip => 2, # ignore duplicates
+                       dangling_symlinks => 0, # ignore dangling symlinks, silently
+                       wanted => sub {
+                               # global variables
+                               our $project_maxdepth;
+                               our $projectroot;
+                               # skip project-list toplevel, if we get it.
+                               return if (m!^[/.]$!);
+                               # only directories can be git repositories
+                               return unless (-d $_);
+                               # don't traverse too deep (Find is super slow on os x)
+                               # $project_maxdepth excludes depth of $projectroot
+                               if (($File::Find::name =~ tr!/!!) - $pfxdepth > $project_maxdepth) {
+                                       $File::Find::prune = 1;
+                                       return;
+                               }
+
+                               my $path = substr($File::Find::name, $pfxlen + 1);
+                               # paranoidly only filter here
+                               if ($paranoid && $filter && $path !~ m!^\Q$filter\E/!) {
+                                       next;
+                               }
+                               # we check related file in $projectroot
+                               if (check_export_ok("$projectroot/$path")) {
+                                       push @list, { path => $path };
+                                       $File::Find::prune = 1;
+                               }
+                       },
+               }, "$dir");
+
+       } elsif (-f $projects_list) {
+               # read from file(url-encoded):
+               # 'git%2Fgit.git Linus+Torvalds'
+               # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
+               # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
+               open my $fd, '<', $projects_list or return;
+       PROJECT:
+               while (my $line = <$fd>) {
+                       chomp $line;
+                       my ($path, $owner) = split ' ', $line;
+                       $path = unescape($path);
+                       $owner = unescape($owner);
+                       if (!defined $path) {
+                               next;
+                       }
+                       # if $filter is rpovided, check if $path begins with $filter
+                       if ($filter && $path !~ m!^\Q$filter\E/!) {
+                               next;
+                       }
+                       if (check_export_ok("$projectroot/$path")) {
+                               my $pr = {
+                                       path => $path
+                               };
+                               if ($owner) {
+                                       $pr->{'owner'} = to_utf8($owner);
+                               }
+                               push @list, $pr;
+                       }
+               }
+               close $fd;
+       }
+       return @list;
+}
+
+# written with help of Tree::Trie module (Perl Artistic License, GPL compatibile)
+# as side effects it sets 'forks' field to list of forks for forked projects
+sub filter_forks_from_projects_list {
+       my $projects = shift;
+
+       my %trie; # prefix tree of directories (path components)
+       # generate trie out of those directories that might contain forks
+       foreach my $pr (@$projects) {
+               my $path = $pr->{'path'};
+               $path =~ s/\.git$//;      # forks of 'repo.git' are in 'repo/' directory
+               next if ($path =~ m!/$!); # skip non-bare repositories, e.g. 'repo/.git'
+               next unless ($path);      # skip '.git' repository: tests, git-instaweb
+               next unless (-d "$projectroot/$path"); # containing directory exists
+               $pr->{'forks'} = [];      # there can be 0 or more forks of project
+
+               # add to trie
+               my @dirs = split('/', $path);
+               # walk the trie, until either runs out of components or out of trie
+               my $ref = \%trie;
+               while (scalar @dirs &&
+                      exists($ref->{$dirs[0]})) {
+                       $ref = $ref->{shift @dirs};
+               }
+               # create rest of trie structure from rest of components
+               foreach my $dir (@dirs) {
+                       $ref = $ref->{$dir} = {};
+               }
+               # create end marker, store $pr as a data
+               $ref->{''} = $pr if (!exists $ref->{''});
+       }
+
+       # filter out forks, by finding shortest prefix match for paths
+       my @filtered;
+ PROJECT:
+       foreach my $pr (@$projects) {
+               # trie lookup
+               my $ref = \%trie;
+       DIR:
+               foreach my $dir (split('/', $pr->{'path'})) {
+                       if (exists $ref->{''}) {
+                               # found [shortest] prefix, is a fork - skip it
+                               push @{$ref->{''}{'forks'}}, $pr;
+                               next PROJECT;
+                       }
+                       if (!exists $ref->{$dir}) {
+                               # not in trie, cannot have prefix, not a fork
+                               push @filtered, $pr;
+                               next PROJECT;
+                       }
+                       # If the dir is there, we just walk one step down the trie.
+                       $ref = $ref->{$dir};
+               }
+               # we ran out of trie
+               # (shouldn't happen: it's either no match, or end marker)
+               push @filtered, $pr;
+       }
+
+       return @filtered;
+}
+
+# note: fill_project_list_info must be run first,
+# for 'descr_long' and 'ctags' to be filled
+sub search_projects_list {
+       my ($projlist, %opts) = @_;
+       my $tagfilter  = $opts{'tagfilter'};
+       my $search_re = $opts{'search_regexp'};
+
+       return @$projlist
+               unless ($tagfilter || $search_re);
+
+       # searching projects require filling to be run before it;
+       fill_project_list_info($projlist,
+                              $tagfilter  ? 'ctags' : (),
+                              $search_re ? ('path', 'descr') : ());
+       my @projects;
+ PROJECT:
+       foreach my $pr (@$projlist) {
+
+               if ($tagfilter) {
+                       next unless ref($pr->{'ctags'}) eq 'HASH';
+                       next unless
+                               grep { lc($_) eq lc($tagfilter) } keys %{$pr->{'ctags'}};
+               }
+
+               if ($search_re) {
+                       next unless
+                               $pr->{'path'} =~ /$search_re/ ||
+                               $pr->{'descr_long'} =~ /$search_re/;
+               }
+
+               push @projects, $pr;
+       }
+
+       return @projects;
+}
+
+our $gitweb_project_owner = undef;
+sub git_get_project_list_from_file {
+
+       return if (defined $gitweb_project_owner);
+
+       $gitweb_project_owner = {};
+       # read from file (url-encoded):
+       # 'git%2Fgit.git Linus+Torvalds'
+       # 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
+       # 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
+       if (-f $projects_list) {
+               open(my $fd, '<', $projects_list);
+               while (my $line = <$fd>) {
+                       chomp $line;
+                       my ($pr, $ow) = split ' ', $line;
+                       $pr = unescape($pr);
+                       $ow = unescape($ow);
+                       $gitweb_project_owner->{$pr} = to_utf8($ow);
+               }
+               close $fd;
+       }
+}
+
+sub git_get_project_owner {
+       my $project = shift;
+       my $owner;
+
+       return undef unless $project;
+       $git_dir = "$projectroot/$project";
+
+       if (!defined $gitweb_project_owner) {
+               git_get_project_list_from_file();
+       }
+
+       if (exists $gitweb_project_owner->{$project}) {
+               $owner = $gitweb_project_owner->{$project};
+       }
+       if (!defined $owner){
+               $owner = git_get_project_config('owner');
+       }
+       if (!defined $owner) {
+               $owner = get_file_owner("$git_dir");
+       }
+
+       return $owner;
+}
+
+sub git_get_last_activity {
+       my ($path) = @_;
+       my $fd;
+
+       $git_dir = "$projectroot/$path";
+       open($fd, "-|", git_cmd(), 'for-each-ref',
+            '--format=%(committer)',
+            '--sort=-committerdate',
+            '--count=1',
+            map { "refs/$_" } get_branch_refs ()) or return;
+       my $most_recent = <$fd>;
+       close $fd or return;
+       if (defined $most_recent &&
+           $most_recent =~ / (\d+) [-+][01]\d\d\d$/) {
+               my $timestamp = $1;
+               my $age = time - $timestamp;
+               return ($age, age_string($age));
+       }
+       return (undef, undef);
+}
+
+# Implementation note: when a single remote is wanted, we cannot use 'git
+# remote show -n' because that command always work (assuming it's a remote URL
+# if it's not defined), and we cannot use 'git remote show' because that would
+# try to make a network roundtrip. So the only way to find if that particular
+# remote is defined is to walk the list provided by 'git remote -v' and stop if
+# and when we find what we want.
+sub git_get_remotes_list {
+       my $wanted = shift;
+       my %remotes = ();
+
+       open my $fd, '-|' , git_cmd(), 'remote', '-v';
+       return unless $fd;
+       while (my $remote = <$fd>) {
+               chomp $remote;
+               $remote =~ s!\t(.*?)\s+\((\w+)\)$!!;
+               next if $wanted and not $remote eq $wanted;
+               my ($url, $key) = ($1, $2);
+
+               $remotes{$remote} ||= { 'heads' => () };
+               $remotes{$remote}{$key} = $url;
+       }
+       close $fd or return;
+       return wantarray ? %remotes : \%remotes;
+}
+
+# Takes a hash of remotes as first parameter and fills it by adding the
+# available remote heads for each of the indicated remotes.
+sub fill_remote_heads {
+       my $remotes = shift;
+       my @heads = map { "remotes/$_" } keys %$remotes;
+       my @remoteheads = git_get_heads_list(undef, @heads);
+       foreach my $remote (keys %$remotes) {
+               $remotes->{$remote}{'heads'} = [ grep {
+                       $_->{'name'} =~ s!^$remote/!!
+                       } @remoteheads ];
+       }
+}
+
+sub git_get_references {
+       my $type = shift || "";
+       my %refs;
+       # 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11
+       # c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{}
+       open my $fd, "-|", git_cmd(), "show-ref", "--dereference",
+               ($type ? ("--", "refs/$type") : ()) # use -- <pattern> if $type
+               or return;
+
+       while (my $line = <$fd>) {
+               chomp $line;
+               if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type.*)$!) {
+                       if (defined $refs{$1}) {
+                               push @{$refs{$1}}, $2;
+                       } else {
+                               $refs{$1} = [ $2 ];
+                       }
+               }
+       }
+       close $fd or return;
+       return \%refs;
+}
+
+sub git_get_rev_name_tags {
+       my $hash = shift || return undef;
+
+       open my $fd, "-|", git_cmd(), "name-rev", "--tags", $hash
+               or return;
+       my $name_rev = <$fd>;
+       close $fd;
+
+       if ($name_rev =~ m|^$hash tags/(.*)$|) {
+               return $1;
+       } else {
+               # catches also '$hash undefined' output
+               return undef;
+       }
+}
+
+## ----------------------------------------------------------------------
+## parse to hash functions
+
+sub parse_date {
+       my $epoch = shift;
+       my $tz = shift || "-0000";
+
+       my %date;
+       my @months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
+       my @days = ("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat");
+       my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($epoch);
+       $date{'hour'} = $hour;
+       $date{'minute'} = $min;
+       $date{'mday'} = $mday;
+       $date{'day'} = $days[$wday];
+       $date{'month'} = $months[$mon];
+       $date{'rfc2822'}   = sprintf "%s, %d %s %4d %02d:%02d:%02d +0000",
+                            $days[$wday], $mday, $months[$mon], 1900+$year, $hour ,$min, $sec;
+       $date{'mday-time'} = sprintf "%d %s %02d:%02d",
+                            $mday, $months[$mon], $hour ,$min;
+       $date{'iso-8601'}  = sprintf "%04d-%02d-%02dT%02d:%02d:%02dZ",
+                            1900+$year, 1+$mon, $mday, $hour ,$min, $sec;
+
+       my ($tz_sign, $tz_hour, $tz_min) =
+               ($tz =~ m/^([-+])(\d\d)(\d\d)$/);
+       $tz_sign = ($tz_sign eq '-' ? -1 : +1);
+       my $local = $epoch + $tz_sign*((($tz_hour*60) + $tz_min)*60);
+       ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($local);
+       $date{'hour_local'} = $hour;
+       $date{'minute_local'} = $min;
+       $date{'tz_local'} = $tz;
+       $date{'iso-tz'} = sprintf("%04d-%02d-%02d %02d:%02d:%02d %s",
+                                 1900+$year, $mon+1, $mday,
+                                 $hour, $min, $sec, $tz);
+       return %date;
+}
+
+sub parse_tag {
+       my $tag_id = shift;
+       my %tag;
+       my @comment;
+
+       open my $fd, "-|", git_cmd(), "cat-file", "tag", $tag_id or return;
+       $tag{'id'} = $tag_id;
+       while (my $line = <$fd>) {
+               chomp $line;
+               if ($line =~ m/^object ([0-9a-fA-F]{40})$/) {
+                       $tag{'object'} = $1;
+               } elsif ($line =~ m/^type (.+)$/) {
+                       $tag{'type'} = $1;
+               } elsif ($line =~ m/^tag (.+)$/) {
+                       $tag{'name'} = $1;
+               } elsif ($line =~ m/^tagger (.*) ([0-9]+) (.*)$/) {
+                       $tag{'author'} = $1;
+                       $tag{'author_epoch'} = $2;
+                       $tag{'author_tz'} = $3;
+                       if ($tag{'author'} =~ m/^([^<]+) <([^>]*)>/) {
+                               $tag{'author_name'}  = $1;
+                               $tag{'author_email'} = $2;
+                       } else {
+                               $tag{'author_name'} = $tag{'author'};
+                       }
+               } elsif ($line =~ m/--BEGIN/) {
+                       push @comment, $line;
+                       last;
+               } elsif ($line eq "") {
+                       last;
+               }
+       }
+       push @comment, <$fd>;
+       $tag{'comment'} = \@comment;
+       close $fd or return;
+       if (!defined $tag{'name'}) {
+               return
+       };
+       return %tag
+}
+
+sub parse_commit_text {
+       my ($commit_text, $withparents) = @_;
+       my @commit_lines = split '\n', $commit_text;
+       my %co;
+
+       pop @commit_lines; # Remove '\0'
+
+       if (! @commit_lines) {
+               return;
+       }
+
+       my $header = shift @commit_lines;
+       if ($header !~ m/^[0-9a-fA-F]{40}/) {
+               return;
+       }
+       ($co{'id'}, my @parents) = split ' ', $header;
+       while (my $line = shift @commit_lines) {
+               last if $line eq "\n";
+               if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) {
+                       $co{'tree'} = $1;
+               } elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) {
+                       push @parents, $1;
+               } elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
+                       $co{'author'} = to_utf8($1);
+                       $co{'author_epoch'} = $2;
+                       $co{'author_tz'} = $3;
+                       if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) {
+                               $co{'author_name'}  = $1;
+                               $co{'author_email'} = $2;
+                       } else {
+                               $co{'author_name'} = $co{'author'};
+                       }
+               } elsif ($line =~ m/^committer (.*) ([0-9]+) (.*)$/) {
+                       $co{'committer'} = to_utf8($1);
+                       $co{'committer_epoch'} = $2;
+                       $co{'committer_tz'} = $3;
+                       if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) {
+                               $co{'committer_name'}  = $1;
+                               $co{'committer_email'} = $2;
+                       } else {
+                               $co{'committer_name'} = $co{'committer'};
+                       }
+               }
+       }
+       if (!defined $co{'tree'}) {
+               return;
+       };
+       $co{'parents'} = \@parents;
+       $co{'parent'} = $parents[0];
+
+       foreach my $title (@commit_lines) {
+               $title =~ s/^    //;
+               if ($title ne "") {
+                       $co{'title'} = chop_str($title, 80, 5);
+                       # remove leading stuff of merges to make the interesting part visible
+                       if (length($title) > 50) {
+                               $title =~ s/^Automatic //;
+                               $title =~ s/^merge (of|with) /Merge ... /i;
+                               if (length($title) > 50) {
+                                       $title =~ s/(http|rsync):\/\///;
+                               }
+                               if (length($title) > 50) {
+                                       $title =~ s/(master|www|rsync)\.//;
+                               }
+                               if (length($title) > 50) {
+                                       $title =~ s/kernel.org:?//;
+                               }
+                               if (length($title) > 50) {
+                                       $title =~ s/\/pub\/scm//;
+                               }
+                       }
+                       $co{'title_short'} = chop_str($title, 50, 5);
+                       last;
+               }
+       }
+       if (! defined $co{'title'} || $co{'title'} eq "") {
+               $co{'title'} = $co{'title_short'} = '(no commit message)';
+       }
+       # remove added spaces
+       foreach my $line (@commit_lines) {
+               $line =~ s/^    //;
+       }
+       $co{'comment'} = \@commit_lines;
+
+       my $age = time - $co{'committer_epoch'};
+       $co{'age'} = $age;
+       $co{'age_string'} = age_string($age);
+       my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday) = gmtime($co{'committer_epoch'});
+       if ($age > 60*60*24*7*2) {
+               $co{'age_string_date'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday;
+               $co{'age_string_age'} = $co{'age_string'};
+       } else {
+               $co{'age_string_date'} = $co{'age_string'};
+               $co{'age_string_age'} = sprintf "%4i-%02u-%02i", 1900 + $year, $mon+1, $mday;
+       }
+       return %co;
+}
+
+sub parse_commit {
+       my ($commit_id) = @_;
+       my %co;
+
+       local $/ = "\0";
+
+       open my $fd, "-|", git_cmd(), "rev-list",
+               "--parents",
+               "--header",
+               "--max-count=1",
+               $commit_id,
+               "--",
+               or die_error(500, "Open git-rev-list failed");
+       %co = parse_commit_text(<$fd>, 1);
+       close $fd;
+
+       return %co;
+}
+
+sub parse_commits {
+       my ($commit_id, $maxcount, $skip, $filename, @args) = @_;
+       my @cos;
+
+       $maxcount ||= 1;
+       $skip ||= 0;
+
+       local $/ = "\0";
+
+       open my $fd, "-|", git_cmd(), "rev-list",
+               "--header",
+               @args,
+               ("--max-count=" . $maxcount),
+               ("--skip=" . $skip),
+               @extra_options,
+               $commit_id,
+               "--",
+               ($filename ? ($filename) : ())
+               or die_error(500, "Open git-rev-list failed");
+       while (my $line = <$fd>) {
+               my %co = parse_commit_text($line);
+               push @cos, \%co;
+       }
+       close $fd;
+
+       return wantarray ? @cos : \@cos;
+}
+
+# parse line of git-diff-tree "raw" output
+sub parse_difftree_raw_line {
+       my $line = shift;
+       my %res;
+
+       # ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M   ls-files.c'
+       # ':100644 100644 7f9281985086971d3877aca27704f2aaf9c448ce bc190ebc71bbd923f2b728e505408f5e54bd073a M   rev-tree.c'
+       if ($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)([0-9]{0,3})\t(.*)$/) {
+               $res{'from_mode'} = $1;
+               $res{'to_mode'} = $2;
+               $res{'from_id'} = $3;
+               $res{'to_id'} = $4;
+               $res{'status'} = $5;
+               $res{'similarity'} = $6;
+               if ($res{'status'} eq 'R' || $res{'status'} eq 'C') { # renamed or copied
+                       ($res{'from_file'}, $res{'to_file'}) = map { unquote($_) } split("\t", $7);
+               } else {
+                       $res{'from_file'} = $res{'to_file'} = $res{'file'} = unquote($7);
+               }
+       }
+       # '::100755 100755 100755 60e79ca1b01bc8b057abe17ddab484699a7f5fdb 94067cc5f73388f33722d52ae02f44692bc07490 94067cc5f73388f33722d52ae02f44692bc07490 MR git-gui/git-gui.sh'
+       # combined diff (for merge commit)
+       elsif ($line =~ s/^(::+)((?:[0-7]{6} )+)((?:[0-9a-fA-F]{40} )+)([a-zA-Z]+)\t(.*)$//) {
+               $res{'nparents'}  = length($1);
+               $res{'from_mode'} = [ split(' ', $2) ];
+               $res{'to_mode'} = pop @{$res{'from_mode'}};
+               $res{'from_id'} = [ split(' ', $3) ];
+               $res{'to_id'} = pop @{$res{'from_id'}};
+               $res{'status'} = [ split('', $4) ];
+               $res{'to_file'} = unquote($5);
+       }
+       # 'c512b523472485aef4fff9e57b229d9d243c967f'
+       elsif ($line =~ m/^([0-9a-fA-F]{40})$/) {
+               $res{'commit'} = $1;
+       }
+
+       return wantarray ? %res : \%res;
+}
+
+# wrapper: return parsed line of git-diff-tree "raw" output
+# (the argument might be raw line, or parsed info)
+sub parsed_difftree_line {
+       my $line_or_ref = shift;
+
+       if (ref($line_or_ref) eq "HASH") {
+               # pre-parsed (or generated by hand)
+               return $line_or_ref;
+       } else {
+               return parse_difftree_raw_line($line_or_ref);
+       }
+}
+
+# parse line of git-ls-tree output
+sub parse_ls_tree_line {
+       my $line = shift;
+       my %opts = @_;
+       my %res;
+
+       if ($opts{'-l'}) {
+               #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa   16717  panic.c'
+               $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40}) +(-|[0-9]+)\t(.+)$/s;
+
+               $res{'mode'} = $1;
+               $res{'type'} = $2;
+               $res{'hash'} = $3;
+               $res{'size'} = $4;
+               if ($opts{'-z'}) {
+                       $res{'name'} = $5;
+               } else {
+                       $res{'name'} = unquote($5);
+               }
+       } else {
+               #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa  panic.c'
+               $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
+
+               $res{'mode'} = $1;
+               $res{'type'} = $2;
+               $res{'hash'} = $3;
+               if ($opts{'-z'}) {
+                       $res{'name'} = $4;
+               } else {
+                       $res{'name'} = unquote($4);
+               }
+       }
+
+       return wantarray ? %res : \%res;
+}
+
+# generates _two_ hashes, references to which are passed as 2 and 3 argument
+sub parse_from_to_diffinfo {
+       my ($diffinfo, $from, $to, @parents) = @_;
+
+       if ($diffinfo->{'nparents'}) {
+               # combined diff
+               $from->{'file'} = [];
+               $from->{'href'} = [];
+               fill_from_file_info($diffinfo, @parents)
+                       unless exists $diffinfo->{'from_file'};
+               for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
+                       $from->{'file'}[$i] =
+                               defined $diffinfo->{'from_file'}[$i] ?
+                                       $diffinfo->{'from_file'}[$i] :
+                                       $diffinfo->{'to_file'};
+                       if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file
+                               $from->{'href'}[$i] = href(action=>"blob",
+                                                          hash_base=>$parents[$i],
+                                                          hash=>$diffinfo->{'from_id'}[$i],
+                                                          file_name=>$from->{'file'}[$i]);
+                       } else {
+                               $from->{'href'}[$i] = undef;
+                       }
+               }
+       } else {
+               # ordinary (not combined) diff
+               $from->{'file'} = $diffinfo->{'from_file'};
+               if ($diffinfo->{'status'} ne "A") { # not new (added) file
+                       $from->{'href'} = href(action=>"blob", hash_base=>$hash_parent,
+                                              hash=>$diffinfo->{'from_id'},
+                                              file_name=>$from->{'file'});
+               } else {
+                       delete $from->{'href'};
+               }
+       }
+
+       $to->{'file'} = $diffinfo->{'to_file'};
+       if (!is_deleted($diffinfo)) { # file exists in result
+               $to->{'href'} = href(action=>"blob", hash_base=>$hash,
+                                    hash=>$diffinfo->{'to_id'},
+                                    file_name=>$to->{'file'});
+       } else {
+               delete $to->{'href'};
+       }
+}
+
+## ......................................................................
+## parse to array of hashes functions
+
+sub git_get_heads_list {
+       my ($limit, @classes) = @_;
+       @classes = get_branch_refs() unless @classes;
+       my @patterns = map { "refs/$_" } @classes;
+       my @headslist;
+
+       open my $fd, '-|', git_cmd(), 'for-each-ref',
+               ($limit ? '--count='.($limit+1) : ()), '--sort=-committerdate',
+               '--format=%(objectname) %(refname) %(subject)%00%(committer)',
+               @patterns
+               or return;
+       while (my $line = <$fd>) {
+               my %ref_item;
+
+               chomp $line;
+               my ($refinfo, $committerinfo) = split(/\0/, $line);
+               my ($hash, $name, $title) = split(' ', $refinfo, 3);
+               my ($committer, $epoch, $tz) =
+                       ($committerinfo =~ /^(.*) ([0-9]+) (.*)$/);
+               $ref_item{'fullname'}  = $name;
+               my $strip_refs = join '|', map { quotemeta } get_branch_refs();
+               $name =~ s!^refs/($strip_refs|remotes)/!!;
+               $ref_item{'name'} = $name;
+               # for refs neither in 'heads' nor 'remotes' we want to
+               # show their ref dir
+               my $ref_dir = (defined $1) ? $1 : '';
+               if ($ref_dir ne '' and $ref_dir ne 'heads' and $ref_dir ne 'remotes') {
+                   $ref_item{'name'} .= ' (' . $ref_dir . ')';
+               }
+
+               $ref_item{'id'}    = $hash;
+               $ref_item{'title'} = $title || '(no commit message)';
+               $ref_item{'epoch'} = $epoch;
+               if ($epoch) {
+                       $ref_item{'age'} = age_string(time - $ref_item{'epoch'});
+               } else {
+                       $ref_item{'age'} = "unknown";
+               }
+
+               push @headslist, \%ref_item;
+       }
+       close $fd;
+
+       return wantarray ? @headslist : \@headslist;
+}
+
+sub git_get_tags_list {
+       my $limit = shift;
+       my @tagslist;
+
+       open my $fd, '-|', git_cmd(), 'for-each-ref',
+               ($limit ? '--count='.($limit+1) : ()), '--sort=-creatordate',
+               '--format=%(objectname) %(objecttype) %(refname) '.
+               '%(*objectname) %(*objecttype) %(subject)%00%(creator)',
+               'refs/tags'
+               or return;
+       while (my $line = <$fd>) {
+               my %ref_item;
+
+               chomp $line;
+               my ($refinfo, $creatorinfo) = split(/\0/, $line);
+               my ($id, $type, $name, $refid, $reftype, $title) = split(' ', $refinfo, 6);
+               my ($creator, $epoch, $tz) =
+                       ($creatorinfo =~ /^(.*) ([0-9]+) (.*)$/);
+               $ref_item{'fullname'} = $name;
+               $name =~ s!^refs/tags/!!;
+
+               $ref_item{'type'} = $type;
+               $ref_item{'id'} = $id;
+               $ref_item{'name'} = $name;
+               if ($type eq "tag") {
+                       $ref_item{'subject'} = $title;
+                       $ref_item{'reftype'} = $reftype;
+                       $ref_item{'refid'}   = $refid;
+               } else {
+                       $ref_item{'reftype'} = $type;
+                       $ref_item{'refid'}   = $id;
+               }
+
+               if ($type eq "tag" || $type eq "commit") {
+                       $ref_item{'epoch'} = $epoch;
+                       if ($epoch) {
+                               $ref_item{'age'} = age_string(time - $ref_item{'epoch'});
+                       } else {
+                               $ref_item{'age'} = "unknown";
+                       }
+               }
+
+               push @tagslist, \%ref_item;
+       }
+       close $fd;
+
+       return wantarray ? @tagslist : \@tagslist;
+}
+
+## ----------------------------------------------------------------------
+## filesystem-related functions
+
+sub get_file_owner {
+       my $path = shift;
+
+       my ($dev, $ino, $mode, $nlink, $st_uid, $st_gid, $rdev, $size) = stat($path);
+       my ($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) = getpwuid($st_uid);
+       if (!defined $gcos) {
+               return undef;
+       }
+       my $owner = $gcos;
+       $owner =~ s/[,;].*$//;
+       return to_utf8($owner);
+}
+
+# assume that file exists
+sub insert_file {
+       my $filename = shift;
+
+       open my $fd, '<', $filename;
+       print map { to_utf8($_) } <$fd>;
+       close $fd;
+}
+
+## ......................................................................
+## mimetype related functions
+
+sub mimetype_guess_file {
+       my $filename = shift;
+       my $mimemap = shift;
+       -r $mimemap or return undef;
+
+       my %mimemap;
+       open(my $mh, '<', $mimemap) or return undef;
+       while (<$mh>) {
+               next if m/^#/; # skip comments
+               my ($mimetype, @exts) = split(/\s+/);
+               foreach my $ext (@exts) {
+                       $mimemap{$ext} = $mimetype;
+               }
+       }
+       close($mh);
+
+       $filename =~ /\.([^.]*)$/;
+       return $mimemap{$1};
+}
+
+sub mimetype_guess {
+       my $filename = shift;
+       my $mime;
+       $filename =~ /\./ or return undef;
+
+       if ($mimetypes_file) {
+               my $file = $mimetypes_file;
+               if ($file !~ m!^/!) { # if it is relative path
+                       # it is relative to project
+                       $file = "$projectroot/$project/$file";
+               }
+               $mime = mimetype_guess_file($filename, $file);
+       }
+       $mime ||= mimetype_guess_file($filename, '/etc/mime.types');
+       return $mime;
+}
+
+sub blob_mimetype {
+       my $fd = shift;
+       my $filename = shift;
+
+       if ($filename) {
+               my $mime = mimetype_guess($filename);
+               $mime and return $mime;
+       }
+
+       # just in case
+       return $default_blob_plain_mimetype unless $fd;
+
+       if (-T $fd) {
+               return 'text/plain';
+       } elsif (! $filename) {
+               return 'application/octet-stream';
+       } elsif ($filename =~ m/\.png$/i) {
+               return 'image/png';
+       } elsif ($filename =~ m/\.gif$/i) {
+               return 'image/gif';
+       } elsif ($filename =~ m/\.jpe?g$/i) {
+               return 'image/jpeg';
+       } else {
+               return 'application/octet-stream';
+       }
+}
+
+sub blob_contenttype {
+       my ($fd, $file_name, $type) = @_;
+
+       $type ||= blob_mimetype($fd, $file_name);
+       if ($type eq 'text/plain' && defined $default_text_plain_charset) {
+               $type .= "; charset=$default_text_plain_charset";
+       }
+
+       return $type;
+}
+
+# guess file syntax for syntax highlighting; return undef if no highlighting
+# the name of syntax can (in the future) depend on syntax highlighter used
+sub guess_file_syntax {
+       my ($highlight, $mimetype, $file_name) = @_;
+       return undef unless ($highlight && defined $file_name);
+       my $basename = basename($file_name, '.in');
+       return $highlight_basename{$basename}
+               if exists $highlight_basename{$basename};
+
+       $basename =~ /\.([^.]*)$/;
+       my $ext = $1 or return undef;
+       return $highlight_ext{$ext}
+               if exists $highlight_ext{$ext};
+
+       return undef;
+}
+
+# run highlighter and return FD of its output,
+# or return original FD if no highlighting
+sub run_highlighter {
+       my ($fd, $highlight, $syntax) = @_;
+        return $fd unless ($highlight);
+
+       close $fd;
+        my $syntax_arg = "--force";
+        if ($syntax) {
+                $syntax_arg = "--syntax $syntax";
+       }
+        open $fd, quote_command(git_cmd(), "cat-file", "blob", $hash)." | ".
+            quote_command($^X, '-CO', '-MEncode=decode,FB_DEFAULT', '-pse',
+                          '$_ = decode($fe, $_, FB_DEFAULT) if !utf8::decode($_);',
+                          '--', "-fe=$fallback_encoding")." | ".
+            quote_command($highlight_bin).
+            " --replace-tabs=8 --fragment $syntax_arg |"
+            or die_error(500, "Couldn't open file or run syntax highlighter");
+        return $fd;
+}
+
+## ======================================================================
+## functions printing HTML: header, footer, error page
+
+sub get_page_title {
+       my $title = to_utf8($site_name);
+
+       unless (defined $project) {
+               if (defined $project_filter) {
+                       $title .= " - projects in '" . esc_path($project_filter) . "'";
+               }
+               return $title;
+       }
+       $title .= " - " . to_utf8($project);
+
+       return $title unless (defined $action);
+       $title .= "/$action"; # $action is US-ASCII (7bit ASCII)
+
+       return $title unless (defined $file_name);
+       $title .= " - " . esc_path($file_name);
+       if ($action eq "tree" && $file_name !~ m|/$|) {
+               $title .= "/";
+       }
+
+       return $title;
+}
+
+sub get_content_type_html {
+       # require explicit support from the UA if we are to send the page as
+       # 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
+       # we have to do this because MSIE sometimes globs '*/*', pretending to
+       # support xhtml+xml but choking when it gets what it asked for.
+       if (defined $cgi->http('HTTP_ACCEPT') &&
+           $cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ &&
+           $cgi->Accept('application/xhtml+xml') != 0) {
+               return 'application/xhtml+xml';
+       } else {
+               return 'text/html';
+       }
+}
+
+sub print_feed_meta {
+       if (defined $project) {
+               my %href_params = get_feed_info();
+               if (!exists $href_params{'-title'}) {
+                       $href_params{'-title'} = 'log';
+               }
+
+               foreach my $format (qw(RSS Atom)) {
+                       my $type = lc($format);
+                       my %link_attr = (
+                               '-rel' => 'alternate',
+                               '-title' => esc_attr("$project - $href_params{'-title'} - $format feed"),
+                               '-type' => "application/$type+xml"
+                       );
+
+                       $href_params{'extra_options'} = undef;
+                       $href_params{'action'} = $type;
+                       $link_attr{'-href'} = href(%href_params);
+                       print "<link ".
+                             "rel=\"$link_attr{'-rel'}\" ".
+                             "title=\"$link_attr{'-title'}\" ".
+                             "href=\"$link_attr{'-href'}\" ".
+                             "type=\"$link_attr{'-type'}\" ".
+                             "/>\n";
+
+                       $href_params{'extra_options'} = '--no-merges';
+                       $link_attr{'-href'} = href(%href_params);
+                       $link_attr{'-title'} .= ' (no merges)';
+                       print "<link ".
+                             "rel=\"$link_attr{'-rel'}\" ".
+                             "title=\"$link_attr{'-title'}\" ".
+                             "href=\"$link_attr{'-href'}\" ".
+                             "type=\"$link_attr{'-type'}\" ".
+                             "/>\n";
+               }
+
+       } else {
+               printf('<link rel="alternate" title="%s projects list" '.
+                      'href="%s" type="text/plain; charset=utf-8" />'."\n",
+                      esc_attr($site_name), href(project=>undef, action=>"project_index"));
+               printf('<link rel="alternate" title="%s projects feeds" '.
+                      'href="%s" type="text/x-opml" />'."\n",
+                      esc_attr($site_name), href(project=>undef, action=>"opml"));
+       }
+}
+
+sub print_header_links {
+       my $status = shift;
+
+       # print out each stylesheet that exist, providing backwards capability
+       # for those people who defined $stylesheet in a config file
+       if (defined $stylesheet) {
+               print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
+       } else {
+               foreach my $stylesheet (@stylesheets) {
+                       next unless $stylesheet;
+                       print '<link rel="stylesheet" type="text/css" href="'.esc_url($stylesheet).'"/>'."\n";
+               }
+       }
+       print_feed_meta()
+               if ($status eq '200 OK');
+       if (defined $favicon) {
+               print qq(<link rel="shortcut icon" href=").esc_url($favicon).qq(" type="image/png" />\n);
+       }
+}
+
+sub print_nav_breadcrumbs_path {
+       my $dirprefix = undef;
+       while (my $part = shift) {
+               $dirprefix .= "/" if defined $dirprefix;
+               $dirprefix .= $part;
+               print $cgi->a({-href => href(project => undef,
+                                            project_filter => $dirprefix,
+                                            action => "project_list")},
+                             esc_html($part)) . " / ";
+       }
+}
+
+sub print_nav_breadcrumbs {
+       my %opts = @_;
+
+       for my $crumb (@extra_breadcrumbs, [ $home_link_str => $home_link ]) {
+               print $cgi->a({-href => esc_url($crumb->[1])}, $crumb->[0]) . " / ";
+       }
+       if (defined $project) {
+               my @dirname = split '/', $project;
+               my $projectbasename = pop @dirname;
+               print_nav_breadcrumbs_path(@dirname);
+               print $cgi->a({-href => href(action=>"summary")}, esc_html($projectbasename));
+               if (defined $action) {
+                       my $action_print = $action ;
+                       if (defined $opts{-action_extra}) {
+                               $action_print = $cgi->a({-href => href(action=>$action)},
+                                       $action);
+                       }
+                       print " / $action_print";
+               }
+               if (defined $opts{-action_extra}) {
+                       print " / $opts{-action_extra}";
+               }
+               print "\n";
+       } elsif (defined $project_filter) {
+               print_nav_breadcrumbs_path(split '/', $project_filter);
+       }
+}
+
+sub print_search_form {
+       if (!defined $searchtext) {
+               $searchtext = "";
+       }
+       my $search_hash;
+       if (defined $hash_base) {
+               $search_hash = $hash_base;
+       } elsif (defined $hash) {
+               $search_hash = $hash;
+       } else {
+               $search_hash = "HEAD";
+       }
+       my $action = $my_uri;
+       my $use_pathinfo = gitweb_check_feature('pathinfo');
+       if ($use_pathinfo) {
+               $action .= "/".esc_url($project);
+       }
+       print $cgi->start_form(-method => "get", -action => $action) .
+             "<div class=\"search\">\n" .
+             (!$use_pathinfo &&
+             $cgi->input({-name=>"p", -value=>$project, -type=>"hidden"}) . "\n") .
+             $cgi->input({-name=>"a", -value=>"search", -type=>"hidden"}) . "\n" .
+             $cgi->input({-name=>"h", -value=>$search_hash, -type=>"hidden"}) . "\n" .
+             $cgi->popup_menu(-name => 'st', -default => 'commit',
+                              -values => ['commit', 'grep', 'author', 'committer', 'pickaxe']) .
+             " " . $cgi->a({-href => href(action=>"search_help"),
+                            -title => "search help" }, "?") . " search:\n",
+             $cgi->textfield(-name => "s", -value => $searchtext, -override => 1) . "\n" .
+             "<span title=\"Extended regular expression\">" .
+             $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
+                            -checked => $search_use_regexp) .
+             "</span>" .
+             "</div>" .
+             $cgi->end_form() . "\n";
+}
+
+sub git_header_html {
+       my $status = shift || "200 OK";
+       my $expires = shift;
+       my %opts = @_;
+
+       my $title = get_page_title();
+       my $content_type = get_content_type_html();
+       print $cgi->header(-type=>$content_type, -charset => 'utf-8',
+                          -status=> $status, -expires => $expires)
+               unless ($opts{'-no_http_header'});
+       my $mod_perl_version = $ENV{'MOD_PERL'} ? " $ENV{'MOD_PERL'}" : '';
+       print <<EOF;
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
+<!-- git web interface version $version, (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
+<!-- git core binaries version $git_version -->
+<head>
+<meta http-equiv="content-type" content="$content_type; charset=utf-8"/>
+<meta name="generator" content="gitweb/$version git/$git_version$mod_perl_version"/>
+<meta name="robots" content="index, nofollow"/>
+<title>$title</title>
+EOF
+       # the stylesheet, favicon etc urls won't work correctly with path_info
+       # unless we set the appropriate base URL
+       if ($ENV{'PATH_INFO'}) {
+               print "<base href=\"".esc_url($base_url)."\" />\n";
+       }
+       print_header_links($status);
+
+       if (defined $site_html_head_string) {
+               print to_utf8($site_html_head_string);
+       }
+
+       print "</head>\n" .
+             "<body>\n";
+
+       if (defined $site_header && -f $site_header) {
+               insert_file($site_header);
+       }
+
+       print "<div class=\"page_header\">\n";
+       if (defined $logo) {
+               print $cgi->a({-href => esc_url($logo_url),
+                              -title => $logo_label},
+                             $cgi->img({-src => esc_url($logo),
+                                        -width => 72, -height => 27,
+                                        -alt => "git",
+                                        -class => "logo"}));
+       }
+       print_nav_breadcrumbs(%opts);
+       print "</div>\n";
+
+       my $have_search = gitweb_check_feature('search');
+       if (defined $project && $have_search) {
+               print_search_form();
+       }
+}
+
+sub git_footer_html {
+       my $feed_class = 'rss_logo';
+
+       print "<div class=\"page_footer\">\n";
+       if (defined $project) {
+               my $descr = git_get_project_description($project);
+               if (defined $descr) {
+                       print "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n";
+               }
+
+               my %href_params = get_feed_info();
+               if (!%href_params) {
+                       $feed_class .= ' generic';
+               }
+               $href_params{'-title'} ||= 'log';
+
+               foreach my $format (qw(RSS Atom)) {
+                       $href_params{'action'} = lc($format);
+                       print $cgi->a({-href => href(%href_params),
+                                     -title => "$href_params{'-title'} $format feed",
+                                     -class => $feed_class}, $format)."\n";
+               }
+
+       } else {
+               print $cgi->a({-href => href(project=>undef, action=>"opml",
+                                            project_filter => $project_filter),
+                             -class => $feed_class}, "OPML") . " ";
+               print $cgi->a({-href => href(project=>undef, action=>"project_index",
+                                            project_filter => $project_filter),
+                             -class => $feed_class}, "TXT") . "\n";
+       }
+       print "</div>\n"; # class="page_footer"
+
+       if (defined $t0 && gitweb_check_feature('timed')) {
+               print "<div id=\"generating_info\">\n";
+               print 'This page took '.
+                     '<span id="generating_time" class="time_span">'.
+                     tv_interval($t0, [ gettimeofday() ]).
+                     ' seconds </span>'.
+                     ' and '.
+                     '<span id="generating_cmd">'.
+                     $number_of_git_cmds.
+                     '</span> git commands '.
+                     " to generate.\n";
+               print "</div>\n"; # class="page_footer"
+       }
+
+       if (defined $site_footer && -f $site_footer) {
+               insert_file($site_footer);
+       }
+
+       print qq!<script type="text/javascript" src="!.esc_url($javascript).qq!"></script>\n!;
+       if (defined $action &&
+           $action eq 'blame_incremental') {
+               print qq!<script type="text/javascript">\n!.
+                     qq!startBlame("!. href(action=>"blame_data", -replay=>1) .qq!",\n!.
+                     qq!           "!. href() .qq!");\n!.
+                     qq!</script>\n!;
+       } else {
+               my ($jstimezone, $tz_cookie, $datetime_class) =
+                       gitweb_get_feature('javascript-timezone');
+
+               print qq!<script type="text/javascript">\n!.
+                     qq!window.onload = function () {\n!;
+               if (gitweb_check_feature('javascript-actions')) {
+                       print qq!       fixLinks();\n!;
+               }
+               if ($jstimezone && $tz_cookie && $datetime_class) {
+                       print qq!       var tz_cookie = { name: '$tz_cookie', expires: 14, path: '/' };\n!. # in days
+                             qq!       onloadTZSetup('$jstimezone', tz_cookie, '$datetime_class');\n!;
+               }
+               print qq!};\n!.
+                     qq!</script>\n!;
+       }
+
+       print "</body>\n" .
+             "</html>";
+}
+
+# die_error(<http_status_code>, <error_message>[, <detailed_html_description>])
+# Example: die_error(404, 'Hash not found')
+# By convention, use the following status codes (as defined in RFC 2616):
+# 400: Invalid or missing CGI parameters, or
+#      requested object exists but has wrong type.
+# 403: Requested feature (like "pickaxe" or "snapshot") not enabled on
+#      this server or project.
+# 404: Requested object/revision/project doesn't exist.
+# 500: The server isn't configured properly, or
+#      an internal error occurred (e.g. failed assertions caused by bugs), or
+#      an unknown error occurred (e.g. the git binary died unexpectedly).
+# 503: The server is currently unavailable (because it is overloaded,
+#      or down for maintenance).  Generally, this is a temporary state.
+sub die_error {
+       my $status = shift || 500;
+       my $error = esc_html(shift) || "Internal Server Error";
+       my $extra = shift;
+       my %opts = @_;
+
+       my %http_responses = (
+               400 => '400 Bad Request',
+               403 => '403 Forbidden',
+               404 => '404 Not Found',
+               500 => '500 Internal Server Error',
+               503 => '503 Service Unavailable',
+       );
+       git_header_html($http_responses{$status}, undef, %opts);
+       print <<EOF;
+<div class="page_body">
+<br /><br />
+$status - $error
+<br />
+EOF
+       if (defined $extra) {
+               print "<hr />\n" .
+                     "$extra\n";
+       }
+       print "</div>\n";
+
+       git_footer_html();
+       goto DONE_GITWEB
+               unless ($opts{'-error_handler'});
+}
+
+## ----------------------------------------------------------------------
+## functions printing or outputting HTML: navigation
+
+sub git_print_page_nav {
+       my ($current, $suppress, $head, $treehead, $treebase, $extra) = @_;
+       $extra = '' if !defined $extra; # pager or formats
+
+       my @navs = qw(summary shortlog log commit commitdiff tree);
+       if ($suppress) {
+               @navs = grep { $_ ne $suppress } @navs;
+       }
+
+       my %arg = map { $_ => {action=>$_} } @navs;
+       if (defined $head) {
+               for (qw(commit commitdiff)) {
+                       $arg{$_}{'hash'} = $head;
+               }
+               if ($current =~ m/^(tree | log | shortlog | commit | commitdiff | search)$/x) {
+                       for (qw(shortlog log)) {
+                               $arg{$_}{'hash'} = $head;
+                       }
+               }
+       }
+
+       $arg{'tree'}{'hash'} = $treehead if defined $treehead;
+       $arg{'tree'}{'hash_base'} = $treebase if defined $treebase;
+
+       my @actions = gitweb_get_feature('actions');
+       my %repl = (
+               '%' => '%',
+               'n' => $project,         # project name
+               'f' => $git_dir,         # project path within filesystem
+               'h' => $treehead || '',  # current hash ('h' parameter)
+               'b' => $treebase || '',  # hash base ('hb' parameter)
+       );
+       while (@actions) {
+               my ($label, $link, $pos) = splice(@actions,0,3);
+               # insert
+               @navs = map { $_ eq $pos ? ($_, $label) : $_ } @navs;
+               # munch munch
+               $link =~ s/%([%nfhb])/$repl{$1}/g;
+               $arg{$label}{'_href'} = $link;
+       }
+
+       print "<div class=\"page_nav\">\n" .
+               (join " | ",
+                map { $_ eq $current ?
+                      $_ : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, "$_")
+                } @navs);
+       print "<br/>\n$extra<br/>\n" .
+             "</div>\n";
+}
+
+# returns a submenu for the nagivation of the refs views (tags, heads,
+# remotes) with the current view disabled and the remotes view only
+# available if the feature is enabled
+sub format_ref_views {
+       my ($current) = @_;
+       my @ref_views = qw{tags heads};
+       push @ref_views, 'remotes' if gitweb_check_feature('remote_heads');
+       return join " | ", map {
+               $_ eq $current ? $_ :
+               $cgi->a({-href => href(action=>$_)}, $_)
+       } @ref_views
+}
+
+sub format_paging_nav {
+       my ($action, $page, $has_next_link) = @_;
+       my $paging_nav;
+
+
+       if ($page > 0) {
+               $paging_nav .=
+                       $cgi->a({-href => href(-replay=>1, page=>undef)}, "first") .
+                       " &sdot; " .
+                       $cgi->a({-href => href(-replay=>1, page=>$page-1),
+                                -accesskey => "p", -title => "Alt-p"}, "prev");
+       } else {
+               $paging_nav .= "first &sdot; prev";
+       }
+
+       if ($has_next_link) {
+               $paging_nav .= " &sdot; " .
+                       $cgi->a({-href => href(-replay=>1, page=>$page+1),
+                                -accesskey => "n", -title => "Alt-n"}, "next");
+       } else {
+               $paging_nav .= " &sdot; next";
+       }
+
+       return $paging_nav;
+}
+
+## ......................................................................
+## functions printing or outputting HTML: div
+
+sub git_print_header_div {
+       my ($action, $title, $hash, $hash_base) = @_;
+       my %args = ();
+
+       $args{'action'} = $action;
+       $args{'hash'} = $hash if $hash;
+       $args{'hash_base'} = $hash_base if $hash_base;
+
+       print "<div class=\"header\">\n" .
+             $cgi->a({-href => href(%args), -class => "title"},
+             $title ? $title : $action) .
+             "\n</div>\n";
+}
+
+sub format_repo_url {
+       my ($name, $url) = @_;
+       return "<tr class=\"metadata_url\"><td>$name</td><td>$url</td></tr>\n";
+}
+
+# Group output by placing it in a DIV element and adding a header.
+# Options for start_div() can be provided by passing a hash reference as the
+# first parameter to the function.
+# Options to git_print_header_div() can be provided by passing an array
+# reference. This must follow the options to start_div if they are present.
+# The content can be a scalar, which is output as-is, a scalar reference, which
+# is output after html escaping, an IO handle passed either as *handle or
+# *handle{IO}, or a function reference. In the latter case all following
+# parameters will be taken as argument to the content function call.
+sub git_print_section {
+       my ($div_args, $header_args, $content);
+       my $arg = shift;
+       if (ref($arg) eq 'HASH') {
+               $div_args = $arg;
+               $arg = shift;
+       }
+       if (ref($arg) eq 'ARRAY') {
+               $header_args = $arg;
+               $arg = shift;
+       }
+       $content = $arg;
+
+       print $cgi->start_div($div_args);
+       git_print_header_div(@$header_args);
+
+       if (ref($content) eq 'CODE') {
+               $content->(@_);
+       } elsif (ref($content) eq 'SCALAR') {
+               print esc_html($$content);
+       } elsif (ref($content) eq 'GLOB' or ref($content) eq 'IO::Handle') {
+               print <$content>;
+       } elsif (!ref($content) && defined($content)) {
+               print $content;
+       }
+
+       print $cgi->end_div;
+}
+
+sub format_timestamp_html {
+       my $date = shift;
+       my $strtime = $date->{'rfc2822'};
+
+       my (undef, undef, $datetime_class) =
+               gitweb_get_feature('javascript-timezone');
+       if ($datetime_class) {
+               $strtime = qq!<span class="$datetime_class">$strtime</span>!;
+       }
+
+       my $localtime_format = '(%02d:%02d %s)';
+       if ($date->{'hour_local'} < 6) {
+               $localtime_format = '(<span class="atnight">%02d:%02d</span> %s)';
+       }
+       $strtime .= ' ' .
+                   sprintf($localtime_format,
+                           $date->{'hour_local'}, $date->{'minute_local'}, $date->{'tz_local'});
+
+       return $strtime;
+}
+
+# Outputs the author name and date in long form
+sub git_print_authorship {
+       my $co = shift;
+       my %opts = @_;
+       my $tag = $opts{-tag} || 'div';
+       my $author = $co->{'author_name'};
+
+       my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});
+       print "<$tag class=\"author_date\">" .
+             format_search_author($author, "author", esc_html($author)) .
+             " [".format_timestamp_html(\%ad)."]".
+             git_get_avatar($co->{'author_email'}, -pad_before => 1) .
+             "</$tag>\n";
+}
+
+# Outputs table rows containing the full author or committer information,
+# in the format expected for 'commit' view (& similar).
+# Parameters are a commit hash reference, followed by the list of people
+# to output information for. If the list is empty it defaults to both
+# author and committer.
+sub git_print_authorship_rows {
+       my $co = shift;
+       # too bad we can't use @people = @_ || ('author', 'committer')
+       my @people = @_;
+       @people = ('author', 'committer') unless @people;
+       foreach my $who (@people) {
+               my %wd = parse_date($co->{"${who}_epoch"}, $co->{"${who}_tz"});
+               print "<tr><td>$who</td><td>" .
+                     format_search_author($co->{"${who}_name"}, $who,
+                                          esc_html($co->{"${who}_name"})) . " " .
+                     format_search_author($co->{"${who}_email"}, $who,
+                                          esc_html("<" . $co->{"${who}_email"} . ">")) .
+                     "</td><td rowspan=\"2\">" .
+                     git_get_avatar($co->{"${who}_email"}, -size => 'double') .
+                     "</td></tr>\n" .
+                     "<tr>" .
+                     "<td></td><td>" .
+                     format_timestamp_html(\%wd) .
+                     "</td>" .
+                     "</tr>\n";
+       }
+}
+
+sub git_print_page_path {
+       my $name = shift;
+       my $type = shift;
+       my $hb = shift;
+
+
+       print "<div class=\"page_path\">";
+       print $cgi->a({-href => href(action=>"tree", hash_base=>$hb),
+                     -title => 'tree root'}, to_utf8("[$project]"));
+       print " / ";
+       if (defined $name) {
+               my @dirname = split '/', $name;
+               my $basename = pop @dirname;
+               my $fullname = '';
+
+               foreach my $dir (@dirname) {
+                       $fullname .= ($fullname ? '/' : '') . $dir;
+                       print $cgi->a({-href => href(action=>"tree", file_name=>$fullname,
+                                                    hash_base=>$hb),
+                                     -title => $fullname}, esc_path($dir));
+                       print " / ";
+               }
+               if (defined $type && $type eq 'blob') {
+                       print $cgi->a({-href => href(action=>"blob_plain", file_name=>$file_name,
+                                                    hash_base=>$hb),
+                                     -title => $name}, esc_path($basename));
+               } elsif (defined $type && $type eq 'tree') {
+                       print $cgi->a({-href => href(action=>"tree", file_name=>$file_name,
+                                                    hash_base=>$hb),
+                                     -title => $name}, esc_path($basename));
+                       print " / ";
+               } else {
+                       print esc_path($basename);
+               }
+       }
+       print "<br/></div>\n";
+}
+
+sub git_print_log {
+       my $log = shift;
+       my %opts = @_;
+
+       if ($opts{'-remove_title'}) {
+               # remove title, i.e. first line of log
+               shift @$log;
+       }
+       # remove leading empty lines
+       while (defined $log->[0] && $log->[0] eq "") {
+               shift @$log;
+       }
+
+       # print log
+       my $skip_blank_line = 0;
+       foreach my $line (@$log) {
+               if ($line =~ m/^\s*([A-Z][-A-Za-z]*-[Bb]y|C[Cc]): /) {
+                       if (! $opts{'-remove_signoff'}) {
+                               print "<span class=\"signoff\">" . esc_html($line) . "</span><br/>\n";
+                               $skip_blank_line = 1;
+                       }
+                       next;
+               }
+
+               if ($line =~ m,\s*([a-z]*link): (https?://\S+),i) {
+                       if (! $opts{'-remove_signoff'}) {
+                               print "<span class=\"signoff\">" . esc_html($1) . ": " .
+                                       "<a href=\"" . esc_html($2) . "\">" . esc_html($2) . "</a>" .
+                                       "</span><br/>\n";
+                               $skip_blank_line = 1;
+                       }
+                       next;
+               }
+
+               # print only one empty line
+               # do not print empty line after signoff
+               if ($line eq "") {
+                       next if ($skip_blank_line);
+                       $skip_blank_line = 1;
+               } else {
+                       $skip_blank_line = 0;
+               }
+
+               print format_log_line_html($line) . "<br/>\n";
+       }
+
+       if ($opts{'-final_empty_line'}) {
+               # end with single empty line
+               print "<br/>\n" unless $skip_blank_line;
+       }
+}
+
+# return link target (what link points to)
+sub git_get_link_target {
+       my $hash = shift;
+       my $link_target;
+
+       # read link
+       open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
+               or return;
+       {
+               local $/ = undef;
+               $link_target = <$fd>;
+       }
+       close $fd
+               or return;
+
+       return $link_target;
+}
+
+# given link target, and the directory (basedir) the link is in,
+# return target of link relative to top directory (top tree);
+# return undef if it is not possible (including absolute links).
+sub normalize_link_target {
+       my ($link_target, $basedir) = @_;
+
+       # absolute symlinks (beginning with '/') cannot be normalized
+       return if (substr($link_target, 0, 1) eq '/');
+
+       # normalize link target to path from top (root) tree (dir)
+       my $path;
+       if ($basedir) {
+               $path = $basedir . '/' . $link_target;
+       } else {
+               # we are in top (root) tree (dir)
+               $path = $link_target;
+       }
+
+       # remove //, /./, and /../
+       my @path_parts;
+       foreach my $part (split('/', $path)) {
+               # discard '.' and ''
+               next if (!$part || $part eq '.');
+               # handle '..'
+               if ($part eq '..') {
+                       if (@path_parts) {
+                               pop @path_parts;
+                       } else {
+                               # link leads outside repository (outside top dir)
+                               return;
+                       }
+               } else {
+                       push @path_parts, $part;
+               }
+       }
+       $path = join('/', @path_parts);
+
+       return $path;
+}
+
+# print tree entry (row of git_tree), but without encompassing <tr> element
+sub git_print_tree_entry {
+       my ($t, $basedir, $hash_base, $have_blame) = @_;
+
+       my %base_key = ();
+       $base_key{'hash_base'} = $hash_base if defined $hash_base;
+
+       # The format of a table row is: mode list link.  Where mode is
+       # the mode of the entry, list is the name of the entry, an href,
+       # and link is the action links of the entry.
+
+       print "<td class=\"mode\">" . mode_str($t->{'mode'}) . "</td>\n";
+       if (exists $t->{'size'}) {
+               print "<td class=\"size\">$t->{'size'}</td>\n";
+       }
+       if ($t->{'type'} eq "blob") {
+               print "<td class=\"list\">" .
+                       $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
+                                              file_name=>"$basedir$t->{'name'}", %base_key),
+                               -class => "list"}, esc_path($t->{'name'}));
+               if (S_ISLNK(oct $t->{'mode'})) {
+                       my $link_target = git_get_link_target($t->{'hash'});
+                       if ($link_target) {
+                               my $norm_target = normalize_link_target($link_target, $basedir);
+                               if (defined $norm_target) {
+                                       print " -> " .
+                                             $cgi->a({-href => href(action=>"object", hash_base=>$hash_base,
+                                                                    file_name=>$norm_target),
+                                                      -title => $norm_target}, esc_path($link_target));
+                               } else {
+                                       print " -> " . esc_path($link_target);
+                               }
+                       }
+               }
+               print "</td>\n";
+               print "<td class=\"link\">";
+               print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
+                                            file_name=>"$basedir$t->{'name'}", %base_key)},
+                             "blob");
+               if ($have_blame) {
+                       print " | " .
+                             $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
+                                                    file_name=>"$basedir$t->{'name'}", %base_key)},
+                                     "blame");
+               }
+               if (defined $hash_base) {
+                       print " | " .
+                             $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+                                                    hash=>$t->{'hash'}, file_name=>"$basedir$t->{'name'}")},
+                                     "history");
+               }
+               print " | " .
+                       $cgi->a({-href => href(action=>"blob_plain", hash_base=>$hash_base,
+                                              file_name=>"$basedir$t->{'name'}")},
+                               "raw");
+               print "</td>\n";
+
+       } elsif ($t->{'type'} eq "tree") {
+               print "<td class=\"list\">";
+               print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
+                                            file_name=>"$basedir$t->{'name'}",
+                                            %base_key)},
+                             esc_path($t->{'name'}));
+               print "</td>\n";
+               print "<td class=\"link\">";
+               print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
+                                            file_name=>"$basedir$t->{'name'}",
+                                            %base_key)},
+                             "tree");
+               if (defined $hash_base) {
+                       print " | " .
+                             $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,
+                                                    file_name=>"$basedir$t->{'name'}")},
+                                     "history");
+               }
+               print "</td>\n";
+       } else {
+               # unknown object: we can only present history for it
+               # (this includes 'commit' object, i.e. submodule support)
+               print "<td class=\"list\">" .
+                     esc_path($t->{'name'}) .
+                     "</td>\n";
+               print "<td class=\"link\">";
+               if (defined $hash_base) {
+                       print $cgi->a({-href => href(action=>"history",
+                                                    hash_base=>$hash_base,
+                                                    file_name=>"$basedir$t->{'name'}")},
+                                     "history");
+               }
+               print "</td>\n";
+       }
+}
+
+## ......................................................................
+## functions printing large fragments of HTML
+
+# get pre-image filenames for merge (combined) diff
+sub fill_from_file_info {
+       my ($diff, @parents) = @_;
+
+       $diff->{'from_file'} = [ ];
+       $diff->{'from_file'}[$diff->{'nparents'} - 1] = undef;
+       for (my $i = 0; $i < $diff->{'nparents'}; $i++) {
+               if ($diff->{'status'}[$i] eq 'R' ||
+                   $diff->{'status'}[$i] eq 'C') {
+                       $diff->{'from_file'}[$i] =
+                               git_get_path_by_hash($parents[$i], $diff->{'from_id'}[$i]);
+               }
+       }
+
+       return $diff;
+}
+
+# is current raw difftree line of file deletion
+sub is_deleted {
+       my $diffinfo = shift;
+
+       return $diffinfo->{'to_id'} eq ('0' x 40);
+}
+
+# does patch correspond to [previous] difftree raw line
+# $diffinfo  - hashref of parsed raw diff format
+# $patchinfo - hashref of parsed patch diff format
+#              (the same keys as in $diffinfo)
+sub is_patch_split {
+       my ($diffinfo, $patchinfo) = @_;
+
+       return defined $diffinfo && defined $patchinfo
+               && $diffinfo->{'to_file'} eq $patchinfo->{'to_file'};
+}
+
+
+sub git_difftree_body {
+       my ($difftree, $hash, @parents) = @_;
+       my ($parent) = $parents[0];
+       my $have_blame = gitweb_check_feature('blame');
+       print "<div class=\"list_head\">\n";
+       if ($#{$difftree} > 10) {
+               print(($#{$difftree} + 1) . " files changed:\n");
+       }
+       print "</div>\n";
+
+       print "<table class=\"" .
+             (@parents > 1 ? "combined " : "") .
+             "diff_tree\">\n";
+
+       # header only for combined diff in 'commitdiff' view
+       my $has_header = @$difftree && @parents > 1 && $action eq 'commitdiff';
+       if ($has_header) {
+               # table header
+               print "<thead><tr>\n" .
+                      "<th></th><th></th>\n"; # filename, patchN link
+               for (my $i = 0; $i < @parents; $i++) {
+                       my $par = $parents[$i];
+                       print "<th>" .
+                             $cgi->a({-href => href(action=>"commitdiff",
+                                                    hash=>$hash, hash_parent=>$par),
+                                      -title => 'commitdiff to parent number ' .
+                                                 ($i+1) . ': ' . substr($par,0,7)},
+                                     $i+1) .
+                             "&nbsp;</th>\n";
+               }
+               print "</tr></thead>\n<tbody>\n";
+       }
+
+       my $alternate = 1;
+       my $patchno = 0;
+       foreach my $line (@{$difftree}) {
+               my $diff = parsed_difftree_line($line);
+
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+
+               if (exists $diff->{'nparents'}) { # combined diff
+
+                       fill_from_file_info($diff, @parents)
+                               unless exists $diff->{'from_file'};
+
+                       if (!is_deleted($diff)) {
+                               # file exists in the result (child) commit
+                               print "<td>" .
+                                     $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+                                                            file_name=>$diff->{'to_file'},
+                                                            hash_base=>$hash),
+                                             -class => "list"}, esc_path($diff->{'to_file'})) .
+                                     "</td>\n";
+                       } else {
+                               print "<td>" .
+                                     esc_path($diff->{'to_file'}) .
+                                     "</td>\n";
+                       }
+
+                       if ($action eq 'commitdiff') {
+                               # link to patch
+                               $patchno++;
+                               print "<td class=\"link\">" .
+                                     $cgi->a({-href => href(-anchor=>"patch$patchno")},
+                                             "patch") .
+                                     " | " .
+                                     "</td>\n";
+                       }
+
+                       my $has_history = 0;
+                       my $not_deleted = 0;
+                       for (my $i = 0; $i < $diff->{'nparents'}; $i++) {
+                               my $hash_parent = $parents[$i];
+                               my $from_hash = $diff->{'from_id'}[$i];
+                               my $from_path = $diff->{'from_file'}[$i];
+                               my $status = $diff->{'status'}[$i];
+
+                               $has_history ||= ($status ne 'A');
+                               $not_deleted ||= ($status ne 'D');
+
+                               if ($status eq 'A') {
+                                       print "<td  class=\"link\" align=\"right\"> | </td>\n";
+                               } elsif ($status eq 'D') {
+                                       print "<td class=\"link\">" .
+                                             $cgi->a({-href => href(action=>"blob",
+                                                                    hash_base=>$hash,
+                                                                    hash=>$from_hash,
+                                                                    file_name=>$from_path)},
+                                                     "blob" . ($i+1)) .
+                                             " | </td>\n";
+                               } else {
+                                       if ($diff->{'to_id'} eq $from_hash) {
+                                               print "<td class=\"link nochange\">";
+                                       } else {
+                                               print "<td class=\"link\">";
+                                       }
+                                       print $cgi->a({-href => href(action=>"blobdiff",
+                                                                    hash=>$diff->{'to_id'},
+                                                                    hash_parent=>$from_hash,
+                                                                    hash_base=>$hash,
+                                                                    hash_parent_base=>$hash_parent,
+                                                                    file_name=>$diff->{'to_file'},
+                                                                    file_parent=>$from_path)},
+                                                     "diff" . ($i+1)) .
+                                             " | </td>\n";
+                               }
+                       }
+
+                       print "<td class=\"link\">";
+                       if ($not_deleted) {
+                               print $cgi->a({-href => href(action=>"blob",
+                                                            hash=>$diff->{'to_id'},
+                                                            file_name=>$diff->{'to_file'},
+                                                            hash_base=>$hash)},
+                                             "blob");
+                               print " | " if ($has_history);
+                       }
+                       if ($has_history) {
+                               print $cgi->a({-href => href(action=>"history",
+                                                            file_name=>$diff->{'to_file'},
+                                                            hash_base=>$hash)},
+                                             "history");
+                       }
+                       print "</td>\n";
+
+                       print "</tr>\n";
+                       next; # instead of 'else' clause, to avoid extra indent
+               }
+               # else ordinary diff
+
+               my ($to_mode_oct, $to_mode_str, $to_file_type);
+               my ($from_mode_oct, $from_mode_str, $from_file_type);
+               if ($diff->{'to_mode'} ne ('0' x 6)) {
+                       $to_mode_oct = oct $diff->{'to_mode'};
+                       if (S_ISREG($to_mode_oct)) { # only for regular file
+                               $to_mode_str = sprintf("%04o", $to_mode_oct & 0777); # permission bits
+                       }
+                       $to_file_type = file_type($diff->{'to_mode'});
+               }
+               if ($diff->{'from_mode'} ne ('0' x 6)) {
+                       $from_mode_oct = oct $diff->{'from_mode'};
+                       if (S_ISREG($from_mode_oct)) { # only for regular file
+                               $from_mode_str = sprintf("%04o", $from_mode_oct & 0777); # permission bits
+                       }
+                       $from_file_type = file_type($diff->{'from_mode'});
+               }
+
+               if ($diff->{'status'} eq "A") { # created
+                       my $mode_chng = "<span class=\"file_status new\">[new $to_file_type";
+                       $mode_chng   .= " with mode: $to_mode_str" if $to_mode_str;
+                       $mode_chng   .= "]</span>";
+                       print "<td>";
+                       print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+                                                    hash_base=>$hash, file_name=>$diff->{'file'}),
+                                     -class => "list"}, esc_path($diff->{'file'}));
+                       print "</td>\n";
+                       print "<td>$mode_chng</td>\n";
+                       print "<td class=\"link\">";
+                       if ($action eq 'commitdiff') {
+                               # link to patch
+                               $patchno++;
+                               print $cgi->a({-href => href(-anchor=>"patch$patchno")},
+                                             "patch") .
+                                     " | ";
+                       }
+                       print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+                                                    hash_base=>$hash, file_name=>$diff->{'file'})},
+                                     "blob");
+                       print "</td>\n";
+
+               } elsif ($diff->{'status'} eq "D") { # deleted
+                       my $mode_chng = "<span class=\"file_status deleted\">[deleted $from_file_type]</span>";
+                       print "<td>";
+                       print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'},
+                                                    hash_base=>$parent, file_name=>$diff->{'file'}),
+                                      -class => "list"}, esc_path($diff->{'file'}));
+                       print "</td>\n";
+                       print "<td>$mode_chng</td>\n";
+                       print "<td class=\"link\">";
+                       if ($action eq 'commitdiff') {
+                               # link to patch
+                               $patchno++;
+                               print $cgi->a({-href => href(-anchor=>"patch$patchno")},
+                                             "patch") .
+                                     " | ";
+                       }
+                       print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'from_id'},
+                                                    hash_base=>$parent, file_name=>$diff->{'file'})},
+                                     "blob") . " | ";
+                       if ($have_blame) {
+                               print $cgi->a({-href => href(action=>"blame", hash_base=>$parent,
+                                                            file_name=>$diff->{'file'})},
+                                             "blame") . " | ";
+                       }
+                       print $cgi->a({-href => href(action=>"history", hash_base=>$parent,
+                                                    file_name=>$diff->{'file'})},
+                                     "history");
+                       print "</td>\n";
+
+               } elsif ($diff->{'status'} eq "M" || $diff->{'status'} eq "T") { # modified, or type changed
+                       my $mode_chnge = "";
+                       if ($diff->{'from_mode'} != $diff->{'to_mode'}) {
+                               $mode_chnge = "<span class=\"file_status mode_chnge\">[changed";
+                               if ($from_file_type ne $to_file_type) {
+                                       $mode_chnge .= " from $from_file_type to $to_file_type";
+                               }
+                               if (($from_mode_oct & 0777) != ($to_mode_oct & 0777)) {
+                                       if ($from_mode_str && $to_mode_str) {
+                                               $mode_chnge .= " mode: $from_mode_str->$to_mode_str";
+                                       } elsif ($to_mode_str) {
+                                               $mode_chnge .= " mode: $to_mode_str";
+                                       }
+                               }
+                               $mode_chnge .= "]</span>\n";
+                       }
+                       print "<td>";
+                       print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+                                                    hash_base=>$hash, file_name=>$diff->{'file'}),
+                                     -class => "list"}, esc_path($diff->{'file'}));
+                       print "</td>\n";
+                       print "<td>$mode_chnge</td>\n";
+                       print "<td class=\"link\">";
+                       if ($action eq 'commitdiff') {
+                               # link to patch
+                               $patchno++;
+                               print $cgi->a({-href => href(-anchor=>"patch$patchno")},
+                                             "patch") .
+                                     " | ";
+                       } elsif ($diff->{'to_id'} ne $diff->{'from_id'}) {
+                               # "commit" view and modified file (not onlu mode changed)
+                               print $cgi->a({-href => href(action=>"blobdiff",
+                                                            hash=>$diff->{'to_id'}, hash_parent=>$diff->{'from_id'},
+                                                            hash_base=>$hash, hash_parent_base=>$parent,
+                                                            file_name=>$diff->{'file'})},
+                                             "diff") .
+                                     " | ";
+                       }
+                       print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+                                                    hash_base=>$hash, file_name=>$diff->{'file'})},
+                                      "blob") . " | ";
+                       if ($have_blame) {
+                               print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
+                                                            file_name=>$diff->{'file'})},
+                                             "blame") . " | ";
+                       }
+                       print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
+                                                    file_name=>$diff->{'file'})},
+                                     "history");
+                       print "</td>\n";
+
+               } elsif ($diff->{'status'} eq "R" || $diff->{'status'} eq "C") { # renamed or copied
+                       my %status_name = ('R' => 'moved', 'C' => 'copied');
+                       my $nstatus = $status_name{$diff->{'status'}};
+                       my $mode_chng = "";
+                       if ($diff->{'from_mode'} != $diff->{'to_mode'}) {
+                               # mode also for directories, so we cannot use $to_mode_str
+                               $mode_chng = sprintf(", mode: %04o", $to_mode_oct & 0777);
+                       }
+                       print "<td>" .
+                             $cgi->a({-href => href(action=>"blob", hash_base=>$hash,
+                                                    hash=>$diff->{'to_id'}, file_name=>$diff->{'to_file'}),
+                                     -class => "list"}, esc_path($diff->{'to_file'})) . "</td>\n" .
+                             "<td><span class=\"file_status $nstatus\">[$nstatus from " .
+                             $cgi->a({-href => href(action=>"blob", hash_base=>$parent,
+                                                    hash=>$diff->{'from_id'}, file_name=>$diff->{'from_file'}),
+                                     -class => "list"}, esc_path($diff->{'from_file'})) .
+                             " with " . (int $diff->{'similarity'}) . "% similarity$mode_chng]</span></td>\n" .
+                             "<td class=\"link\">";
+                       if ($action eq 'commitdiff') {
+                               # link to patch
+                               $patchno++;
+                               print $cgi->a({-href => href(-anchor=>"patch$patchno")},
+                                             "patch") .
+                                     " | ";
+                       } elsif ($diff->{'to_id'} ne $diff->{'from_id'}) {
+                               # "commit" view and modified file (not only pure rename or copy)
+                               print $cgi->a({-href => href(action=>"blobdiff",
+                                                            hash=>$diff->{'to_id'}, hash_parent=>$diff->{'from_id'},
+                                                            hash_base=>$hash, hash_parent_base=>$parent,
+                                                            file_name=>$diff->{'to_file'}, file_parent=>$diff->{'from_file'})},
+                                             "diff") .
+                                     " | ";
+                       }
+                       print $cgi->a({-href => href(action=>"blob", hash=>$diff->{'to_id'},
+                                                    hash_base=>$parent, file_name=>$diff->{'to_file'})},
+                                     "blob") . " | ";
+                       if ($have_blame) {
+                               print $cgi->a({-href => href(action=>"blame", hash_base=>$hash,
+                                                            file_name=>$diff->{'to_file'})},
+                                             "blame") . " | ";
+                       }
+                       print $cgi->a({-href => href(action=>"history", hash_base=>$hash,
+                                                   file_name=>$diff->{'to_file'})},
+                                     "history");
+                       print "</td>\n";
+
+               } # we should not encounter Unmerged (U) or Unknown (X) status
+               print "</tr>\n";
+       }
+       print "</tbody>" if $has_header;
+       print "</table>\n";
+}
+
+# Print context lines and then rem/add lines in a side-by-side manner.
+sub print_sidebyside_diff_lines {
+       my ($ctx, $rem, $add) = @_;
+
+       # print context block before add/rem block
+       if (@$ctx) {
+               print join '',
+                       '<div class="chunk_block ctx">',
+                               '<div class="old">',
+                               @$ctx,
+                               '</div>',
+                               '<div class="new">',
+                               @$ctx,
+                               '</div>',
+                       '</div>';
+       }
+
+       if (!@$add) {
+               # pure removal
+               print join '',
+                       '<div class="chunk_block rem">',
+                               '<div class="old">',
+                               @$rem,
+                               '</div>',
+                       '</div>';
+       } elsif (!@$rem) {
+               # pure addition
+               print join '',
+                       '<div class="chunk_block add">',
+                               '<div class="new">',
+                               @$add,
+                               '</div>',
+                       '</div>';
+       } else {
+               print join '',
+                       '<div class="chunk_block chg">',
+                               '<div class="old">',
+                               @$rem,
+                               '</div>',
+                               '<div class="new">',
+                               @$add,
+                               '</div>',
+                       '</div>';
+       }
+}
+
+# Print context lines and then rem/add lines in inline manner.
+sub print_inline_diff_lines {
+       my ($ctx, $rem, $add) = @_;
+
+       print @$ctx, @$rem, @$add;
+}
+
+# Format removed and added line, mark changed part and HTML-format them.
+# Implementation is based on contrib/diff-highlight
+sub format_rem_add_lines_pair {
+       my ($rem, $add, $num_parents) = @_;
+
+       # We need to untabify lines before split()'ing them;
+       # otherwise offsets would be invalid.
+       chomp $rem;
+       chomp $add;
+       $rem = untabify($rem);
+       $add = untabify($add);
+
+       my @rem = split(//, $rem);
+       my @add = split(//, $add);
+       my ($esc_rem, $esc_add);
+       # Ignore leading +/- characters for each parent.
+       my ($prefix_len, $suffix_len) = ($num_parents, 0);
+       my ($prefix_has_nonspace, $suffix_has_nonspace);
+
+       my $shorter = (@rem < @add) ? @rem : @add;
+       while ($prefix_len < $shorter) {
+               last if ($rem[$prefix_len] ne $add[$prefix_len]);
+
+               $prefix_has_nonspace = 1 if ($rem[$prefix_len] !~ /\s/);
+               $prefix_len++;
+       }
+
+       while ($prefix_len + $suffix_len < $shorter) {
+               last if ($rem[-1 - $suffix_len] ne $add[-1 - $suffix_len]);
+
+               $suffix_has_nonspace = 1 if ($rem[-1 - $suffix_len] !~ /\s/);
+               $suffix_len++;
+       }
+
+       # Mark lines that are different from each other, but have some common
+       # part that isn't whitespace.  If lines are completely different, don't
+       # mark them because that would make output unreadable, especially if
+       # diff consists of multiple lines.
+       if ($prefix_has_nonspace || $suffix_has_nonspace) {
+               $esc_rem = esc_html_hl_regions($rem, 'marked',
+                       [$prefix_len, @rem - $suffix_len], -nbsp=>1);
+               $esc_add = esc_html_hl_regions($add, 'marked',
+                       [$prefix_len, @add - $suffix_len], -nbsp=>1);
+       } else {
+               $esc_rem = esc_html($rem, -nbsp=>1);
+               $esc_add = esc_html($add, -nbsp=>1);
+       }
+
+       return format_diff_line(\$esc_rem, 'rem'),
+              format_diff_line(\$esc_add, 'add');
+}
+
+# HTML-format diff context, removed and added lines.
+sub format_ctx_rem_add_lines {
+       my ($ctx, $rem, $add, $num_parents) = @_;
+       my (@new_ctx, @new_rem, @new_add);
+       my $can_highlight = 0;
+       my $is_combined = ($num_parents > 1);
+
+       # Highlight if every removed line has a corresponding added line.
+       if (@$add > 0 && @$add == @$rem) {
+               $can_highlight = 1;
+
+               # Highlight lines in combined diff only if the chunk contains
+               # diff between the same version, e.g.
+               #
+               #    - a
+               #   -  b
+               #    + c
+               #   +  d
+               #
+               # Otherwise the highlightling would be confusing.
+               if ($is_combined) {
+                       for (my $i = 0; $i < @$add; $i++) {
+                               my $prefix_rem = substr($rem->[$i], 0, $num_parents);
+                               my $prefix_add = substr($add->[$i], 0, $num_parents);
+
+                               $prefix_rem =~ s/-/+/g;
+
+                               if ($prefix_rem ne $prefix_add) {
+                                       $can_highlight = 0;
+                                       last;
+                               }
+                       }
+               }
+       }
+
+       if ($can_highlight) {
+               for (my $i = 0; $i < @$add; $i++) {
+                       my ($line_rem, $line_add) = format_rem_add_lines_pair(
+                               $rem->[$i], $add->[$i], $num_parents);
+                       push @new_rem, $line_rem;
+                       push @new_add, $line_add;
+               }
+       } else {
+               @new_rem = map { format_diff_line($_, 'rem') } @$rem;
+               @new_add = map { format_diff_line($_, 'add') } @$add;
+       }
+
+       @new_ctx = map { format_diff_line($_, 'ctx') } @$ctx;
+
+       return (\@new_ctx, \@new_rem, \@new_add);
+}
+
+# Print context lines and then rem/add lines.
+sub print_diff_lines {
+       my ($ctx, $rem, $add, $diff_style, $num_parents) = @_;
+       my $is_combined = $num_parents > 1;
+
+       ($ctx, $rem, $add) = format_ctx_rem_add_lines($ctx, $rem, $add,
+               $num_parents);
+
+       if ($diff_style eq 'sidebyside' && !$is_combined) {
+               print_sidebyside_diff_lines($ctx, $rem, $add);
+       } else {
+               # default 'inline' style and unknown styles
+               print_inline_diff_lines($ctx, $rem, $add);
+       }
+}
+
+sub print_diff_chunk {
+       my ($diff_style, $num_parents, $from, $to, @chunk) = @_;
+       my (@ctx, @rem, @add);
+
+       # The class of the previous line.
+       my $prev_class = '';
+
+       return unless @chunk;
+
+       # incomplete last line might be among removed or added lines,
+       # or both, or among context lines: find which
+       for (my $i = 1; $i < @chunk; $i++) {
+               if ($chunk[$i][0] eq 'incomplete') {
+                       $chunk[$i][0] = $chunk[$i-1][0];
+               }
+       }
+
+       # guardian
+       push @chunk, ["", ""];
+
+       foreach my $line_info (@chunk) {
+               my ($class, $line) = @$line_info;
+
+               # print chunk headers
+               if ($class && $class eq 'chunk_header') {
+                       print format_diff_line($line, $class, $from, $to);
+                       next;
+               }
+
+               ## print from accumulator when have some add/rem lines or end
+               # of chunk (flush context lines), or when have add and rem
+               # lines and new block is reached (otherwise add/rem lines could
+               # be reordered)
+               if (!$class || ((@rem || @add) && $class eq 'ctx') ||
+                   (@rem && @add && $class ne $prev_class)) {
+                       print_diff_lines(\@ctx, \@rem, \@add,
+                                        $diff_style, $num_parents);
+                       @ctx = @rem = @add = ();
+               }
+
+               ## adding lines to accumulator
+               # guardian value
+               last unless $line;
+               # rem, add or change
+               if ($class eq 'rem') {
+                       push @rem, $line;
+               } elsif ($class eq 'add') {
+                       push @add, $line;
+               }
+               # context line
+               if ($class eq 'ctx') {
+                       push @ctx, $line;
+               }
+
+               $prev_class = $class;
+       }
+}
+
+sub git_patchset_body {
+       my ($fd, $diff_style, $difftree, $hash, @hash_parents) = @_;
+       my ($hash_parent) = $hash_parents[0];
+
+       my $is_combined = (@hash_parents > 1);
+       my $patch_idx = 0;
+       my $patch_number = 0;
+       my $patch_line;
+       my $diffinfo;
+       my $to_name;
+       my (%from, %to);
+       my @chunk; # for side-by-side diff
+
+       print "<div class=\"patchset\">\n";
+
+       # skip to first patch
+       while ($patch_line = <$fd>) {
+               chomp $patch_line;
+
+               last if ($patch_line =~ m/^diff /);
+       }
+
+ PATCH:
+       while ($patch_line) {
+
+               # parse "git diff" header line
+               if ($patch_line =~ m/^diff --git (\"(?:[^\\\"]*(?:\\.[^\\\"]*)*)\"|[^ "]*) (.*)$/) {
+                       # $1 is from_name, which we do not use
+                       $to_name = unquote($2);
+                       $to_name =~ s!^b/!!;
+               } elsif ($patch_line =~ m/^diff --(cc|combined) ("?.*"?)$/) {
+                       # $1 is 'cc' or 'combined', which we do not use
+                       $to_name = unquote($2);
+               } else {
+                       $to_name = undef;
+               }
+
+               # check if current patch belong to current raw line
+               # and parse raw git-diff line if needed
+               if (is_patch_split($diffinfo, { 'to_file' => $to_name })) {
+                       # this is continuation of a split patch
+                       print "<div class=\"patch cont\">\n";
+               } else {
+                       # advance raw git-diff output if needed
+                       $patch_idx++ if defined $diffinfo;
+
+                       # read and prepare patch information
+                       $diffinfo = parsed_difftree_line($difftree->[$patch_idx]);
+
+                       # compact combined diff output can have some patches skipped
+                       # find which patch (using pathname of result) we are at now;
+                       if ($is_combined) {
+                               while ($to_name ne $diffinfo->{'to_file'}) {
+                                       print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
+                                             format_diff_cc_simplified($diffinfo, @hash_parents) .
+                                             "</div>\n";  # class="patch"
+
+                                       $patch_idx++;
+                                       $patch_number++;
+
+                                       last if $patch_idx > $#$difftree;
+                                       $diffinfo = parsed_difftree_line($difftree->[$patch_idx]);
+                               }
+                       }
+
+                       # modifies %from, %to hashes
+                       parse_from_to_diffinfo($diffinfo, \%from, \%to, @hash_parents);
+
+                       # this is first patch for raw difftree line with $patch_idx index
+                       # we index @$difftree array from 0, but number patches from 1
+                       print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n";
+               }
+
+               # git diff header
+               #assert($patch_line =~ m/^diff /) if DEBUG;
+               #assert($patch_line !~ m!$/$!) if DEBUG; # is chomp-ed
+               $patch_number++;
+               # print "git diff" header
+               print format_git_diff_header_line($patch_line, $diffinfo,
+                                                 \%from, \%to);
+
+               # print extended diff header
+               print "<div class=\"diff extended_header\">\n";
+       EXTENDED_HEADER:
+               while ($patch_line = <$fd>) {
+                       chomp $patch_line;
+
+                       last EXTENDED_HEADER if ($patch_line =~ m/^--- |^diff /);
+
+                       print format_extended_diff_header_line($patch_line, $diffinfo,
+                                                              \%from, \%to);
+               }
+               print "</div>\n"; # class="diff extended_header"
+
+               # from-file/to-file diff header
+               if (! $patch_line) {
+                       print "</div>\n"; # class="patch"
+                       last PATCH;
+               }
+               next PATCH if ($patch_line =~ m/^diff /);
+               #assert($patch_line =~ m/^---/) if DEBUG;
+
+               my $last_patch_line = $patch_line;
+               $patch_line = <$fd>;
+               chomp $patch_line;
+               #assert($patch_line =~ m/^\+\+\+/) if DEBUG;
+
+               print format_diff_from_to_header($last_patch_line, $patch_line,
+                                                $diffinfo, \%from, \%to,
+                                                @hash_parents);
+
+               # the patch itself
+       LINE:
+               while ($patch_line = <$fd>) {
+                       chomp $patch_line;
+
+                       next PATCH if ($patch_line =~ m/^diff /);
+
+                       my $class = diff_line_class($patch_line, \%from, \%to);
+
+                       if ($class eq 'chunk_header') {
+                               print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
+                               @chunk = ();
+                       }
+
+                       push @chunk, [ $class, $patch_line ];
+               }
+
+       } continue {
+               if (@chunk) {
+                       print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
+                       @chunk = ();
+               }
+               print "</div>\n"; # class="patch"
+       }
+
+       # for compact combined (--cc) format, with chunk and patch simplification
+       # the patchset might be empty, but there might be unprocessed raw lines
+       for (++$patch_idx if $patch_number > 0;
+            $patch_idx < @$difftree;
+            ++$patch_idx) {
+               # read and prepare patch information
+               $diffinfo = parsed_difftree_line($difftree->[$patch_idx]);
+
+               # generate anchor for "patch" links in difftree / whatchanged part
+               print "<div class=\"patch\" id=\"patch". ($patch_idx+1) ."\">\n" .
+                     format_diff_cc_simplified($diffinfo, @hash_parents) .
+                     "</div>\n";  # class="patch"
+
+               $patch_number++;
+       }
+
+       if ($patch_number == 0) {
+               if (@hash_parents > 1) {
+                       print "<div class=\"diff nodifferences\">Trivial merge</div>\n";
+               } else {
+                       print "<div class=\"diff nodifferences\">No differences found</div>\n";
+               }
+       }
+
+       print "</div>\n"; # class="patchset"
+}
+
+# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+sub git_project_search_form {
+       my ($searchtext, $search_use_regexp) = @_;
+
+       my $limit = '';
+       if ($project_filter) {
+               $limit = " in '$project_filter/'";
+       }
+
+       print "<div class=\"projsearch\">\n";
+       print $cgi->start_form(-method => 'get', -action => $my_uri) .
+             $cgi->hidden(-name => 'a', -value => 'project_list')  . "\n";
+       print $cgi->hidden(-name => 'pf', -value => $project_filter). "\n"
+               if (defined $project_filter);
+       print $cgi->textfield(-name => 's', -value => $searchtext,
+                             -title => "Search project by name and description$limit",
+                             -size => 60) . "\n" .
+             "<span title=\"Extended regular expression\">" .
+             $cgi->checkbox(-name => 'sr', -value => 1, -label => 're',
+                            -checked => $search_use_regexp) .
+             "</span>\n" .
+             $cgi->submit(-name => 'btnS', -value => 'Search') .
+             $cgi->end_form() . "\n" .
+             $cgi->a({-href => href(project => undef, searchtext => undef,
+                                    project_filter => $project_filter)},
+                     esc_html("List all projects$limit")) . "<br />\n";
+       print "</div>\n";
+}
+
+# entry for given @keys needs filling if at least one of keys in list
+# is not present in %$project_info
+sub project_info_needs_filling {
+       my ($project_info, @keys) = @_;
+
+       # return List::MoreUtils::any { !exists $project_info->{$_} } @keys;
+       foreach my $key (@keys) {
+               if (!exists $project_info->{$key}) {
+                       return 1;
+               }
+       }
+       return;
+}
+
+# fills project list info (age, description, owner, category, forks, etc.)
+# for each project in the list, removing invalid projects from
+# returned list, or fill only specified info.
+#
+# Invalid projects are removed from the returned list if and only if you
+# ask 'age' or 'age_string' to be filled, because they are the only fields
+# that run unconditionally git command that requires repository, and
+# therefore do always check if project repository is invalid.
+#
+# USAGE:
+# * fill_project_list_info(\@project_list, 'descr_long', 'ctags')
+#   ensures that 'descr_long' and 'ctags' fields are filled
+# * @project_list = fill_project_list_info(\@project_list)
+#   ensures that all fields are filled (and invalid projects removed)
+#
+# NOTE: modifies $projlist, but does not remove entries from it
+sub fill_project_list_info {
+       my ($projlist, @wanted_keys) = @_;
+       my @projects;
+       my $filter_set = sub { return @_; };
+       if (@wanted_keys) {
+               my %wanted_keys = map { $_ => 1 } @wanted_keys;
+               $filter_set = sub { return grep { $wanted_keys{$_} } @_; };
+       }
+
+       my $show_ctags = gitweb_check_feature('ctags');
+ PROJECT:
+       foreach my $pr (@$projlist) {
+               if (project_info_needs_filling($pr, $filter_set->('age', 'age_string'))) {
+                       my (@activity) = git_get_last_activity($pr->{'path'});
+                       unless (@activity) {
+                               next PROJECT;
+                       }
+                       ($pr->{'age'}, $pr->{'age_string'}) = @activity;
+               }
+               if (project_info_needs_filling($pr, $filter_set->('descr', 'descr_long'))) {
+                       my $descr = git_get_project_description($pr->{'path'}) || "";
+                       $descr = to_utf8($descr);
+                       $pr->{'descr_long'} = $descr;
+                       $pr->{'descr'} = chop_str($descr, $projects_list_description_width, 5);
+               }
+               if (project_info_needs_filling($pr, $filter_set->('owner'))) {
+                       $pr->{'owner'} = git_get_project_owner("$pr->{'path'}") || "";
+               }
+               if ($show_ctags &&
+                   project_info_needs_filling($pr, $filter_set->('ctags'))) {
+                       $pr->{'ctags'} = git_get_project_ctags($pr->{'path'});
+               }
+               if ($projects_list_group_categories &&
+                   project_info_needs_filling($pr, $filter_set->('category'))) {
+                       my $cat = git_get_project_category($pr->{'path'}) ||
+                                                          $project_list_default_category;
+                       $pr->{'category'} = to_utf8($cat);
+               }
+
+               push @projects, $pr;
+       }
+
+       return @projects;
+}
+
+sub sort_projects_list {
+       my ($projlist, $order) = @_;
+
+       sub order_str {
+               my $key = shift;
+               return sub { $a->{$key} cmp $b->{$key} };
+       }
+
+       sub order_num_then_undef {
+               my $key = shift;
+               return sub {
+                       defined $a->{$key} ?
+                               (defined $b->{$key} ? $a->{$key} <=> $b->{$key} : -1) :
+                               (defined $b->{$key} ? 1 : 0)
+               };
+       }
+
+       my %orderings = (
+               project => order_str('path'),
+               descr => order_str('descr_long'),
+               owner => order_str('owner'),
+               age => order_num_then_undef('age'),
+       );
+
+       my $ordering = $orderings{$order};
+       return defined $ordering ? sort $ordering @$projlist : @$projlist;
+}
+
+# returns a hash of categories, containing the list of project
+# belonging to each category
+sub build_projlist_by_category {
+       my ($projlist, $from, $to) = @_;
+       my %categories;
+
+       $from = 0 unless defined $from;
+       $to = $#$projlist if (!defined $to || $#$projlist < $to);
+
+       for (my $i = $from; $i <= $to; $i++) {
+               my $pr = $projlist->[$i];
+               push @{$categories{ $pr->{'category'} }}, $pr;
+       }
+
+       return wantarray ? %categories : \%categories;
+}
+
+# print 'sort by' <th> element, generating 'sort by $name' replay link
+# if that order is not selected
+sub print_sort_th {
+       print format_sort_th(@_);
+}
+
+sub format_sort_th {
+       my ($name, $order, $header) = @_;
+       my $sort_th = "";
+       $header ||= ucfirst($name);
+
+       if ($order eq $name) {
+               $sort_th .= "<th>$header</th>\n";
+       } else {
+               $sort_th .= "<th>" .
+                           $cgi->a({-href => href(-replay=>1, order=>$name),
+                                    -class => "header"}, $header) .
+                           "</th>\n";
+       }
+
+       return $sort_th;
+}
+
+sub git_project_list_rows {
+       my ($projlist, $from, $to, $check_forks) = @_;
+
+       $from = 0 unless defined $from;
+       $to = $#$projlist if (!defined $to || $#$projlist < $to);
+
+       my $alternate = 1;
+       for (my $i = $from; $i <= $to; $i++) {
+               my $pr = $projlist->[$i];
+
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+
+               if ($check_forks) {
+                       print "<td>";
+                       if ($pr->{'forks'}) {
+                               my $nforks = scalar @{$pr->{'forks'}};
+                               if ($nforks > 0) {
+                                       print $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks"),
+                                                      -title => "$nforks forks"}, "+");
+                               } else {
+                                       print $cgi->span({-title => "$nforks forks"}, "+");
+                               }
+                       }
+                       print "</td>\n";
+               }
+               print "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
+                                       -class => "list"},
+                                      esc_html_match_hl($pr->{'path'}, $search_regexp)) .
+                     "</td>\n" .
+                     "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
+                                       -class => "list",
+                                       -title => $pr->{'descr_long'}},
+                                       $search_regexp
+                                       ? esc_html_match_hl_chopped($pr->{'descr_long'},
+                                                                   $pr->{'descr'}, $search_regexp)
+                                       : esc_html($pr->{'descr'})) .
+                     "</td>\n";
+               unless ($omit_owner) {
+                       print "<td><i>" . chop_and_escape_str($pr->{'owner'}, 15) . "</i></td>\n";
+               }
+               unless ($omit_age_column) {
+                       print "<td class=\"". age_class($pr->{'age'}) . "\">" .
+                           (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "</td>\n";
+               }
+               print"<td class=\"link\">" .
+                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary")}, "summary")   . " | " .
+                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"shortlog")}, "shortlog") . " | " .
+                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"log")}, "log") . " | " .
+                     $cgi->a({-href => href(project=>$pr->{'path'}, action=>"tree")}, "tree") .
+                     ($pr->{'forks'} ? " | " . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"forks")}, "forks") : '') .
+                     "</td>\n" .
+                     "</tr>\n";
+       }
+}
+
+sub git_project_list_body {
+       # actually uses global variable $project
+       my ($projlist, $order, $from, $to, $extra, $no_header) = @_;
+       my @projects = @$projlist;
+
+       my $check_forks = gitweb_check_feature('forks');
+       my $show_ctags  = gitweb_check_feature('ctags');
+       my $tagfilter = $show_ctags ? $input_params{'ctag'} : undef;
+       $check_forks = undef
+               if ($tagfilter || $search_regexp);
+
+       # filtering out forks before filling info allows to do less work
+       @projects = filter_forks_from_projects_list(\@projects)
+               if ($check_forks);
+       # search_projects_list pre-fills required info
+       @projects = search_projects_list(\@projects,
+                                        'search_regexp' => $search_regexp,
+                                        'tagfilter'  => $tagfilter)
+               if ($tagfilter || $search_regexp);
+       # fill the rest
+       my @all_fields = ('descr', 'descr_long', 'ctags', 'category');
+       push @all_fields, ('age', 'age_string') unless($omit_age_column);
+       push @all_fields, 'owner' unless($omit_owner);
+       @projects = fill_project_list_info(\@projects, @all_fields);
+
+       $order ||= $default_projects_order;
+       $from = 0 unless defined $from;
+       $to = $#projects if (!defined $to || $#projects < $to);
+
+       # short circuit
+       if ($from > $to) {
+               print "<center>\n".
+                     "<b>No such projects found</b><br />\n".
+                     "Click ".$cgi->a({-href=>href(project=>undef)},"here")." to view all projects<br />\n".
+                     "</center>\n<br />\n";
+               return;
+       }
+
+       @projects = sort_projects_list(\@projects, $order);
+
+       if ($show_ctags) {
+               my $ctags = git_gather_all_ctags(\@projects);
+               my $cloud = git_populate_project_tagcloud($ctags);
+               print git_show_project_tagcloud($cloud, 64);
+       }
+
+       print "<table class=\"project_list\">\n";
+       unless ($no_header) {
+               print "<tr>\n";
+               if ($check_forks) {
+                       print "<th></th>\n";
+               }
+               print_sort_th('project', $order, 'Project');
+               print_sort_th('descr', $order, 'Description');
+               print_sort_th('owner', $order, 'Owner') unless $omit_owner;
+               print_sort_th('age', $order, 'Last Change') unless $omit_age_column;
+               print "<th></th>\n" . # for links
+                     "</tr>\n";
+       }
+
+       if ($projects_list_group_categories) {
+               # only display categories with projects in the $from-$to window
+               @projects = sort {$a->{'category'} cmp $b->{'category'}} @projects[$from..$to];
+               my %categories = build_projlist_by_category(\@projects, $from, $to);
+               foreach my $cat (sort keys %categories) {
+                       unless ($cat eq "") {
+                               print "<tr>\n";
+                               if ($check_forks) {
+                                       print "<td></td>\n";
+                               }
+                               print "<td class=\"category\" colspan=\"5\">".esc_html($cat)."</td>\n";
+                               print "</tr>\n";
+                       }
+
+                       git_project_list_rows($categories{$cat}, undef, undef, $check_forks);
+               }
+       } else {
+               git_project_list_rows(\@projects, $from, $to, $check_forks);
+       }
+
+       if (defined $extra) {
+               print "<tr>\n";
+               if ($check_forks) {
+                       print "<td></td>\n";
+               }
+               print "<td colspan=\"5\">$extra</td>\n" .
+                     "</tr>\n";
+       }
+       print "</table>\n";
+}
+
+sub git_log_body {
+       # uses global variable $project
+       my ($commitlist, $from, $to, $refs, $extra) = @_;
+
+       $from = 0 unless defined $from;
+       $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
+
+       for (my $i = 0; $i <= $to; $i++) {
+               my %co = %{$commitlist->[$i]};
+               next if !%co;
+               my $commit = $co{'id'};
+               my $ref = format_ref_marker($refs, $commit);
+               git_print_header_div('commit',
+                              "<span class=\"age\">$co{'age_string'}</span>" .
+                              esc_html($co{'title'}) . $ref,
+                              $commit);
+               print "<div class=\"title_text\">\n" .
+                     "<div class=\"log_link\">\n" .
+                     $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") .
+                     " | " .
+                     $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") .
+                     " | " .
+                     $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
+                     "<br/>\n" .
+                     "</div>\n";
+                     git_print_authorship(\%co, -tag => 'span');
+                     print "<br/>\n</div>\n";
+
+               print "<div class=\"log_body\">\n";
+               git_print_log($co{'comment'}, -final_empty_line=> 1);
+               print "</div>\n";
+       }
+       if ($extra) {
+               print "<div class=\"page_nav\">\n";
+               print "$extra\n";
+               print "</div>\n";
+       }
+}
+
+sub git_shortlog_body {
+       # uses global variable $project
+       my ($commitlist, $from, $to, $refs, $extra) = @_;
+
+       $from = 0 unless defined $from;
+       $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
+
+       print "<table class=\"shortlog\">\n";
+       my $alternate = 1;
+       for (my $i = $from; $i <= $to; $i++) {
+               my %co = %{$commitlist->[$i]};
+               my $commit = $co{'id'};
+               my $ref = format_ref_marker($refs, $commit);
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+               # git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
+               print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+                     format_author_html('td', \%co, 10) . "<td>";
+               print format_subject_html($co{'title'}, $co{'title_short'},
+                                         href(action=>"commit", hash=>$commit), $ref);
+               print "</td>\n" .
+                     "<td class=\"link\">" .
+                     $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
+                     $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
+                     $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
+               my $snapshot_links = format_snapshot_links($commit);
+               if (defined $snapshot_links) {
+                       print " | " . $snapshot_links;
+               }
+               print "</td>\n" .
+                     "</tr>\n";
+       }
+       if (defined $extra) {
+               print "<tr>\n" .
+                     "<td colspan=\"4\">$extra</td>\n" .
+                     "</tr>\n";
+       }
+       print "</table>\n";
+}
+
+sub git_history_body {
+       # Warning: assumes constant type (blob or tree) during history
+       my ($commitlist, $from, $to, $refs, $extra,
+           $file_name, $file_hash, $ftype) = @_;
+
+       $from = 0 unless defined $from;
+       $to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist});
+
+       print "<table class=\"history\">\n";
+       my $alternate = 1;
+       for (my $i = $from; $i <= $to; $i++) {
+               my %co = %{$commitlist->[$i]};
+               if (!%co) {
+                       next;
+               }
+               my $commit = $co{'id'};
+
+               my $ref = format_ref_marker($refs, $commit);
+
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+               print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+       # shortlog:   format_author_html('td', \%co, 10)
+                     format_author_html('td', \%co, 15, 3) . "<td>";
+               # originally git_history used chop_str($co{'title'}, 50)
+               print format_subject_html($co{'title'}, $co{'title_short'},
+                                         href(action=>"commit", hash=>$commit), $ref);
+               print "</td>\n" .
+                     "<td class=\"link\">" .
+                     $cgi->a({-href => href(action=>$ftype, hash_base=>$commit, file_name=>$file_name)}, $ftype) . " | " .
+                     $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff");
+
+               if ($ftype eq 'blob') {
+                       my $blob_current = $file_hash;
+                       my $blob_parent  = git_get_hash_by_path($commit, $file_name);
+                       if (defined $blob_current && defined $blob_parent &&
+                                       $blob_current ne $blob_parent) {
+                               print " | " .
+                                       $cgi->a({-href => href(action=>"blobdiff",
+                                                              hash=>$blob_current, hash_parent=>$blob_parent,
+                                                              hash_base=>$hash_base, hash_parent_base=>$commit,
+                                                              file_name=>$file_name)},
+                                               "diff to current");
+                       }
+               }
+               print "</td>\n" .
+                     "</tr>\n";
+       }
+       if (defined $extra) {
+               print "<tr>\n" .
+                     "<td colspan=\"4\">$extra</td>\n" .
+                     "</tr>\n";
+       }
+       print "</table>\n";
+}
+
+sub git_tags_body {
+       # uses global variable $project
+       my ($taglist, $from, $to, $extra) = @_;
+       $from = 0 unless defined $from;
+       $to = $#{$taglist} if (!defined $to || $#{$taglist} < $to);
+
+       print "<table class=\"tags\">\n";
+       my $alternate = 1;
+       for (my $i = $from; $i <= $to; $i++) {
+               my $entry = $taglist->[$i];
+               my %tag = %$entry;
+               my $comment = $tag{'subject'};
+               my $comment_short;
+               if (defined $comment) {
+                       $comment_short = chop_str($comment, 30, 5);
+               }
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+               if (defined $tag{'age'}) {
+                       print "<td><i>$tag{'age'}</i></td>\n";
+               } else {
+                       print "<td></td>\n";
+               }
+               print "<td>" .
+                     $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'}),
+                              -class => "list name"}, esc_html($tag{'name'})) .
+                     "</td>\n" .
+                     "<td>";
+               if (defined $comment) {
+                       print format_subject_html($comment, $comment_short,
+                                                 href(action=>"tag", hash=>$tag{'id'}));
+               }
+               print "</td>\n" .
+                     "<td class=\"selflink\">";
+               if ($tag{'type'} eq "tag") {
+                       print $cgi->a({-href => href(action=>"tag", hash=>$tag{'id'})}, "tag");
+               } else {
+                       print "&nbsp;";
+               }
+               print "</td>\n" .
+                     "<td class=\"link\">" . " | " .
+                     $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'})}, $tag{'reftype'});
+               if ($tag{'reftype'} eq "commit") {
+                       print " | " . $cgi->a({-href => href(action=>"shortlog", hash=>$tag{'fullname'})}, "shortlog") .
+                             " | " . $cgi->a({-href => href(action=>"log", hash=>$tag{'fullname'})}, "log");
+               } elsif ($tag{'reftype'} eq "blob") {
+                       print " | " . $cgi->a({-href => href(action=>"blob_plain", hash=>$tag{'refid'})}, "raw");
+               }
+               print "</td>\n" .
+                     "</tr>";
+       }
+       if (defined $extra) {
+               print "<tr>\n" .
+                     "<td colspan=\"5\">$extra</td>\n" .
+                     "</tr>\n";
+       }
+       print "</table>\n";
+}
+
+sub git_heads_body {
+       # uses global variable $project
+       my ($headlist, $head_at, $from, $to, $extra) = @_;
+       $from = 0 unless defined $from;
+       $to = $#{$headlist} if (!defined $to || $#{$headlist} < $to);
+
+       print "<table class=\"heads\">\n";
+       my $alternate = 1;
+       for (my $i = $from; $i <= $to; $i++) {
+               my $entry = $headlist->[$i];
+               my %ref = %$entry;
+               my $curr = defined $head_at && $ref{'id'} eq $head_at;
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+               print "<td><i>$ref{'age'}</i></td>\n" .
+                     ($curr ? "<td class=\"current_head\">" : "<td>") .
+                     $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'}),
+                              -class => "list name"},esc_html($ref{'name'})) .
+                     "</td>\n" .
+                     "<td class=\"link\">" .
+                     $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'})}, "shortlog") . " | " .
+                     $cgi->a({-href => href(action=>"log", hash=>$ref{'fullname'})}, "log") . " | " .
+                     $cgi->a({-href => href(action=>"tree", hash=>$ref{'fullname'}, hash_base=>$ref{'fullname'})}, "tree") .
+                     "</td>\n" .
+                     "</tr>";
+       }
+       if (defined $extra) {
+               print "<tr>\n" .
+                     "<td colspan=\"3\">$extra</td>\n" .
+                     "</tr>\n";
+       }
+       print "</table>\n";
+}
+
+# Display a single remote block
+sub git_remote_block {
+       my ($remote, $rdata, $limit, $head) = @_;
+
+       my $heads = $rdata->{'heads'};
+       my $fetch = $rdata->{'fetch'};
+       my $push = $rdata->{'push'};
+
+       my $urls_table = "<table class=\"projects_list\">\n" ;
+
+       if (defined $fetch) {
+               if ($fetch eq $push) {
+                       $urls_table .= format_repo_url("URL", $fetch);
+               } else {
+                       $urls_table .= format_repo_url("Fetch URL", $fetch);
+                       $urls_table .= format_repo_url("Push URL", $push) if defined $push;
+               }
+       } elsif (defined $push) {
+               $urls_table .= format_repo_url("Push URL", $push);
+       } else {
+               $urls_table .= format_repo_url("", "No remote URL");
+       }
+
+       $urls_table .= "</table>\n";
+
+       my $dots;
+       if (defined $limit && $limit < @$heads) {
+               $dots = $cgi->a({-href => href(action=>"remotes", hash=>$remote)}, "...");
+       }
+
+       print $urls_table;
+       git_heads_body($heads, $head, 0, $limit, $dots);
+}
+
+# Display a list of remote names with the respective fetch and push URLs
+sub git_remotes_list {
+       my ($remotedata, $limit) = @_;
+       print "<table class=\"heads\">\n";
+       my $alternate = 1;
+       my @remotes = sort keys %$remotedata;
+
+       my $limited = $limit && $limit < @remotes;
+
+       $#remotes = $limit - 1 if $limited;
+
+       while (my $remote = shift @remotes) {
+               my $rdata = $remotedata->{$remote};
+               my $fetch = $rdata->{'fetch'};
+               my $push = $rdata->{'push'};
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+               print "<td>" .
+                     $cgi->a({-href=> href(action=>'remotes', hash=>$remote),
+                              -class=> "list name"},esc_html($remote)) .
+                     "</td>";
+               print "<td class=\"link\">" .
+                     (defined $fetch ? $cgi->a({-href=> $fetch}, "fetch") : "fetch") .
+                     " | " .
+                     (defined $push ? $cgi->a({-href=> $push}, "push") : "push") .
+                     "</td>";
+
+               print "</tr>\n";
+       }
+
+       if ($limited) {
+               print "<tr>\n" .
+                     "<td colspan=\"3\">" .
+                     $cgi->a({-href => href(action=>"remotes")}, "...") .
+                     "</td>\n" . "</tr>\n";
+       }
+
+       print "</table>";
+}
+
+# Display remote heads grouped by remote, unless there are too many
+# remotes, in which case we only display the remote names
+sub git_remotes_body {
+       my ($remotedata, $limit, $head) = @_;
+       if ($limit and $limit < keys %$remotedata) {
+               git_remotes_list($remotedata, $limit);
+       } else {
+               fill_remote_heads($remotedata);
+               while (my ($remote, $rdata) = each %$remotedata) {
+                       git_print_section({-class=>"remote", -id=>$remote},
+                               ["remotes", $remote, $remote], sub {
+                                       git_remote_block($remote, $rdata, $limit, $head);
+                               });
+               }
+       }
+}
+
+sub git_search_message {
+       my %co = @_;
+
+       my $greptype;
+       if ($searchtype eq 'commit') {
+               $greptype = "--grep=";
+       } elsif ($searchtype eq 'author') {
+               $greptype = "--author=";
+       } elsif ($searchtype eq 'committer') {
+               $greptype = "--committer=";
+       }
+       $greptype .= $searchtext;
+       my @commitlist = parse_commits($hash, 101, (100 * $page), undef,
+                                      $greptype, '--regexp-ignore-case',
+                                      $search_use_regexp ? '--extended-regexp' : '--fixed-strings');
+
+       my $paging_nav = '';
+       if ($page > 0) {
+               $paging_nav .=
+                       $cgi->a({-href => href(-replay=>1, page=>undef)},
+                               "first") .
+                       " &sdot; " .
+                       $cgi->a({-href => href(-replay=>1, page=>$page-1),
+                                -accesskey => "p", -title => "Alt-p"}, "prev");
+       } else {
+               $paging_nav .= "first &sdot; prev";
+       }
+       my $next_link = '';
+       if ($#commitlist >= 100) {
+               $next_link =
+                       $cgi->a({-href => href(-replay=>1, page=>$page+1),
+                                -accesskey => "n", -title => "Alt-n"}, "next");
+               $paging_nav .= " &sdot; $next_link";
+       } else {
+               $paging_nav .= " &sdot; next";
+       }
+
+       git_header_html();
+
+       git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
+       git_print_header_div('commit', esc_html($co{'title'}), $hash);
+       if ($page == 0 && !@commitlist) {
+               print "<p>No match.</p>\n";
+       } else {
+               git_search_grep_body(\@commitlist, 0, 99, $next_link);
+       }
+
+       git_footer_html();
+}
+
+sub git_search_changes {
+       my %co = @_;
+
+       local $/ = "\n";
+       open my $fd, '-|', git_cmd(), '--no-pager', 'log', @diff_opts,
+               '--pretty=format:%H', '--no-abbrev', '--raw', "-S$searchtext",
+               ($search_use_regexp ? '--pickaxe-regex' : ())
+                       or die_error(500, "Open git-log failed");
+
+       git_header_html();
+
+       git_print_page_nav('','', $hash,$co{'tree'},$hash);
+       git_print_header_div('commit', esc_html($co{'title'}), $hash);
+
+       print "<table class=\"pickaxe search\">\n";
+       my $alternate = 1;
+       undef %co;
+       my @files;
+       while (my $line = <$fd>) {
+               chomp $line;
+               next unless $line;
+
+               my %set = parse_difftree_raw_line($line);
+               if (defined $set{'commit'}) {
+                       # finish previous commit
+                       if (%co) {
+                               print "</td>\n" .
+                                     "<td class=\"link\">" .
+                                     $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})},
+                                             "commit") .
+                                     " | " .
+                                     $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'},
+                                                            hash_base=>$co{'id'})},
+                                             "tree") .
+                                     "</td>\n" .
+                                     "</tr>\n";
+                       }
+
+                       if ($alternate) {
+                               print "<tr class=\"dark\">\n";
+                       } else {
+                               print "<tr class=\"light\">\n";
+                       }
+                       $alternate ^= 1;
+                       %co = parse_commit($set{'commit'});
+                       my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
+                       print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+                             "<td><i>$author</i></td>\n" .
+                             "<td>" .
+                             $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
+                                     -class => "list subject"},
+                                     chop_and_escape_str($co{'title'}, 50) . "<br/>");
+               } elsif (defined $set{'to_id'}) {
+                       next if ($set{'to_id'} =~ m/^0{40}$/);
+
+                       print $cgi->a({-href => href(action=>"blob", hash_base=>$co{'id'},
+                                                    hash=>$set{'to_id'}, file_name=>$set{'to_file'}),
+                                     -class => "list"},
+                                     "<span class=\"match\">" . esc_path($set{'file'}) . "</span>") .
+                             "<br/>\n";
+               }
+       }
+       close $fd;
+
+       # finish last commit (warning: repetition!)
+       if (%co) {
+               print "</td>\n" .
+                     "<td class=\"link\">" .
+                     $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})},
+                             "commit") .
+                     " | " .
+                     $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'},
+                                            hash_base=>$co{'id'})},
+                             "tree") .
+                     "</td>\n" .
+                     "</tr>\n";
+       }
+
+       print "</table>\n";
+
+       git_footer_html();
+}
+
+sub git_search_files {
+       my %co = @_;
+
+       local $/ = "\n";
+       open my $fd, "-|", git_cmd(), 'grep', '-n', '-z',
+               $search_use_regexp ? ('-E', '-i') : '-F',
+               $searchtext, $co{'tree'}
+                       or die_error(500, "Open git-grep failed");
+
+       git_header_html();
+
+       git_print_page_nav('','', $hash,$co{'tree'},$hash);
+       git_print_header_div('commit', esc_html($co{'title'}), $hash);
+
+       print "<table class=\"grep_search\">\n";
+       my $alternate = 1;
+       my $matches = 0;
+       my $lastfile = '';
+       my $file_href;
+       while (my $line = <$fd>) {
+               chomp $line;
+               my ($file, $lno, $ltext, $binary);
+               last if ($matches++ > 1000);
+               if ($line =~ /^Binary file (.+) matches$/) {
+                       $file = $1;
+                       $binary = 1;
+               } else {
+                       ($file, $lno, $ltext) = split(/\0/, $line, 3);
+                       $file =~ s/^$co{'tree'}://;
+               }
+               if ($file ne $lastfile) {
+                       $lastfile and print "</td></tr>\n";
+                       if ($alternate++) {
+                               print "<tr class=\"dark\">\n";
+                       } else {
+                               print "<tr class=\"light\">\n";
+                       }
+                       $file_href = href(action=>"blob", hash_base=>$co{'id'},
+                                         file_name=>$file);
+                       print "<td class=\"list\">".
+                               $cgi->a({-href => $file_href, -class => "list"}, esc_path($file));
+                       print "</td><td>\n";
+                       $lastfile = $file;
+               }
+               if ($binary) {
+                       print "<div class=\"binary\">Binary file</div>\n";
+               } else {
+                       $ltext = untabify($ltext);
+                       if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) {
+                               $ltext = esc_html($1, -nbsp=>1);
+                               $ltext .= '<span class="match">';
+                               $ltext .= esc_html($2, -nbsp=>1);
+                               $ltext .= '</span>';
+                               $ltext .= esc_html($3, -nbsp=>1);
+                       } else {
+                               $ltext = esc_html($ltext, -nbsp=>1);
+                       }
+                       print "<div class=\"pre\">" .
+                               $cgi->a({-href => $file_href.'#l'.$lno,
+                                       -class => "linenr"}, sprintf('%4i', $lno)) .
+                               ' ' .  $ltext . "</div>\n";
+               }
+       }
+       if ($lastfile) {
+               print "</td></tr>\n";
+               if ($matches > 1000) {
+                       print "<div class=\"diff nodifferences\">Too many matches, listing trimmed</div>\n";
+               }
+       } else {
+               print "<div class=\"diff nodifferences\">No matches found</div>\n";
+       }
+       close $fd;
+
+       print "</table>\n";
+
+       git_footer_html();
+}
+
+sub git_search_grep_body {
+       my ($commitlist, $from, $to, $extra) = @_;
+       $from = 0 unless defined $from;
+       $to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
+
+       print "<table class=\"commit_search\">\n";
+       my $alternate = 1;
+       for (my $i = $from; $i <= $to; $i++) {
+               my %co = %{$commitlist->[$i]};
+               if (!%co) {
+                       next;
+               }
+               my $commit = $co{'id'};
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+               print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+                     format_author_html('td', \%co, 15, 5) .
+                     "<td>" .
+                     $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
+                              -class => "list subject"},
+                             chop_and_escape_str($co{'title'}, 50) . "<br/>");
+               my $comment = $co{'comment'};
+               foreach my $line (@$comment) {
+                       if ($line =~ m/^(.*?)($search_regexp)(.*)$/i) {
+                               my ($lead, $match, $trail) = ($1, $2, $3);
+                               $match = chop_str($match, 70, 5, 'center');
+                               my $contextlen = int((80 - length($match))/2);
+                               $contextlen = 30 if ($contextlen > 30);
+                               $lead  = chop_str($lead,  $contextlen, 10, 'left');
+                               $trail = chop_str($trail, $contextlen, 10, 'right');
+
+                               $lead  = esc_html($lead);
+                               $match = esc_html($match);
+                               $trail = esc_html($trail);
+
+                               print "$lead<span class=\"match\">$match</span>$trail<br />";
+                       }
+               }
+               print "</td>\n" .
+                     "<td class=\"link\">" .
+                     $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
+                     " | " .
+                     $cgi->a({-href => href(action=>"commitdiff", hash=>$co{'id'})}, "commitdiff") .
+                     " | " .
+                     $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
+               print "</td>\n" .
+                     "</tr>\n";
+       }
+       if (defined $extra) {
+               print "<tr>\n" .
+                     "<td colspan=\"3\">$extra</td>\n" .
+                     "</tr>\n";
+       }
+       print "</table>\n";
+}
+
+## ======================================================================
+## ======================================================================
+## actions
+
+sub git_project_list {
+       my $order = $input_params{'order'};
+       if (defined $order && $order !~ m/none|project|descr|owner|age/) {
+               die_error(400, "Unknown order parameter");
+       }
+
+       my @list = git_get_projects_list($project_filter, $strict_export);
+       if (!@list) {
+               die_error(404, "No projects found");
+       }
+
+       git_header_html();
+       if (defined $home_text && -f $home_text) {
+               print "<div class=\"index_include\">\n";
+               insert_file($home_text);
+               print "</div>\n";
+       }
+
+       git_project_search_form($searchtext, $search_use_regexp);
+       git_project_list_body(\@list, $order);
+       git_footer_html();
+}
+
+sub git_forks {
+       my $order = $input_params{'order'};
+       if (defined $order && $order !~ m/none|project|descr|owner|age/) {
+               die_error(400, "Unknown order parameter");
+       }
+
+       my $filter = $project;
+       $filter =~ s/\.git$//;
+       my @list = git_get_projects_list($filter);
+       if (!@list) {
+               die_error(404, "No forks found");
+       }
+
+       git_header_html();
+       git_print_page_nav('','');
+       git_print_header_div('summary', "$project forks");
+       git_project_list_body(\@list, $order);
+       git_footer_html();
+}
+
+sub git_project_index {
+       my @projects = git_get_projects_list($project_filter, $strict_export);
+       if (!@projects) {
+               die_error(404, "No projects found");
+       }
+
+       print $cgi->header(
+               -type => 'text/plain',
+               -charset => 'utf-8',
+               -content_disposition => 'inline; filename="index.aux"');
+
+       foreach my $pr (@projects) {
+               if (!exists $pr->{'owner'}) {
+                       $pr->{'owner'} = git_get_project_owner("$pr->{'path'}");
+               }
+
+               my ($path, $owner) = ($pr->{'path'}, $pr->{'owner'});
+               # quote as in CGI::Util::encode, but keep the slash, and use '+' for ' '
+               $path  =~ s/([^a-zA-Z0-9_.\-\/ ])/sprintf("%%%02X", ord($1))/eg;
+               $owner =~ s/([^a-zA-Z0-9_.\-\/ ])/sprintf("%%%02X", ord($1))/eg;
+               $path  =~ s/ /\+/g;
+               $owner =~ s/ /\+/g;
+
+               print "$path $owner\n";
+       }
+}
+
+sub git_summary {
+       my $descr = git_get_project_description($project) || "none";
+       my %co = parse_commit("HEAD");
+       my %cd = %co ? parse_date($co{'committer_epoch'}, $co{'committer_tz'}) : ();
+       my $head = $co{'id'};
+       my $remote_heads = gitweb_check_feature('remote_heads');
+
+       my $owner = git_get_project_owner($project);
+
+       my $refs = git_get_references();
+       # These get_*_list functions return one more to allow us to see if
+       # there are more ...
+       my @taglist  = git_get_tags_list(16);
+       my @headlist = git_get_heads_list(16);
+       my %remotedata = $remote_heads ? git_get_remotes_list() : ();
+       my @forklist;
+       my $check_forks = gitweb_check_feature('forks');
+
+       if ($check_forks) {
+               # find forks of a project
+               my $filter = $project;
+               $filter =~ s/\.git$//;
+               @forklist = git_get_projects_list($filter);
+               # filter out forks of forks
+               @forklist = filter_forks_from_projects_list(\@forklist)
+                       if (@forklist);
+       }
+
+       git_header_html();
+       git_print_page_nav('summary','', $head);
+
+       print "<div class=\"title\">&nbsp;</div>\n";
+       print "<table class=\"projects_list\">\n" .
+             "<tr id=\"metadata_desc\"><td>description</td><td>" . esc_html($descr) . "</td></tr>\n";
+        if ($owner and not $omit_owner) {
+               print  "<tr id=\"metadata_owner\"><td>owner</td><td>" . esc_html($owner) . "</td></tr>\n";
+        }
+       if (defined $cd{'rfc2822'}) {
+               print "<tr id=\"metadata_lchange\"><td>last change</td>" .
+                     "<td>".format_timestamp_html(\%cd)."</td></tr>\n";
+       }
+
+       # use per project git URL list in $projectroot/$project/cloneurl
+       # or make project git URL from git base URL and project name
+       my $url_tag = "URL";
+       my @url_list = git_get_project_url_list($project);
+       @url_list = map { "$_/$project" } @git_base_url_list unless @url_list;
+       foreach my $git_url (@url_list) {
+               next unless $git_url;
+               print format_repo_url($url_tag, $git_url);
+               $url_tag = "";
+       }
+
+       # Tag cloud
+       my $show_ctags = gitweb_check_feature('ctags');
+       if ($show_ctags) {
+               my $ctags = git_get_project_ctags($project);
+               if (%$ctags) {
+                       # without ability to add tags, don't show if there are none
+                       my $cloud = git_populate_project_tagcloud($ctags);
+                       print "<tr id=\"metadata_ctags\">" .
+                             "<td>content tags</td>" .
+                             "<td>".git_show_project_tagcloud($cloud, 48)."</td>" .
+                             "</tr>\n";
+               }
+       }
+
+       print "</table>\n";
+
+       # If XSS prevention is on, we don't include README.html.
+       # TODO: Allow a readme in some safe format.
+       if (!$prevent_xss && -s "$projectroot/$project/README.html") {
+               print "<div class=\"title\">readme</div>\n" .
+                     "<div class=\"readme\">\n";
+               insert_file("$projectroot/$project/README.html");
+               print "\n</div>\n"; # class="readme"
+       }
+
+       # we need to request one more than 16 (0..15) to check if
+       # those 16 are all
+       my @commitlist = $head ? parse_commits($head, 17) : ();
+       if (@commitlist) {
+               git_print_header_div('shortlog');
+               git_shortlog_body(\@commitlist, 0, 15, $refs,
+                                 $#commitlist <=  15 ? undef :
+                                 $cgi->a({-href => href(action=>"shortlog")}, "..."));
+       }
+
+       if (@taglist) {
+               git_print_header_div('tags');
+               git_tags_body(\@taglist, 0, 15,
+                             $#taglist <=  15 ? undef :
+                             $cgi->a({-href => href(action=>"tags")}, "..."));
+       }
+
+       if (@headlist) {
+               git_print_header_div('heads');
+               git_heads_body(\@headlist, $head, 0, 15,
+                              $#headlist <= 15 ? undef :
+                              $cgi->a({-href => href(action=>"heads")}, "..."));
+       }
+
+       if (%remotedata) {
+               git_print_header_div('remotes');
+               git_remotes_body(\%remotedata, 15, $head);
+       }
+
+       if (@forklist) {
+               git_print_header_div('forks');
+               git_project_list_body(\@forklist, 'age', 0, 15,
+                                     $#forklist <= 15 ? undef :
+                                     $cgi->a({-href => href(action=>"forks")}, "..."),
+                                     'no_header');
+       }
+
+       git_footer_html();
+}
+
+sub git_tag {
+       my %tag = parse_tag($hash);
+
+       if (! %tag) {
+               die_error(404, "Unknown tag object");
+       }
+
+       my $head = git_get_head_hash($project);
+       git_header_html();
+       git_print_page_nav('','', $head,undef,$head);
+       git_print_header_div('commit', esc_html($tag{'name'}), $hash);
+       print "<div class=\"title_text\">\n" .
+             "<table class=\"object_header\">\n" .
+             "<tr>\n" .
+             "<td>object</td>\n" .
+             "<td>" . $cgi->a({-class => "list", -href => href(action=>$tag{'type'}, hash=>$tag{'object'})},
+                              $tag{'object'}) . "</td>\n" .
+             "<td class=\"link\">" . $cgi->a({-href => href(action=>$tag{'type'}, hash=>$tag{'object'})},
+                                             $tag{'type'}) . "</td>\n" .
+             "</tr>\n";
+       if (defined($tag{'author'})) {
+               git_print_authorship_rows(\%tag, 'author');
+       }
+       print "</table>\n\n" .
+             "</div>\n";
+       print "<div class=\"page_body\">";
+       my $comment = $tag{'comment'};
+       foreach my $line (@$comment) {
+               chomp $line;
+               print esc_html($line, -nbsp=>1) . "<br/>\n";
+       }
+       print "</div>\n";
+       git_footer_html();
+}
+
+sub git_blame_common {
+       my $format = shift || 'porcelain';
+       if ($format eq 'porcelain' && $input_params{'javascript'}) {
+               $format = 'incremental';
+               $action = 'blame_incremental'; # for page title etc
+       }
+
+       # permissions
+       gitweb_check_feature('blame')
+               or die_error(403, "Blame view not allowed");
+
+       # error checking
+       die_error(400, "No file name given") unless $file_name;
+       $hash_base ||= git_get_head_hash($project);
+       die_error(404, "Couldn't find base commit") unless $hash_base;
+       my %co = parse_commit($hash_base)
+               or die_error(404, "Commit not found");
+       my $ftype = "blob";
+       if (!defined $hash) {
+               $hash = git_get_hash_by_path($hash_base, $file_name, "blob")
+                       or die_error(404, "Error looking up file");
+       } else {
+               $ftype = git_get_type($hash);
+               if ($ftype !~ "blob") {
+                       die_error(400, "Object is not a blob");
+               }
+       }
+
+       my $fd;
+       if ($format eq 'incremental') {
+               # get file contents (as base)
+               open $fd, "-|", git_cmd(), 'cat-file', 'blob', $hash
+                       or die_error(500, "Open git-cat-file failed");
+       } elsif ($format eq 'data') {
+               # run git-blame --incremental
+               open $fd, "-|", git_cmd(), "blame", "--incremental",
+                       $hash_base, "--", $file_name
+                       or die_error(500, "Open git-blame --incremental failed");
+       } else {
+               # run git-blame --porcelain
+               open $fd, "-|", git_cmd(), "blame", '-p',
+                       $hash_base, '--', $file_name
+                       or die_error(500, "Open git-blame --porcelain failed");
+       }
+       binmode $fd, ':utf8';
+
+       # incremental blame data returns early
+       if ($format eq 'data') {
+               print $cgi->header(
+                       -type=>"text/plain", -charset => "utf-8",
+                       -status=> "200 OK");
+               local $| = 1; # output autoflush
+               while (my $line = <$fd>) {
+                       print to_utf8($line);
+               }
+               close $fd
+                       or print "ERROR $!\n";
+
+               print 'END';
+               if (defined $t0 && gitweb_check_feature('timed')) {
+                       print ' '.
+                             tv_interval($t0, [ gettimeofday() ]).
+                             ' '.$number_of_git_cmds;
+               }
+               print "\n";
+
+               return;
+       }
+
+       # page header
+       git_header_html();
+       my $formats_nav =
+               $cgi->a({-href => href(action=>"blob", -replay=>1)},
+                       "blob") .
+               " | ";
+       if ($format eq 'incremental') {
+               $formats_nav .=
+                       $cgi->a({-href => href(action=>"blame", javascript=>0, -replay=>1)},
+                               "blame") . " (non-incremental)";
+       } else {
+               $formats_nav .=
+                       $cgi->a({-href => href(action=>"blame_incremental", -replay=>1)},
+                               "blame") . " (incremental)";
+       }
+       $formats_nav .=
+               " | " .
+               $cgi->a({-href => href(action=>"history", -replay=>1)},
+                       "history") .
+               " | " .
+               $cgi->a({-href => href(action=>$action, file_name=>$file_name)},
+                       "HEAD");
+       git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
+       git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
+       git_print_page_path($file_name, $ftype, $hash_base);
+
+       # page body
+       if ($format eq 'incremental') {
+               print "<noscript>\n<div class=\"error\"><center><b>\n".
+                     "This page requires JavaScript to run.\n Use ".
+                     $cgi->a({-href => href(action=>'blame',javascript=>0,-replay=>1)},
+                             'this page').
+                     " instead.\n".
+                     "</b></center></div>\n</noscript>\n";
+
+               print qq!<div id="progress_bar" style="width: 100%; background-color: yellow"></div>\n!;
+       }
+
+       print qq!<div class="page_body">\n!;
+       print qq!<div id="progress_info">... / ...</div>\n!
+               if ($format eq 'incremental');
+       print qq!<table id="blame_table" class="blame" width="100%">\n!.
+             #qq!<col width="5.5em" /><col width="2.5em" /><col width="*" />\n!.
+             qq!<thead>\n!.
+             qq!<tr><th>Commit</th><th>Line</th><th>Data</th></tr>\n!.
+             qq!</thead>\n!.
+             qq!<tbody>\n!;
+
+       my @rev_color = qw(light dark);
+       my $num_colors = scalar(@rev_color);
+       my $current_color = 0;
+
+       if ($format eq 'incremental') {
+               my $color_class = $rev_color[$current_color];
+
+               #contents of a file
+               my $linenr = 0;
+       LINE:
+               while (my $line = <$fd>) {
+                       chomp $line;
+                       $linenr++;
+
+                       print qq!<tr id="l$linenr" class="$color_class">!.
+                             qq!<td class="sha1"><a href=""> </a></td>!.
+                             qq!<td class="linenr">!.
+                             qq!<a class="linenr" href="">$linenr</a></td>!;
+                       print qq!<td class="pre">! . esc_html($line) . "</td>\n";
+                       print qq!</tr>\n!;
+               }
+
+       } else { # porcelain, i.e. ordinary blame
+               my %metainfo = (); # saves information about commits
+
+               # blame data
+       LINE:
+               while (my $line = <$fd>) {
+                       chomp $line;
+                       # the header: <SHA-1> <src lineno> <dst lineno> [<lines in group>]
+                       # no <lines in group> for subsequent lines in group of lines
+                       my ($full_rev, $orig_lineno, $lineno, $group_size) =
+                          ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
+                       if (!exists $metainfo{$full_rev}) {
+                               $metainfo{$full_rev} = { 'nprevious' => 0 };
+                       }
+                       my $meta = $metainfo{$full_rev};
+                       my $data;
+                       while ($data = <$fd>) {
+                               chomp $data;
+                               last if ($data =~ s/^\t//); # contents of line
+                               if ($data =~ /^(\S+)(?: (.*))?$/) {
+                                       $meta->{$1} = $2 unless exists $meta->{$1};
+                               }
+                               if ($data =~ /^previous /) {
+                                       $meta->{'nprevious'}++;
+                               }
+                       }
+                       my $short_rev = substr($full_rev, 0, 8);
+                       my $author = $meta->{'author'};
+                       my %date =
+                               parse_date($meta->{'author-time'}, $meta->{'author-tz'});
+                       my $date = $date{'iso-tz'};
+                       if ($group_size) {
+                               $current_color = ($current_color + 1) % $num_colors;
+                       }
+                       my $tr_class = $rev_color[$current_color];
+                       $tr_class .= ' boundary' if (exists $meta->{'boundary'});
+                       $tr_class .= ' no-previous' if ($meta->{'nprevious'} == 0);
+                       $tr_class .= ' multiple-previous' if ($meta->{'nprevious'} > 1);
+                       print "<tr id=\"l$lineno\" class=\"$tr_class\">\n";
+                       if ($group_size) {
+                               print "<td class=\"sha1\"";
+                               print " title=\"". esc_html($author) . ", $date\"";
+                               print " rowspan=\"$group_size\"" if ($group_size > 1);
+                               print ">";
+                               print $cgi->a({-href => href(action=>"commit",
+                                                            hash=>$full_rev,
+                                                            file_name=>$file_name)},
+                                             esc_html($short_rev));
+                               if ($group_size >= 2) {
+                                       my @author_initials = ($author =~ /\b([[:upper:]])\B/g);
+                                       if (@author_initials) {
+                                               print "<br />" .
+                                                     esc_html(join('', @author_initials));
+                                               #           or join('.', ...)
+                                       }
+                               }
+                               print "</td>\n";
+                       }
+                       # 'previous' <sha1 of parent commit> <filename at commit>
+                       if (exists $meta->{'previous'} &&
+                           $meta->{'previous'} =~ /^([a-fA-F0-9]{40}) (.*)$/) {
+                               $meta->{'parent'} = $1;
+                               $meta->{'file_parent'} = unquote($2);
+                       }
+                       my $linenr_commit =
+                               exists($meta->{'parent'}) ?
+                               $meta->{'parent'} : $full_rev;
+                       my $linenr_filename =
+                               exists($meta->{'file_parent'}) ?
+                               $meta->{'file_parent'} : unquote($meta->{'filename'});
+                       my $blamed = href(action => 'blame',
+                                         file_name => $linenr_filename,
+                                         hash_base => $linenr_commit);
+                       print "<td class=\"linenr\">";
+                       print $cgi->a({ -href => "$blamed#l$orig_lineno",
+                                       -class => "linenr" },
+                                     esc_html($lineno));
+                       print "</td>";
+                       print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
+                       print "</tr>\n";
+               } # end while
+
+       }
+
+       # footer
+       print "</tbody>\n".
+             "</table>\n"; # class="blame"
+       print "</div>\n";   # class="blame_body"
+       close $fd
+               or print "Reading blob failed\n";
+
+       git_footer_html();
+}
+
+sub git_blame {
+       git_blame_common();
+}
+
+sub git_blame_incremental {
+       git_blame_common('incremental');
+}
+
+sub git_blame_data {
+       git_blame_common('data');
+}
+
+sub git_tags {
+       my $head = git_get_head_hash($project);
+       git_header_html();
+       git_print_page_nav('','', $head,undef,$head,format_ref_views('tags'));
+       git_print_header_div('summary', $project);
+
+       my @tagslist = git_get_tags_list();
+       if (@tagslist) {
+               git_tags_body(\@tagslist);
+       }
+       git_footer_html();
+}
+
+sub git_heads {
+       my $head = git_get_head_hash($project);
+       git_header_html();
+       git_print_page_nav('','', $head,undef,$head,format_ref_views('heads'));
+       git_print_header_div('summary', $project);
+
+       my @headslist = git_get_heads_list();
+       if (@headslist) {
+               git_heads_body(\@headslist, $head);
+       }
+       git_footer_html();
+}
+
+# used both for single remote view and for list of all the remotes
+sub git_remotes {
+       gitweb_check_feature('remote_heads')
+               or die_error(403, "Remote heads view is disabled");
+
+       my $head = git_get_head_hash($project);
+       my $remote = $input_params{'hash'};
+
+       my $remotedata = git_get_remotes_list($remote);
+       die_error(500, "Unable to get remote information") unless defined $remotedata;
+
+       unless (%$remotedata) {
+               die_error(404, defined $remote ?
+                       "Remote $remote not found" :
+                       "No remotes found");
+       }
+
+       git_header_html(undef, undef, -action_extra => $remote);
+       git_print_page_nav('', '',  $head, undef, $head,
+               format_ref_views($remote ? '' : 'remotes'));
+
+       fill_remote_heads($remotedata);
+       if (defined $remote) {
+               git_print_header_div('remotes', "$remote remote for $project");
+               git_remote_block($remote, $remotedata->{$remote}, undef, $head);
+       } else {
+               git_print_header_div('summary', "$project remotes");
+               git_remotes_body($remotedata, undef, $head);
+       }
+
+       git_footer_html();
+}
+
+sub git_blob_plain {
+       my $type = shift;
+       my $expires;
+
+       if (!defined $hash) {
+               if (defined $file_name) {
+                       my $base = $hash_base || git_get_head_hash($project);
+                       $hash = git_get_hash_by_path($base, $file_name, "blob")
+                               or die_error(404, "Cannot find file");
+               } else {
+                       die_error(400, "No file name defined");
+               }
+       } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+               # blobs defined by non-textual hash id's can be cached
+               $expires = "+1d";
+       }
+
+       open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
+               or die_error(500, "Open git-cat-file blob '$hash' failed");
+
+       # content-type (can include charset)
+       $type = blob_contenttype($fd, $file_name, $type);
+
+       # "save as" filename, even when no $file_name is given
+       my $save_as = "$hash";
+       if (defined $file_name) {
+               $save_as = $file_name;
+       } elsif ($type =~ m/^text\//) {
+               $save_as .= '.txt';
+       }
+
+       # With XSS prevention on, blobs of all types except a few known safe
+       # ones are served with "Content-Disposition: attachment" to make sure
+       # they don't run in our security domain.  For certain image types,
+       # blob view writes an <img> tag referring to blob_plain view, and we
+       # want to be sure not to break that by serving the image as an
+       # attachment (though Firefox 3 doesn't seem to care).
+       my $sandbox = $prevent_xss &&
+               $type !~ m!^(?:text/[a-z]+|image/(?:gif|png|jpeg))(?:[ ;]|$)!;
+
+       # serve text/* as text/plain
+       if ($prevent_xss &&
+           ($type =~ m!^text/[a-z]+\b(.*)$! ||
+            ($type =~ m!^[a-z]+/[a-z]\+xml\b(.*)$! && -T $fd))) {
+               my $rest = $1;
+               $rest = defined $rest ? $rest : '';
+               $type = "text/plain$rest";
+       }
+
+       print $cgi->header(
+               -type => $type,
+               -expires => $expires,
+               -content_disposition =>
+                       ($sandbox ? 'attachment' : 'inline')
+                       . '; filename="' . $save_as . '"');
+       local $/ = undef;
+       binmode STDOUT, ':raw';
+       print <$fd>;
+       binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
+       close $fd;
+}
+
+sub git_blob {
+       my $expires;
+
+       if (!defined $hash) {
+               if (defined $file_name) {
+                       my $base = $hash_base || git_get_head_hash($project);
+                       $hash = git_get_hash_by_path($base, $file_name, "blob")
+                               or die_error(404, "Cannot find file");
+               } else {
+                       die_error(400, "No file name defined");
+               }
+       } elsif ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+               # blobs defined by non-textual hash id's can be cached
+               $expires = "+1d";
+       }
+
+       my $have_blame = gitweb_check_feature('blame');
+       open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
+               or die_error(500, "Couldn't cat $file_name, $hash");
+       my $mimetype = blob_mimetype($fd, $file_name);
+       # use 'blob_plain' (aka 'raw') view for files that cannot be displayed
+       if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)! && -B $fd) {
+               close $fd;
+               return git_blob_plain($mimetype);
+       }
+       # we can have blame only for text/* mimetype
+       $have_blame &&= ($mimetype =~ m!^text/!);
+
+       my $highlight = gitweb_check_feature('highlight');
+       my $syntax = guess_file_syntax($highlight, $mimetype, $file_name);
+       $fd = run_highlighter($fd, $highlight, $syntax);
+
+       git_header_html(undef, $expires);
+       my $formats_nav = '';
+       if (defined $hash_base && (my %co = parse_commit($hash_base))) {
+               if (defined $file_name) {
+                       if ($have_blame) {
+                               $formats_nav .=
+                                       $cgi->a({-href => href(action=>"blame", -replay=>1)},
+                                               "blame") .
+                                       " | ";
+                       }
+                       $formats_nav .=
+                               $cgi->a({-href => href(action=>"history", -replay=>1)},
+                                       "history") .
+                               " | " .
+                               $cgi->a({-href => href(action=>"blob_plain", -replay=>1)},
+                                       "raw") .
+                               " | " .
+                               $cgi->a({-href => href(action=>"blob",
+                                                      hash_base=>"HEAD", file_name=>$file_name)},
+                                       "HEAD");
+               } else {
+                       $formats_nav .=
+                               $cgi->a({-href => href(action=>"blob_plain", -replay=>1)},
+                                       "raw");
+               }
+               git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
+               git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
+       } else {
+               print "<div class=\"page_nav\">\n" .
+                     "<br/><br/></div>\n" .
+                     "<div class=\"title\">".esc_html($hash)."</div>\n";
+       }
+       git_print_page_path($file_name, "blob", $hash_base);
+       print "<div class=\"page_body\">\n";
+       if ($mimetype =~ m!^image/!) {
+               print qq!<img class="blob" type="!.esc_attr($mimetype).qq!"!;
+               if ($file_name) {
+                       print qq! alt="!.esc_attr($file_name).qq!" title="!.esc_attr($file_name).qq!"!;
+               }
+               print qq! src="! .
+                     href(action=>"blob_plain", hash=>$hash,
+                          hash_base=>$hash_base, file_name=>$file_name) .
+                     qq!" />\n!;
+       } else {
+               my $nr;
+               while (my $line = <$fd>) {
+                       chomp $line;
+                       $nr++;
+                       $line = untabify($line);
+                       printf qq!<div class="pre"><a id="l%i" href="%s#l%i" class="linenr">%4i</a> %s</div>\n!,
+                              $nr, esc_attr(href(-replay => 1)), $nr, $nr,
+                               ($highlight) ? sanitize($line) : esc_html($line, -nbsp=>1);
+               }
+       }
+       close $fd
+               or print "Reading blob failed.\n";
+       print "</div>";
+       git_footer_html();
+}
+
+sub git_tree {
+       if (!defined $hash_base) {
+               $hash_base = "HEAD";
+       }
+       if (!defined $hash) {
+               if (defined $file_name) {
+                       $hash = git_get_hash_by_path($hash_base, $file_name, "tree");
+               } else {
+                       $hash = $hash_base;
+               }
+       }
+       die_error(404, "No such tree") unless defined($hash);
+
+       my $show_sizes = gitweb_check_feature('show-sizes');
+       my $have_blame = gitweb_check_feature('blame');
+
+       my @entries = ();
+       {
+               local $/ = "\0";
+               open my $fd, "-|", git_cmd(), "ls-tree", '-z',
+                       ($show_sizes ? '-l' : ()), @extra_options, $hash
+                       or die_error(500, "Open git-ls-tree failed");
+               @entries = map { chomp; $_ } <$fd>;
+               close $fd
+                       or die_error(404, "Reading tree failed");
+       }
+
+       my $refs = git_get_references();
+       my $ref = format_ref_marker($refs, $hash_base);
+       git_header_html();
+       my $basedir = '';
+       if (defined $hash_base && (my %co = parse_commit($hash_base))) {
+               my @views_nav = ();
+               if (defined $file_name) {
+                       push @views_nav,
+                               $cgi->a({-href => href(action=>"history", -replay=>1)},
+                                       "history"),
+                               $cgi->a({-href => href(action=>"tree",
+                                                      hash_base=>"HEAD", file_name=>$file_name)},
+                                       "HEAD"),
+               }
+               my $snapshot_links = format_snapshot_links($hash);
+               if (defined $snapshot_links) {
+                       # FIXME: Should be available when we have no hash base as well.
+                       push @views_nav, $snapshot_links;
+               }
+               git_print_page_nav('tree','', $hash_base, undef, undef,
+                                  join(' | ', @views_nav));
+               git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
+       } else {
+               undef $hash_base;
+               print "<div class=\"page_nav\">\n";
+               print "<br/><br/></div>\n";
+               print "<div class=\"title\">".esc_html($hash)."</div>\n";
+       }
+       if (defined $file_name) {
+               $basedir = $file_name;
+               if ($basedir ne '' && substr($basedir, -1) ne '/') {
+                       $basedir .= '/';
+               }
+               git_print_page_path($file_name, 'tree', $hash_base);
+       }
+       print "<div class=\"page_body\">\n";
+       print "<table class=\"tree\">\n";
+       my $alternate = 1;
+       # '..' (top directory) link if possible
+       if (defined $hash_base &&
+           defined $file_name && $file_name =~ m![^/]+$!) {
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+
+               my $up = $file_name;
+               $up =~ s!/?[^/]+$!!;
+               undef $up unless $up;
+               # based on git_print_tree_entry
+               print '<td class="mode">' . mode_str('040000') . "</td>\n";
+               print '<td class="size">&nbsp;</td>'."\n" if $show_sizes;
+               print '<td class="list">';
+               print $cgi->a({-href => href(action=>"tree",
+                                            hash_base=>$hash_base,
+                                            file_name=>$up)},
+                             "..");
+               print "</td>\n";
+               print "<td class=\"link\"></td>\n";
+
+               print "</tr>\n";
+       }
+       foreach my $line (@entries) {
+               my %t = parse_ls_tree_line($line, -z => 1, -l => $show_sizes);
+
+               if ($alternate) {
+                       print "<tr class=\"dark\">\n";
+               } else {
+                       print "<tr class=\"light\">\n";
+               }
+               $alternate ^= 1;
+
+               git_print_tree_entry(\%t, $basedir, $hash_base, $have_blame);
+
+               print "</tr>\n";
+       }
+       print "</table>\n" .
+             "</div>";
+       git_footer_html();
+}
+
+sub sanitize_for_filename {
+    my $name = shift;
+
+    $name =~ s!/!-!g;
+    $name =~ s/[^[:alnum:]_.-]//g;
+
+    return $name;
+}
+
+sub snapshot_name {
+       my ($project, $hash) = @_;
+
+       # path/to/project.git  -> project
+       # path/to/project/.git -> project
+       my $name = to_utf8($project);
+       $name =~ s,([^/])/*\.git$,$1,;
+       $name = sanitize_for_filename(basename($name));
+
+       my $ver = $hash;
+       if ($hash =~ /^[0-9a-fA-F]+$/) {
+               # shorten SHA-1 hash
+               my $full_hash = git_get_full_hash($project, $hash);
+               if ($full_hash =~ /^$hash/ && length($hash) > 7) {
+                       $ver = git_get_short_hash($project, $hash);
+               }
+       } elsif ($hash =~ m!^refs/tags/(.*)$!) {
+               # tags don't need shortened SHA-1 hash
+               $ver = $1;
+       } else {
+               # branches and other need shortened SHA-1 hash
+               my $strip_refs = join '|', map { quotemeta } get_branch_refs();
+               if ($hash =~ m!^refs/($strip_refs|remotes)/(.*)$!) {
+                       my $ref_dir = (defined $1) ? $1 : '';
+                       $ver = $2;
+
+                       $ref_dir = sanitize_for_filename($ref_dir);
+                       # for refs neither in heads nor remotes we want to
+                       # add a ref dir to archive name
+                       if ($ref_dir ne '' and $ref_dir ne 'heads' and $ref_dir ne 'remotes') {
+                               $ver = $ref_dir . '-' . $ver;
+                       }
+               }
+               $ver .= '-' . git_get_short_hash($project, $hash);
+       }
+       # special case of sanitization for filename - we change
+       # slashes to dots instead of dashes
+       # in case of hierarchical branch names
+       $ver =~ s!/!.!g;
+       $ver =~ s/[^[:alnum:]_.-]//g;
+
+       # name = project-version_string
+       $name = "$name-$ver";
+
+       return wantarray ? ($name, $name) : $name;
+}
+
+sub exit_if_unmodified_since {
+       my ($latest_epoch) = @_;
+       our $cgi;
+
+       my $if_modified = $cgi->http('IF_MODIFIED_SINCE');
+       if (defined $if_modified) {
+               my $since;
+               if (eval { require HTTP::Date; 1; }) {
+                       $since = HTTP::Date::str2time($if_modified);
+               } elsif (eval { require Time::ParseDate; 1; }) {
+                       $since = Time::ParseDate::parsedate($if_modified, GMT => 1);
+               }
+               if (defined $since && $latest_epoch <= $since) {
+                       my %latest_date = parse_date($latest_epoch);
+                       print $cgi->header(
+                               -last_modified => $latest_date{'rfc2822'},
+                               -status => '304 Not Modified');
+                       goto DONE_GITWEB;
+               }
+       }
+}
+
+sub git_snapshot {
+       my $format = $input_params{'snapshot_format'};
+       if (!@snapshot_fmts) {
+               die_error(403, "Snapshots not allowed");
+       }
+       # default to first supported snapshot format
+       $format ||= $snapshot_fmts[0];
+       if ($format !~ m/^[a-z0-9]+$/) {
+               die_error(400, "Invalid snapshot format parameter");
+       } elsif (!exists($known_snapshot_formats{$format})) {
+               die_error(400, "Unknown snapshot format");
+       } elsif ($known_snapshot_formats{$format}{'disabled'}) {
+               die_error(403, "Snapshot format not allowed");
+       } elsif (!grep($_ eq $format, @snapshot_fmts)) {
+               die_error(403, "Unsupported snapshot format");
+       }
+
+       my $type = git_get_type("$hash^{}");
+       if (!$type) {
+               die_error(404, 'Object does not exist');
+       }  elsif ($type eq 'blob') {
+               die_error(400, 'Object is not a tree-ish');
+       }
+
+       my ($name, $prefix) = snapshot_name($project, $hash);
+       my $filename = "$name$known_snapshot_formats{$format}{'suffix'}";
+
+       my %co = parse_commit($hash);
+       exit_if_unmodified_since($co{'committer_epoch'}) if %co;
+
+       my $cmd = quote_command(
+               git_cmd(), 'archive',
+               "--format=$known_snapshot_formats{$format}{'format'}",
+               "--prefix=$prefix/", $hash);
+       if (exists $known_snapshot_formats{$format}{'compressor'}) {
+               $cmd .= ' | ' . quote_command(@{$known_snapshot_formats{$format}{'compressor'}});
+       }
+
+       $filename =~ s/(["\\])/\\$1/g;
+       my %latest_date;
+       if (%co) {
+               %latest_date = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
+       }
+
+       print $cgi->header(
+               -type => $known_snapshot_formats{$format}{'type'},
+               -content_disposition => 'inline; filename="' . $filename . '"',
+               %co ? (-last_modified => $latest_date{'rfc2822'}) : (),
+               -status => '200 OK');
+
+       open my $fd, "-|", $cmd
+               or die_error(500, "Execute git-archive failed");
+       binmode STDOUT, ':raw';
+       print <$fd>;
+       binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
+       close $fd;
+}
+
+sub git_log_generic {
+       my ($fmt_name, $body_subr, $base, $parent, $file_name, $file_hash) = @_;
+
+       my $head = git_get_head_hash($project);
+       if (!defined $base) {
+               $base = $head;
+       }
+       if (!defined $page) {
+               $page = 0;
+       }
+       my $refs = git_get_references();
+
+       my $commit_hash = $base;
+       if (defined $parent) {
+               $commit_hash = "$parent..$base";
+       }
+       my @commitlist =
+               parse_commits($commit_hash, 101, (100 * $page),
+                             defined $file_name ? ($file_name, "--full-history") : ());
+
+       my $ftype;
+       if (!defined $file_hash && defined $file_name) {
+               # some commits could have deleted file in question,
+               # and not have it in tree, but one of them has to have it
+               for (my $i = 0; $i < @commitlist; $i++) {
+                       $file_hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name);
+                       last if defined $file_hash;
+               }
+       }
+       if (defined $file_hash) {
+               $ftype = git_get_type($file_hash);
+       }
+       if (defined $file_name && !defined $ftype) {
+               die_error(500, "Unknown type of object");
+       }
+       my %co;
+       if (defined $file_name) {
+               %co = parse_commit($base)
+                       or die_error(404, "Unknown commit object");
+       }
+
+
+       my $paging_nav = format_paging_nav($fmt_name, $page, $#commitlist >= 100);
+       my $next_link = '';
+       if ($#commitlist >= 100) {
+               $next_link =
+                       $cgi->a({-href => href(-replay=>1, page=>$page+1),
+                                -accesskey => "n", -title => "Alt-n"}, "next");
+       }
+       my $patch_max = gitweb_get_feature('patches');
+       if ($patch_max && !defined $file_name) {
+               if ($patch_max < 0 || @commitlist <= $patch_max) {
+                       $paging_nav .= " &sdot; " .
+                               $cgi->a({-href => href(action=>"patches", -replay=>1)},
+                                       "patches");
+               }
+       }
+
+       git_header_html();
+       git_print_page_nav($fmt_name,'', $hash,$hash,$hash, $paging_nav);
+       if (defined $file_name) {
+               git_print_header_div('commit', esc_html($co{'title'}), $base);
+       } else {
+               git_print_header_div('summary', $project)
+       }
+       git_print_page_path($file_name, $ftype, $hash_base)
+               if (defined $file_name);
+
+       $body_subr->(\@commitlist, 0, 99, $refs, $next_link,
+                    $file_name, $file_hash, $ftype);
+
+       git_footer_html();
+}
+
+sub git_log {
+       git_log_generic('log', \&git_log_body,
+                       $hash, $hash_parent);
+}
+
+sub git_commit {
+       $hash ||= $hash_base || "HEAD";
+       my %co = parse_commit($hash)
+           or die_error(404, "Unknown commit object");
+
+       my $parent  = $co{'parent'};
+       my $parents = $co{'parents'}; # listref
+
+       # we need to prepare $formats_nav before any parameter munging
+       my $formats_nav;
+       if (!defined $parent) {
+               # --root commitdiff
+               $formats_nav .= '(initial)';
+       } elsif (@$parents == 1) {
+               # single parent commit
+               $formats_nav .=
+                       '(parent: ' .
+                       $cgi->a({-href => href(action=>"commit",
+                                              hash=>$parent)},
+                               esc_html(substr($parent, 0, 7))) .
+                       ')';
+       } else {
+               # merge commit
+               $formats_nav .=
+                       '(merge: ' .
+                       join(' ', map {
+                               $cgi->a({-href => href(action=>"commit",
+                                                      hash=>$_)},
+                                       esc_html(substr($_, 0, 7)));
+                       } @$parents ) .
+                       ')';
+       }
+       if (gitweb_check_feature('patches') && @$parents <= 1) {
+               $formats_nav .= " | " .
+                       $cgi->a({-href => href(action=>"patch", -replay=>1)},
+                               "patch");
+       }
+
+       if (!defined $parent) {
+               $parent = "--root";
+       }
+       my @difftree;
+       open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
+               @diff_opts,
+               (@$parents <= 1 ? $parent : '-c'),
+               $hash, "--"
+               or die_error(500, "Open git-diff-tree failed");
+       @difftree = map { chomp; $_ } <$fd>;
+       close $fd or die_error(404, "Reading git-diff-tree failed");
+
+       # non-textual hash id's can be cached
+       my $expires;
+       if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+               $expires = "+1d";
+       }
+       my $refs = git_get_references();
+       my $ref = format_ref_marker($refs, $co{'id'});
+
+       git_header_html(undef, $expires);
+       git_print_page_nav('commit', '',
+                          $hash, $co{'tree'}, $hash,
+                          $formats_nav);
+
+       if (defined $co{'parent'}) {
+               git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash);
+       } else {
+               git_print_header_div('tree', esc_html($co{'title'}) . $ref, $co{'tree'}, $hash);
+       }
+       print "<div class=\"title_text\">\n" .
+             "<table class=\"object_header\">\n";
+       git_print_authorship_rows(\%co);
+       print "<tr><td>commit</td><td class=\"sha1\">$co{'id'}</td></tr>\n";
+       print "<tr>" .
+             "<td>tree</td>" .
+             "<td class=\"sha1\">" .
+             $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash),
+                      class => "list"}, $co{'tree'}) .
+             "</td>" .
+             "<td class=\"link\">" .
+             $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash)},
+                     "tree");
+       my $snapshot_links = format_snapshot_links($hash);
+       if (defined $snapshot_links) {
+               print " | " . $snapshot_links;
+       }
+       print "</td>" .
+             "</tr>\n";
+
+       foreach my $par (@$parents) {
+               print "<tr>" .
+                     "<td>parent</td>" .
+                     "<td class=\"sha1\">" .
+                     $cgi->a({-href => href(action=>"commit", hash=>$par),
+                              class => "list"}, $par) .
+                     "</td>" .
+                     "<td class=\"link\">" .
+                     $cgi->a({-href => href(action=>"commit", hash=>$par)}, "commit") .
+                     " | " .
+                     $cgi->a({-href => href(action=>"commitdiff", hash=>$hash, hash_parent=>$par)}, "diff") .
+                     "</td>" .
+                     "</tr>\n";
+       }
+       print "</table>".
+             "</div>\n";
+
+       print "<div class=\"page_body\">\n";
+       git_print_log($co{'comment'});
+       print "</div>\n";
+
+       git_difftree_body(\@difftree, $hash, @$parents);
+
+       git_footer_html();
+}
+
+sub git_object {
+       # object is defined by:
+       # - hash or hash_base alone
+       # - hash_base and file_name
+       my $type;
+
+       # - hash or hash_base alone
+       if ($hash || ($hash_base && !defined $file_name)) {
+               my $object_id = $hash || $hash_base;
+
+               open my $fd, "-|", quote_command(
+                       git_cmd(), 'cat-file', '-t', $object_id) . ' 2> /dev/null'
+                       or die_error(404, "Object does not exist");
+               $type = <$fd>;
+               defined $type && chomp $type;
+               close $fd
+                       or die_error(404, "Object does not exist");
+
+       # - hash_base and file_name
+       } elsif ($hash_base && defined $file_name) {
+               $file_name =~ s,/+$,,;
+
+               system(git_cmd(), "cat-file", '-e', $hash_base) == 0
+                       or die_error(404, "Base object does not exist");
+
+               # here errors should not happen
+               open my $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $file_name
+                       or die_error(500, "Open git-ls-tree failed");
+               my $line = <$fd>;
+               close $fd;
+
+               #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa  panic.c'
+               unless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/) {
+                       die_error(404, "File or directory for given base does not exist");
+               }
+               $type = $2;
+               $hash = $3;
+       } else {
+               die_error(400, "Not enough information to find object");
+       }
+
+       print $cgi->redirect(-uri => href(action=>$type, -full=>1,
+                                         hash=>$hash, hash_base=>$hash_base,
+                                         file_name=>$file_name),
+                            -status => '302 Found');
+}
+
+sub git_blobdiff {
+       my $format = shift || 'html';
+       my $diff_style = $input_params{'diff_style'} || 'inline';
+
+       my $fd;
+       my @difftree;
+       my %diffinfo;
+       my $expires;
+
+       # preparing $fd and %diffinfo for git_patchset_body
+       # new style URI
+       if (defined $hash_base && defined $hash_parent_base) {
+               if (defined $file_name) {
+                       # read raw output
+                       open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+                               $hash_parent_base, $hash_base,
+                               "--", (defined $file_parent ? $file_parent : ()), $file_name
+                               or die_error(500, "Open git-diff-tree failed");
+                       @difftree = map { chomp; $_ } <$fd>;
+                       close $fd
+                               or die_error(404, "Reading git-diff-tree failed");
+                       @difftree
+                               or die_error(404, "Blob diff not found");
+
+               } elsif (defined $hash &&
+                        $hash =~ /[0-9a-fA-F]{40}/) {
+                       # try to find filename from $hash
+
+                       # read filtered raw output
+                       open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+                               $hash_parent_base, $hash_base, "--"
+                               or die_error(500, "Open git-diff-tree failed");
+                       @difftree =
+                               # ':100644 100644 03b21826... 3b93d5e7... M     ls-files.c'
+                               # $hash == to_id
+                               grep { /^:[0-7]{6} [0-7]{6} [0-9a-fA-F]{40} $hash/ }
+                               map { chomp; $_ } <$fd>;
+                       close $fd
+                               or die_error(404, "Reading git-diff-tree failed");
+                       @difftree
+                               or die_error(404, "Blob diff not found");
+
+               } else {
+                       die_error(400, "Missing one of the blob diff parameters");
+               }
+
+               if (@difftree > 1) {
+                       die_error(400, "Ambiguous blob diff specification");
+               }
+
+               %diffinfo = parse_difftree_raw_line($difftree[0]);
+               $file_parent ||= $diffinfo{'from_file'} || $file_name;
+               $file_name   ||= $diffinfo{'to_file'};
+
+               $hash_parent ||= $diffinfo{'from_id'};
+               $hash        ||= $diffinfo{'to_id'};
+
+               # non-textual hash id's can be cached
+               if ($hash_base =~ m/^[0-9a-fA-F]{40}$/ &&
+                   $hash_parent_base =~ m/^[0-9a-fA-F]{40}$/) {
+                       $expires = '+1d';
+               }
+
+               # open patch output
+               open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+                       '-p', ($format eq 'html' ? "--full-index" : ()),
+                       $hash_parent_base, $hash_base,
+                       "--", (defined $file_parent ? $file_parent : ()), $file_name
+                       or die_error(500, "Open git-diff-tree failed");
+       }
+
+       # old/legacy style URI -- not generated anymore since 1.4.3.
+       if (!%diffinfo) {
+               die_error('404 Not Found', "Missing one of the blob diff parameters")
+       }
+
+       # header
+       if ($format eq 'html') {
+               my $formats_nav =
+                       $cgi->a({-href => href(action=>"blobdiff_plain", -replay=>1)},
+                               "raw");
+               $formats_nav .= diff_style_nav($diff_style);
+               git_header_html(undef, $expires);
+               if (defined $hash_base && (my %co = parse_commit($hash_base))) {
+                       git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
+                       git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
+               } else {
+                       print "<div class=\"page_nav\"><br/>$formats_nav<br/></div>\n";
+                       print "<div class=\"title\">".esc_html("$hash vs $hash_parent")."</div>\n";
+               }
+               if (defined $file_name) {
+                       git_print_page_path($file_name, "blob", $hash_base);
+               } else {
+                       print "<div class=\"page_path\"></div>\n";
+               }
+
+       } elsif ($format eq 'plain') {
+               print $cgi->header(
+                       -type => 'text/plain',
+                       -charset => 'utf-8',
+                       -expires => $expires,
+                       -content_disposition => 'inline; filename="' . "$file_name" . '.patch"');
+
+               print "X-Git-Url: " . $cgi->self_url() . "\n\n";
+
+       } else {
+               die_error(400, "Unknown blobdiff format");
+       }
+
+       # patch
+       if ($format eq 'html') {
+               print "<div class=\"page_body\">\n";
+
+               git_patchset_body($fd, $diff_style,
+                                 [ \%diffinfo ], $hash_base, $hash_parent_base);
+               close $fd;
+
+               print "</div>\n"; # class="page_body"
+               git_footer_html();
+
+       } else {
+               while (my $line = <$fd>) {
+                       $line =~ s!a/($hash|$hash_parent)!'a/'.esc_path($diffinfo{'from_file'})!eg;
+                       $line =~ s!b/($hash|$hash_parent)!'b/'.esc_path($diffinfo{'to_file'})!eg;
+
+                       print $line;
+
+                       last if $line =~ m!^\+\+\+!;
+               }
+               local $/ = undef;
+               print <$fd>;
+               close $fd;
+       }
+}
+
+sub git_blobdiff_plain {
+       git_blobdiff('plain');
+}
+
+# assumes that it is added as later part of already existing navigation,
+# so it returns "| foo | bar" rather than just "foo | bar"
+sub diff_style_nav {
+       my ($diff_style, $is_combined) = @_;
+       $diff_style ||= 'inline';
+
+       return "" if ($is_combined);
+
+       my @styles = (inline => 'inline', 'sidebyside' => 'side by side');
+       my %styles = @styles;
+       @styles =
+               @styles[ map { $_ * 2 } 0..$#styles/2 ];
+
+       return join '',
+               map { " | ".$_ }
+               map {
+                       $_ eq $diff_style ? $styles{$_} :
+                       $cgi->a({-href => href(-replay=>1, diff_style => $_)}, $styles{$_})
+               } @styles;
+}
+
+sub git_commitdiff {
+       my %params = @_;
+       my $format = $params{-format} || 'html';
+       my $diff_style = $input_params{'diff_style'} || 'inline';
+
+       my ($patch_max) = gitweb_get_feature('patches');
+       if ($format eq 'patch') {
+               die_error(403, "Patch view not allowed") unless $patch_max;
+       }
+
+       $hash ||= $hash_base || "HEAD";
+       my %co = parse_commit($hash)
+           or die_error(404, "Unknown commit object");
+
+       # choose format for commitdiff for merge
+       if (! defined $hash_parent && @{$co{'parents'}} > 1) {
+               $hash_parent = '--cc';
+       }
+       # we need to prepare $formats_nav before almost any parameter munging
+       my $formats_nav;
+       if ($format eq 'html') {
+               $formats_nav =
+                       $cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)},
+                               "raw");
+               if ($patch_max && @{$co{'parents'}} <= 1) {
+                       $formats_nav .= " | " .
+                               $cgi->a({-href => href(action=>"patch", -replay=>1)},
+                                       "patch");
+               }
+               $formats_nav .= diff_style_nav($diff_style, @{$co{'parents'}} > 1);
+
+               if (defined $hash_parent &&
+                   $hash_parent ne '-c' && $hash_parent ne '--cc') {
+                       # commitdiff with two commits given
+                       my $hash_parent_short = $hash_parent;
+                       if ($hash_parent =~ m/^[0-9a-fA-F]{40}$/) {
+                               $hash_parent_short = substr($hash_parent, 0, 7);
+                       }
+                       $formats_nav .=
+                               ' (from';
+                       for (my $i = 0; $i < @{$co{'parents'}}; $i++) {
+                               if ($co{'parents'}[$i] eq $hash_parent) {
+                                       $formats_nav .= ' parent ' . ($i+1);
+                                       last;
+                               }
+                       }
+                       $formats_nav .= ': ' .
+                               $cgi->a({-href => href(-replay=>1,
+                                                      hash=>$hash_parent, hash_base=>undef)},
+                                       esc_html($hash_parent_short)) .
+                               ')';
+               } elsif (!$co{'parent'}) {
+                       # --root commitdiff
+                       $formats_nav .= ' (initial)';
+               } elsif (scalar @{$co{'parents'}} == 1) {
+                       # single parent commit
+                       $formats_nav .=
+                               ' (parent: ' .
+                               $cgi->a({-href => href(-replay=>1,
+                                                      hash=>$co{'parent'}, hash_base=>undef)},
+                                       esc_html(substr($co{'parent'}, 0, 7))) .
+                               ')';
+               } else {
+                       # merge commit
+                       if ($hash_parent eq '--cc') {
+                               $formats_nav .= ' | ' .
+                                       $cgi->a({-href => href(-replay=>1,
+                                                              hash=>$hash, hash_parent=>'-c')},
+                                               'combined');
+                       } else { # $hash_parent eq '-c'
+                               $formats_nav .= ' | ' .
+                                       $cgi->a({-href => href(-replay=>1,
+                                                              hash=>$hash, hash_parent=>'--cc')},
+                                               'compact');
+                       }
+                       $formats_nav .=
+                               ' (merge: ' .
+                               join(' ', map {
+                                       $cgi->a({-href => href(-replay=>1,
+                                                              hash=>$_, hash_base=>undef)},
+                                               esc_html(substr($_, 0, 7)));
+                               } @{$co{'parents'}} ) .
+                               ')';
+               }
+       }
+
+       my $hash_parent_param = $hash_parent;
+       if (!defined $hash_parent_param) {
+               # --cc for multiple parents, --root for parentless
+               $hash_parent_param =
+                       @{$co{'parents'}} > 1 ? '--cc' : $co{'parent'} || '--root';
+       }
+
+       # read commitdiff
+       my $fd;
+       my @difftree;
+       if ($format eq 'html') {
+               open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+                       "--no-commit-id", "--patch-with-raw", "--full-index",
+                       $hash_parent_param, $hash, "--"
+                       or die_error(500, "Open git-diff-tree failed");
+
+               while (my $line = <$fd>) {
+                       chomp $line;
+                       # empty line ends raw part of diff-tree output
+                       last unless $line;
+                       push @difftree, scalar parse_difftree_raw_line($line);
+               }
+
+       } elsif ($format eq 'plain') {
+               open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+                       '-p', $hash_parent_param, $hash, "--"
+                       or die_error(500, "Open git-diff-tree failed");
+       } elsif ($format eq 'patch') {
+               # For commit ranges, we limit the output to the number of
+               # patches specified in the 'patches' feature.
+               # For single commits, we limit the output to a single patch,
+               # diverging from the git-format-patch default.
+               my @commit_spec = ();
+               if ($hash_parent) {
+                       if ($patch_max > 0) {
+                               push @commit_spec, "-$patch_max";
+                       }
+                       push @commit_spec, '-n', "$hash_parent..$hash";
+               } else {
+                       if ($params{-single}) {
+                               push @commit_spec, '-1';
+                       } else {
+                               if ($patch_max > 0) {
+                                       push @commit_spec, "-$patch_max";
+                               }
+                               push @commit_spec, "-n";
+                       }
+                       push @commit_spec, '--root', $hash;
+               }
+               open $fd, "-|", git_cmd(), "format-patch", @diff_opts,
+                       '--encoding=utf8', '--stdout', @commit_spec
+                       or die_error(500, "Open git-format-patch failed");
+       } else {
+               die_error(400, "Unknown commitdiff format");
+       }
+
+       # non-textual hash id's can be cached
+       my $expires;
+       if ($hash =~ m/^[0-9a-fA-F]{40}$/) {
+               $expires = "+1d";
+       }
+
+       # write commit message
+       if ($format eq 'html') {
+               my $refs = git_get_references();
+               my $ref = format_ref_marker($refs, $co{'id'});
+
+               git_header_html(undef, $expires);
+               git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);
+               git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash);
+               print "<div class=\"title_text\">\n" .
+                     "<table class=\"object_header\">\n";
+               git_print_authorship_rows(\%co);
+               print "</table>".
+                     "</div>\n";
+               print "<div class=\"page_body\">\n";
+               if (@{$co{'comment'}} > 1) {
+                       print "<div class=\"log\">\n";
+                       git_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1);
+                       print "</div>\n"; # class="log"
+               }
+
+       } elsif ($format eq 'plain') {
+               my $refs = git_get_references("tags");
+               my $tagname = git_get_rev_name_tags($hash);
+               my $filename = basename($project) . "-$hash.patch";
+
+               print $cgi->header(
+                       -type => 'text/plain',
+                       -charset => 'utf-8',
+                       -expires => $expires,
+                       -content_disposition => 'inline; filename="' . "$filename" . '"');
+               my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
+               print "From: " . to_utf8($co{'author'}) . "\n";
+               print "Date: $ad{'rfc2822'} ($ad{'tz_local'})\n";
+               print "Subject: " . to_utf8($co{'title'}) . "\n";
+
+               print "X-Git-Tag: $tagname\n" if $tagname;
+               print "X-Git-Url: " . $cgi->self_url() . "\n\n";
+
+               foreach my $line (@{$co{'comment'}}) {
+                       print to_utf8($line) . "\n";
+               }
+               print "---\n\n";
+       } elsif ($format eq 'patch') {
+               my $filename = basename($project) . "-$hash.patch";
+
+               print $cgi->header(
+                       -type => 'text/plain',
+                       -charset => 'utf-8',
+                       -expires => $expires,
+                       -content_disposition => 'inline; filename="' . "$filename" . '"');
+       }
+
+       # write patch
+       if ($format eq 'html') {
+               my $use_parents = !defined $hash_parent ||
+                       $hash_parent eq '-c' || $hash_parent eq '--cc';
+               git_difftree_body(\@difftree, $hash,
+                                 $use_parents ? @{$co{'parents'}} : $hash_parent);
+               print "<br/>\n";
+
+               git_patchset_body($fd, $diff_style,
+                                 \@difftree, $hash,
+                                 $use_parents ? @{$co{'parents'}} : $hash_parent);
+               close $fd;
+               print "</div>\n"; # class="page_body"
+               git_footer_html();
+
+       } elsif ($format eq 'plain') {
+               local $/ = undef;
+               print <$fd>;
+               close $fd
+                       or print "Reading git-diff-tree failed\n";
+       } elsif ($format eq 'patch') {
+               local $/ = undef;
+               print <$fd>;
+               close $fd
+                       or print "Reading git-format-patch failed\n";
+       }
+}
+
+sub git_commitdiff_plain {
+       git_commitdiff(-format => 'plain');
+}
+
+# format-patch-style patches
+sub git_patch {
+       git_commitdiff(-format => 'patch', -single => 1);
+}
+
+sub git_patches {
+       git_commitdiff(-format => 'patch');
+}
+
+sub git_history {
+       git_log_generic('history', \&git_history_body,
+                       $hash_base, $hash_parent_base,
+                       $file_name, $hash);
+}
+
+sub git_search {
+       $searchtype ||= 'commit';
+
+       # check if appropriate features are enabled
+       gitweb_check_feature('search')
+               or die_error(403, "Search is disabled");
+       if ($searchtype eq 'pickaxe') {
+               # pickaxe may take all resources of your box and run for several minutes
+               # with every query - so decide by yourself how public you make this feature
+               gitweb_check_feature('pickaxe')
+                       or die_error(403, "Pickaxe search is disabled");
+       }
+       if ($searchtype eq 'grep') {
+               # grep search might be potentially CPU-intensive, too
+               gitweb_check_feature('grep')
+                       or die_error(403, "Grep search is disabled");
+       }
+
+       if (!defined $searchtext) {
+               die_error(400, "Text field is empty");
+       }
+       if (!defined $hash) {
+               $hash = git_get_head_hash($project);
+       }
+       my %co = parse_commit($hash);
+       if (!%co) {
+               die_error(404, "Unknown commit object");
+       }
+       if (!defined $page) {
+               $page = 0;
+       }
+
+       if ($searchtype eq 'commit' ||
+           $searchtype eq 'author' ||
+           $searchtype eq 'committer') {
+               git_search_message(%co);
+       } elsif ($searchtype eq 'pickaxe') {
+               git_search_changes(%co);
+       } elsif ($searchtype eq 'grep') {
+               git_search_files(%co);
+       } else {
+               die_error(400, "Unknown search type");
+       }
+}
+
+sub git_search_help {
+       git_header_html();
+       git_print_page_nav('','', $hash,$hash,$hash);
+       print <<EOT;
+<p><strong>Pattern</strong> is by default a normal string that is matched precisely (but without
+regard to case, except in the case of pickaxe). However, when you check the <em>re</em> checkbox,
+the pattern entered is recognized as the POSIX extended
+<a href="http://en.wikipedia.org/wiki/Regular_expression">regular expression</a> (also case
+insensitive).</p>
+<dl>
+<dt><b>commit</b></dt>
+<dd>The commit messages and authorship information will be scanned for the given pattern.</dd>
+EOT
+       my $have_grep = gitweb_check_feature('grep');
+       if ($have_grep) {
+               print <<EOT;
+<dt><b>grep</b></dt>
+<dd>All files in the currently selected tree (HEAD unless you are explicitly browsing
+    a different one) are searched for the given pattern. On large trees, this search can take
+a while and put some strain on the server, so please use it with some consideration. Note that
+due to git-grep peculiarity, currently if regexp mode is turned off, the matches are
+case-sensitive.</dd>
+EOT
+       }
+       print <<EOT;
+<dt><b>author</b></dt>
+<dd>Name and e-mail of the change author and date of birth of the patch will be scanned for the given pattern.</dd>
+<dt><b>committer</b></dt>
+<dd>Name and e-mail of the committer and date of commit will be scanned for the given pattern.</dd>
+EOT
+       my $have_pickaxe = gitweb_check_feature('pickaxe');
+       if ($have_pickaxe) {
+               print <<EOT;
+<dt><b>pickaxe</b></dt>
+<dd>All commits that caused the string to appear or disappear from any file (changes that
+added, removed or "modified" the string) will be listed. This search can take a while and
+takes a lot of strain on the server, so please use it wisely. Note that since you may be
+interested even in changes just changing the case as well, this search is case sensitive.</dd>
+EOT
+       }
+       print "</dl>\n";
+       git_footer_html();
+}
+
+sub git_shortlog {
+       git_log_generic('shortlog', \&git_shortlog_body,
+                       $hash, $hash_parent);
+}
+
+## ......................................................................
+## feeds (RSS, Atom; OPML)
+
+sub git_feed {
+       my $format = shift || 'atom';
+       my $have_blame = gitweb_check_feature('blame');
+
+       # Atom: http://www.atomenabled.org/developers/syndication/
+       # RSS:  http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
+       if ($format ne 'rss' && $format ne 'atom') {
+               die_error(400, "Unknown web feed format");
+       }
+
+       # log/feed of current (HEAD) branch, log of given branch, history of file/directory
+       my $head = $hash || 'HEAD';
+       my @commitlist = parse_commits($head, 150, 0, $file_name);
+
+       my %latest_commit;
+       my %latest_date;
+       my $content_type = "application/$format+xml";
+       if (defined $cgi->http('HTTP_ACCEPT') &&
+                $cgi->Accept('text/xml') > $cgi->Accept($content_type)) {
+               # browser (feed reader) prefers text/xml
+               $content_type = 'text/xml';
+       }
+       if (defined($commitlist[0])) {
+               %latest_commit = %{$commitlist[0]};
+               my $latest_epoch = $latest_commit{'committer_epoch'};
+               exit_if_unmodified_since($latest_epoch);
+               %latest_date = parse_date($latest_epoch, $latest_commit{'committer_tz'});
+       }
+       print $cgi->header(
+               -type => $content_type,
+               -charset => 'utf-8',
+               %latest_date ? (-last_modified => $latest_date{'rfc2822'}) : (),
+               -status => '200 OK');
+
+       # Optimization: skip generating the body if client asks only
+       # for Last-Modified date.
+       return if ($cgi->request_method() eq 'HEAD');
+
+       # header variables
+       my $title = "$site_name - $project/$action";
+       my $feed_type = 'log';
+       if (defined $hash) {
+               $title .= " - '$hash'";
+               $feed_type = 'branch log';
+               if (defined $file_name) {
+                       $title .= " :: $file_name";
+                       $feed_type = 'history';
+               }
+       } elsif (defined $file_name) {
+               $title .= " - $file_name";
+               $feed_type = 'history';
+       }
+       $title .= " $feed_type";
+       $title = esc_html($title);
+       my $descr = git_get_project_description($project);
+       if (defined $descr) {
+               $descr = esc_html($descr);
+       } else {
+               $descr = "$project " .
+                        ($format eq 'rss' ? 'RSS' : 'Atom') .
+                        " feed";
+       }
+       my $owner = git_get_project_owner($project);
+       $owner = esc_html($owner);
+
+       #header
+       my $alt_url;
+       if (defined $file_name) {
+               $alt_url = href(-full=>1, action=>"history", hash=>$hash, file_name=>$file_name);
+       } elsif (defined $hash) {
+               $alt_url = href(-full=>1, action=>"log", hash=>$hash);
+       } else {
+               $alt_url = href(-full=>1, action=>"summary");
+       }
+       print qq!<?xml version="1.0" encoding="utf-8"?>\n!;
+       if ($format eq 'rss') {
+               print <<XML;
+<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
+<channel>
+XML
+               print "<title>$title</title>\n" .
+                     "<link>$alt_url</link>\n" .
+                     "<description>$descr</description>\n" .
+                     "<language>en</language>\n" .
+                     # project owner is responsible for 'editorial' content
+                     "<managingEditor>$owner</managingEditor>\n";
+               if (defined $logo || defined $favicon) {
+                       # prefer the logo to the favicon, since RSS
+                       # doesn't allow both
+                       my $img = esc_url($logo || $favicon);
+                       print "<image>\n" .
+                             "<url>$img</url>\n" .
+                             "<title>$title</title>\n" .
+                             "<link>$alt_url</link>\n" .
+                             "</image>\n";
+               }
+               if (%latest_date) {
+                       print "<pubDate>$latest_date{'rfc2822'}</pubDate>\n";
+                       print "<lastBuildDate>$latest_date{'rfc2822'}</lastBuildDate>\n";
+               }
+               print "<generator>gitweb v.$version/$git_version</generator>\n";
+       } elsif ($format eq 'atom') {
+               print <<XML;
+<feed xmlns="http://www.w3.org/2005/Atom">
+XML
+               print "<title>$title</title>\n" .
+                     "<subtitle>$descr</subtitle>\n" .
+                     '<link rel="alternate" type="text/html" href="' .
+                     $alt_url . '" />' . "\n" .
+                     '<link rel="self" type="' . $content_type . '" href="' .
+                     $cgi->self_url() . '" />' . "\n" .
+                     "<id>" . href(-full=>1) . "</id>\n" .
+                     # use project owner for feed author
+                     "<author><name>$owner</name></author>\n";
+               if (defined $favicon) {
+                       print "<icon>" . esc_url($favicon) . "</icon>\n";
+               }
+               if (defined $logo) {
+                       # not twice as wide as tall: 72 x 27 pixels
+                       print "<logo>" . esc_url($logo) . "</logo>\n";
+               }
+               if (! %latest_date) {
+                       # dummy date to keep the feed valid until commits trickle in:
+                       print "<updated>1970-01-01T00:00:00Z</updated>\n";
+               } else {
+                       print "<updated>$latest_date{'iso-8601'}</updated>\n";
+               }
+               print "<generator version='$version/$git_version'>gitweb</generator>\n";
+       }
+
+       # contents
+       for (my $i = 0; $i <= $#commitlist; $i++) {
+               my %co = %{$commitlist[$i]};
+               my $commit = $co{'id'};
+               # we read 150, we always show 30 and the ones more recent than 48 hours
+               if (($i >= 20) && ((time - $co{'author_epoch'}) > 48*60*60)) {
+                       last;
+               }
+               my %cd = parse_date($co{'author_epoch'}, $co{'author_tz'});
+
+               # get list of changed files
+               open my $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+                       $co{'parent'} || "--root",
+                       $co{'id'}, "--", (defined $file_name ? $file_name : ())
+                       or next;
+               my @difftree = map { chomp; $_ } <$fd>;
+               close $fd
+                       or next;
+
+               # print element (entry, item)
+               my $co_url = href(-full=>1, action=>"commitdiff", hash=>$commit);
+               if ($format eq 'rss') {
+                       print "<item>\n" .
+                             "<title>" . esc_html($co{'title'}) . "</title>\n" .
+                             "<author>" . esc_html($co{'author'}) . "</author>\n" .
+                             "<pubDate>$cd{'rfc2822'}</pubDate>\n" .
+                             "<guid isPermaLink=\"true\">$co_url</guid>\n" .
+                             "<link>$co_url</link>\n" .
+                             "<description>" . esc_html($co{'title'}) . "</description>\n" .
+                             "<content:encoded>" .
+                             "<![CDATA[\n";
+               } elsif ($format eq 'atom') {
+                       print "<entry>\n" .
+                             "<title type=\"html\">" . esc_html($co{'title'}) . "</title>\n" .
+                             "<updated>$cd{'iso-8601'}</updated>\n" .
+                             "<author>\n" .
+                             "  <name>" . esc_html($co{'author_name'}) . "</name>\n";
+                       if ($co{'author_email'}) {
+                               print "  <email>" . esc_html($co{'author_email'}) . "</email>\n";
+                       }
+                       print "</author>\n" .
+                             # use committer for contributor
+                             "<contributor>\n" .
+                             "  <name>" . esc_html($co{'committer_name'}) . "</name>\n";
+                       if ($co{'committer_email'}) {
+                               print "  <email>" . esc_html($co{'committer_email'}) . "</email>\n";
+                       }
+                       print "</contributor>\n" .
+                             "<published>$cd{'iso-8601'}</published>\n" .
+                             "<link rel=\"alternate\" type=\"text/html\" href=\"$co_url\" />\n" .
+                             "<id>$co_url</id>\n" .
+                             "<content type=\"xhtml\" xml:base=\"" . esc_url($my_url) . "\">\n" .
+                             "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n";
+               }
+               my $comment = $co{'comment'};
+               print "<pre>\n";
+               foreach my $line (@$comment) {
+                       $line = esc_html($line);
+                       print "$line\n";
+               }
+               print "</pre><ul>\n";
+               foreach my $difftree_line (@difftree) {
+                       my %difftree = parse_difftree_raw_line($difftree_line);
+                       next if !$difftree{'from_id'};
+
+                       my $file = $difftree{'file'} || $difftree{'to_file'};
+
+                       print "<li>" .
+                             "[" .
+                             $cgi->a({-href => href(-full=>1, action=>"blobdiff",
+                                                    hash=>$difftree{'to_id'}, hash_parent=>$difftree{'from_id'},
+                                                    hash_base=>$co{'id'}, hash_parent_base=>$co{'parent'},
+                                                    file_name=>$file, file_parent=>$difftree{'from_file'}),
+                                     -title => "diff"}, 'D');
+                       if ($have_blame) {
+                               print $cgi->a({-href => href(-full=>1, action=>"blame",
+                                                            file_name=>$file, hash_base=>$commit),
+                                             -title => "blame"}, 'B');
+                       }
+                       # if this is not a feed of a file history
+                       if (!defined $file_name || $file_name ne $file) {
+                               print $cgi->a({-href => href(-full=>1, action=>"history",
+                                                            file_name=>$file, hash=>$commit),
+                                             -title => "history"}, 'H');
+                       }
+                       $file = esc_path($file);
+                       print "] ".
+                             "$file</li>\n";
+               }
+               if ($format eq 'rss') {
+                       print "</ul>]]>\n" .
+                             "</content:encoded>\n" .
+                             "</item>\n";
+               } elsif ($format eq 'atom') {
+                       print "</ul>\n</div>\n" .
+                             "</content>\n" .
+                             "</entry>\n";
+               }
+       }
+
+       # end of feed
+       if ($format eq 'rss') {
+               print "</channel>\n</rss>\n";
+       } elsif ($format eq 'atom') {
+               print "</feed>\n";
+       }
+}
+
+sub git_rss {
+       git_feed('rss');
+}
+
+sub git_atom {
+       git_feed('atom');
+}
+
+sub git_opml {
+       my @list = git_get_projects_list($project_filter, $strict_export);
+       if (!@list) {
+               die_error(404, "No projects found");
+       }
+
+       print $cgi->header(
+               -type => 'text/xml',
+               -charset => 'utf-8',
+               -content_disposition => 'inline; filename="opml.xml"');
+
+       my $title = esc_html($site_name);
+       my $filter = " within subdirectory ";
+       if (defined $project_filter) {
+               $filter .= esc_html($project_filter);
+       } else {
+               $filter = "";
+       }
+       print <<XML;
+<?xml version="1.0" encoding="utf-8"?>
+<opml version="1.0">
+<head>
+  <title>$title OPML Export$filter</title>
+</head>
+<body>
+<outline text="git RSS feeds">
+XML
+
+       foreach my $pr (@list) {
+               my %proj = %$pr;
+               my $head = git_get_head_hash($proj{'path'});
+               if (!defined $head) {
+                       next;
+               }
+               $git_dir = "$projectroot/$proj{'path'}";
+               my %co = parse_commit($head);
+               if (!%co) {
+                       next;
+               }
+
+               my $path = esc_html(chop_str($proj{'path'}, 25, 5));
+               my $rss  = href('project' => $proj{'path'}, 'action' => 'rss', -full => 1);
+               my $html = href('project' => $proj{'path'}, 'action' => 'summary', -full => 1);
+               print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
+       }
+       print <<XML;
+</outline>
+</body>
+</opml>
+XML
+}
diff --git a/flidas-2017-12.xkb b/flidas-2017-12.xkb
new file mode 100644 (file)
index 0000000..42d0fba
--- /dev/null
@@ -0,0 +1,1820 @@
+xkb_keymap {
+xkb_keycodes "evdev+aliases(qwerty)" {
+    minimum = 8;
+    maximum = 255;
+     <ESC> = 9;
+    <AE01> = 10;
+    <AE02> = 11;
+    <AE03> = 12;
+    <AE04> = 13;
+    <AE05> = 14;
+    <AE06> = 15;
+    <AE07> = 16;
+    <AE08> = 17;
+    <AE09> = 18;
+    <AE10> = 19;
+    <AE11> = 20;
+    <AE12> = 21;
+    <BKSP> = 22;
+     <TAB> = 23;
+    <AD01> = 24;
+    <AD02> = 25;
+    <AD03> = 26;
+    <AD04> = 27;
+    <AD05> = 28;
+    <AD06> = 29;
+    <AD07> = 30;
+    <AD08> = 31;
+    <AD09> = 32;
+    <AD10> = 33;
+    <AD11> = 34;
+    <AD12> = 35;
+    <RTRN> = 36;
+    <LCTL> = 37;
+    <AC01> = 38;
+    <AC02> = 39;
+    <AC03> = 40;
+    <AC04> = 41;
+    <AC05> = 42;
+    <AC06> = 43;
+    <AC07> = 44;
+    <AC08> = 45;
+    <AC09> = 46;
+    <AC10> = 47;
+    <AC11> = 48;
+    <TLDE> = 49;
+    <LFSH> = 50;
+    <BKSL> = 51;
+    <AB01> = 52;
+    <AB02> = 53;
+    <AB03> = 54;
+    <AB04> = 55;
+    <AB05> = 56;
+    <AB06> = 57;
+    <AB07> = 58;
+    <AB08> = 59;
+    <AB09> = 60;
+    <AB10> = 61;
+    <RTSH> = 62;
+    <KPMU> = 63;
+    <LALT> = 64;
+    <SPCE> = 65;
+    <CAPS> = 66;
+    <FK01> = 67;
+    <FK02> = 68;
+    <FK03> = 69;
+    <FK04> = 70;
+    <FK05> = 71;
+    <FK06> = 72;
+    <FK07> = 73;
+    <FK08> = 74;
+    <FK09> = 75;
+    <FK10> = 76;
+    <NMLK> = 77;
+    <SCLK> = 78;
+     <KP7> = 79;
+     <KP8> = 80;
+     <KP9> = 81;
+    <KPSU> = 82;
+     <KP4> = 83;
+     <KP5> = 84;
+     <KP6> = 85;
+    <KPAD> = 86;
+     <KP1> = 87;
+     <KP2> = 88;
+     <KP3> = 89;
+     <KP0> = 90;
+    <KPDL> = 91;
+    <LVL3> = 92;
+    <LSGT> = 94;
+    <FK11> = 95;
+    <FK12> = 96;
+    <AB11> = 97;
+    <KATA> = 98;
+    <HIRA> = 99;
+    <HENK> = 100;
+    <HKTG> = 101;
+    <MUHE> = 102;
+    <JPCM> = 103;
+    <KPEN> = 104;
+    <RCTL> = 105;
+    <KPDV> = 106;
+    <PRSC> = 107;
+    <RALT> = 108;
+    <LNFD> = 109;
+    <HOME> = 110;
+      <UP> = 111;
+    <PGUP> = 112;
+    <LEFT> = 113;
+    <RGHT> = 114;
+     <END> = 115;
+    <DOWN> = 116;
+    <PGDN> = 117;
+     <INS> = 118;
+    <DELE> = 119;
+    <I120> = 120;
+    <MUTE> = 121;
+    <VOL-> = 122;
+    <VOL+> = 123;
+    <POWR> = 124;
+    <KPEQ> = 125;
+    <I126> = 126;
+    <PAUS> = 127;
+    <I128> = 128;
+    <I129> = 129;
+    <HNGL> = 130;
+    <HJCV> = 131;
+    <AE13> = 132;
+    <LWIN> = 133;
+    <RWIN> = 134;
+    <COMP> = 135;
+    <STOP> = 136;
+    <AGAI> = 137;
+    <PROP> = 138;
+    <UNDO> = 139;
+    <FRNT> = 140;
+    <COPY> = 141;
+    <OPEN> = 142;
+    <PAST> = 143;
+    <FIND> = 144;
+     <CUT> = 145;
+    <HELP> = 146;
+    <I147> = 147;
+    <I148> = 148;
+    <I149> = 149;
+    <I150> = 150;
+    <I151> = 151;
+    <I152> = 152;
+    <I153> = 153;
+    <I154> = 154;
+    <I155> = 155;
+    <I156> = 156;
+    <I157> = 157;
+    <I158> = 158;
+    <I159> = 159;
+    <I160> = 160;
+    <I161> = 161;
+    <I162> = 162;
+    <I163> = 163;
+    <I164> = 164;
+    <I165> = 165;
+    <I166> = 166;
+    <I167> = 167;
+    <I168> = 168;
+    <I169> = 169;
+    <I170> = 170;
+    <I171> = 171;
+    <I172> = 172;
+    <I173> = 173;
+    <I174> = 174;
+    <I175> = 175;
+    <I176> = 176;
+    <I177> = 177;
+    <I178> = 178;
+    <I179> = 179;
+    <I180> = 180;
+    <I181> = 181;
+    <I182> = 182;
+    <I183> = 183;
+    <I184> = 184;
+    <I185> = 185;
+    <I186> = 186;
+    <I187> = 187;
+    <I188> = 188;
+    <I189> = 189;
+    <I190> = 190;
+    <FK13> = 191;
+    <FK14> = 192;
+    <FK15> = 193;
+    <FK16> = 194;
+    <FK17> = 195;
+    <FK18> = 196;
+    <FK19> = 197;
+    <FK20> = 198;
+    <FK21> = 199;
+    <FK22> = 200;
+    <FK23> = 201;
+    <FK24> = 202;
+    <MDSW> = 203;
+     <ALT> = 204;
+    <META> = 205;
+    <SUPR> = 206;
+    <HYPR> = 207;
+    <I208> = 208;
+    <I209> = 209;
+    <I210> = 210;
+    <I211> = 211;
+    <I212> = 212;
+    <I213> = 213;
+    <I214> = 214;
+    <I215> = 215;
+    <I216> = 216;
+    <I217> = 217;
+    <I218> = 218;
+    <I219> = 219;
+    <I220> = 220;
+    <I221> = 221;
+    <I222> = 222;
+    <I223> = 223;
+    <I224> = 224;
+    <I225> = 225;
+    <I226> = 226;
+    <I227> = 227;
+    <I228> = 228;
+    <I229> = 229;
+    <I230> = 230;
+    <I231> = 231;
+    <I232> = 232;
+    <I233> = 233;
+    <I234> = 234;
+    <I235> = 235;
+    <I236> = 236;
+    <I237> = 237;
+    <I238> = 238;
+    <I239> = 239;
+    <I240> = 240;
+    <I241> = 241;
+    <I242> = 242;
+    <I243> = 243;
+    <I244> = 244;
+    <I245> = 245;
+    <I246> = 246;
+    <I247> = 247;
+    <I248> = 248;
+    <I249> = 249;
+    <I250> = 250;
+    <I251> = 251;
+    <I252> = 252;
+    <I253> = 253;
+    indicator 1 = "Caps Lock";
+    indicator 2 = "Num Lock";
+    indicator 3 = "Scroll Lock";
+    indicator 4 = "Compose";
+    indicator 5 = "Kana";
+    indicator 6 = "Sleep";
+    indicator 7 = "Suspend";
+    indicator 8 = "Mute";
+    indicator 9 = "Misc";
+    indicator 10 = "Mail";
+    indicator 11 = "Charging";
+    virtual indicator 12 = "Shift Lock";
+    virtual indicator 13 = "Group 2";
+    virtual indicator 14 = "Mouse Keys";
+    alias <AC12> = <BKSL>;
+    alias <MENU> = <COMP>;
+    alias <HZTG> = <TLDE>;
+    alias <LMTA> = <LWIN>;
+    alias <RMTA> = <RWIN>;
+    alias <ALGR> = <RALT>;
+    alias <KPPT> = <I129>;
+    alias <LatQ> = <AD01>;
+    alias <LatW> = <AD02>;
+    alias <LatE> = <AD03>;
+    alias <LatR> = <AD04>;
+    alias <LatT> = <AD05>;
+    alias <LatY> = <AD06>;
+    alias <LatU> = <AD07>;
+    alias <LatI> = <AD08>;
+    alias <LatO> = <AD09>;
+    alias <LatP> = <AD10>;
+    alias <LatA> = <AC01>;
+    alias <LatS> = <AC02>;
+    alias <LatD> = <AC03>;
+    alias <LatF> = <AC04>;
+    alias <LatG> = <AC05>;
+    alias <LatH> = <AC06>;
+    alias <LatJ> = <AC07>;
+    alias <LatK> = <AC08>;
+    alias <LatL> = <AC09>;
+    alias <LatZ> = <AB01>;
+    alias <LatX> = <AB02>;
+    alias <LatC> = <AB03>;
+    alias <LatV> = <AB04>;
+    alias <LatB> = <AB05>;
+    alias <LatN> = <AB06>;
+    alias <LatM> = <AB07>;
+};
+
+xkb_types "complete" {
+
+    virtual_modifiers NumLock,Alt,LevelThree,LAlt,RAlt,RControl,LControl,ScrollLock,LevelFive,AltGr,Meta,Super,Hyper;
+
+    type "ONE_LEVEL" {
+        modifiers= none;
+        level_name[Level1]= "Any";
+    };
+    type "TWO_LEVEL" {
+        modifiers= Shift;
+        map[Shift]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+    };
+    type "ALPHABETIC" {
+        modifiers= Shift+Lock;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Caps";
+    };
+    type "KEYPAD" {
+        modifiers= Shift+NumLock;
+        map[Shift]= Level2;
+        map[NumLock]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+    };
+    type "SHIFT+ALT" {
+        modifiers= Shift+Alt;
+        map[Shift+Alt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift+Alt";
+    };
+    type "PC_SUPER_LEVEL2" {
+        modifiers= Mod4;
+        map[Mod4]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Super";
+    };
+    type "PC_CONTROL_LEVEL2" {
+        modifiers= Control;
+        map[Control]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Control";
+    };
+    type "PC_LCONTROL_LEVEL2" {
+        modifiers= LControl;
+        map[LControl]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "LControl";
+    };
+    type "PC_RCONTROL_LEVEL2" {
+        modifiers= RControl;
+        map[RControl]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "RControl";
+    };
+    type "PC_ALT_LEVEL2" {
+        modifiers= Alt;
+        map[Alt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Alt";
+    };
+    type "PC_LALT_LEVEL2" {
+        modifiers= LAlt;
+        map[LAlt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "LAlt";
+    };
+    type "PC_RALT_LEVEL2" {
+        modifiers= RAlt;
+        map[RAlt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "RAlt";
+    };
+    type "CTRL+ALT" {
+        modifiers= Shift+Control+Alt+LevelThree;
+        map[Shift]= Level2;
+        preserve[Shift]= Shift;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        preserve[Shift+LevelThree]= Shift;
+        map[Control+Alt]= Level5;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "Ctrl+Alt";
+    };
+    type "LOCAL_EIGHT_LEVEL" {
+        modifiers= Shift+Lock+Control+LevelThree;
+        map[Shift+Lock]= Level1;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Control]= Level5;
+        map[Shift+Lock+Control]= Level5;
+        map[Shift+Control]= Level6;
+        map[Lock+Control]= Level6;
+        map[Control+LevelThree]= Level7;
+        map[Shift+Lock+Control+LevelThree]= Level7;
+        map[Shift+Control+LevelThree]= Level8;
+        map[Lock+Control+LevelThree]= Level8;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Level3";
+        level_name[Level4]= "Shift Level3";
+        level_name[Level5]= "Ctrl";
+        level_name[Level6]= "Shift Ctrl";
+        level_name[Level7]= "Level3 Ctrl";
+        level_name[Level8]= "Shift Level3 Ctrl";
+    };
+    type "THREE_LEVEL" {
+        modifiers= Shift+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Level3";
+    };
+    type "EIGHT_LEVEL" {
+        modifiers= Shift+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Shift+Lock+LevelThree]= Level3;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[Lock+LevelFive]= Level6;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[Lock+LevelThree+LevelFive]= Level8;
+        map[Shift+Lock+LevelThree+LevelFive]= Level7;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_SEMIALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level4;
+        preserve[Shift+Lock+LevelThree]= Lock;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[Lock+LevelFive]= Level6;
+        preserve[Lock+LevelFive]= Lock;
+        map[Shift+Lock+LevelFive]= Level6;
+        preserve[Shift+Lock+LevelFive]= Lock;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[Lock+LevelThree+LevelFive]= Level7;
+        preserve[Lock+LevelThree+LevelFive]= Lock;
+        map[Shift+Lock+LevelThree+LevelFive]= Level8;
+        preserve[Shift+Lock+LevelThree+LevelFive]= Lock;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "FOUR_LEVEL" {
+        modifiers= Shift+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Shift+Lock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_SEMIALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level4;
+        preserve[Shift+Lock+LevelThree]= Lock;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_MIXED_KEYPAD" {
+        modifiers= Shift+NumLock+LevelThree;
+        map[Shift+NumLock]= Level1;
+        map[NumLock]= Level2;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[NumLock+LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Shift+NumLock+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_X" {
+        modifiers= Shift+Control+Alt+LevelThree;
+        map[LevelThree]= Level2;
+        map[Shift+LevelThree]= Level3;
+        map[Control+Alt]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Alt Base";
+        level_name[Level3]= "Shift Alt";
+        level_name[Level4]= "Ctrl+Alt";
+    };
+    type "SEPARATE_CAPS_AND_SHIFT_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level4;
+        preserve[Lock]= Lock;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "AltGr Base";
+        level_name[Level4]= "Shift AltGr";
+    };
+    type "FOUR_LEVEL_PLUS_LOCK" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock]= Level5;
+        map[Shift+Lock]= Level2;
+        map[Lock+LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "Lock";
+    };
+    type "FOUR_LEVEL_KEYPAD" {
+        modifiers= Shift+NumLock+LevelThree;
+        map[Shift]= Level2;
+        map[NumLock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[NumLock+LevelThree]= Level4;
+        map[Shift+NumLock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Alt Number";
+    };
+};
+
+xkb_compatibility "complete" {
+
+    virtual_modifiers NumLock,Alt,LevelThree,LAlt,RAlt,RControl,LControl,ScrollLock,LevelFive,AltGr,Meta,Super,Hyper;
+
+    interpret.useModMapMods= AnyLevel;
+    interpret.repeat= False;
+    interpret.locking= False;
+    interpret ISO_Level2_Latch+Exactly(Shift) {
+        useModMapMods=level1;
+        action= LatchMods(modifiers=Shift,clearLocks,latchToLock);
+    };
+    interpret Shift_Lock+AnyOf(Shift+Lock) {
+        action= LockMods(modifiers=Shift);
+    };
+    interpret Num_Lock+AnyOf(all) {
+        virtualModifier= NumLock;
+        action= LockMods(modifiers=NumLock);
+    };
+    interpret ISO_Level3_Shift+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= SetMods(modifiers=LevelThree,clearLocks);
+    };
+    interpret ISO_Level3_Latch+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock);
+    };
+    interpret ISO_Level3_Lock+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= LockMods(modifiers=LevelThree);
+    };
+    interpret Alt_L+AnyOf(all) {
+        virtualModifier= Alt;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Alt_R+AnyOf(all) {
+        virtualModifier= Alt;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Meta_L+AnyOf(all) {
+        virtualModifier= Meta;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Meta_R+AnyOf(all) {
+        virtualModifier= Meta;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Super_L+AnyOf(all) {
+        virtualModifier= Super;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Super_R+AnyOf(all) {
+        virtualModifier= Super;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Hyper_L+AnyOf(all) {
+        virtualModifier= Hyper;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Hyper_R+AnyOf(all) {
+        virtualModifier= Hyper;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Scroll_Lock+AnyOf(all) {
+        virtualModifier= ScrollLock;
+        action= LockMods(modifiers=modMapMods);
+    };
+    interpret ISO_Level5_Shift+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= SetMods(modifiers=LevelFive,clearLocks);
+    };
+    interpret ISO_Level5_Latch+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock);
+    };
+    interpret ISO_Level5_Lock+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= LockMods(modifiers=LevelFive);
+    };
+    interpret Mode_switch+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= SetGroup(group=+1);
+    };
+    interpret ISO_Level3_Shift+AnyOfOrNone(all) {
+        action= SetMods(modifiers=LevelThree,clearLocks);
+    };
+    interpret ISO_Level3_Latch+AnyOfOrNone(all) {
+        action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock);
+    };
+    interpret ISO_Level3_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=LevelThree);
+    };
+    interpret ISO_Group_Latch+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LatchGroup(group=2);
+    };
+    interpret ISO_Next_Group+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LockGroup(group=+1);
+    };
+    interpret ISO_Prev_Group+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LockGroup(group=-1);
+    };
+    interpret ISO_First_Group+AnyOfOrNone(all) {
+        action= LockGroup(group=1);
+    };
+    interpret ISO_Last_Group+AnyOfOrNone(all) {
+        action= LockGroup(group=2);
+    };
+    interpret KP_1+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+1);
+    };
+    interpret KP_End+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+1);
+    };
+    interpret KP_2+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=+1);
+    };
+    interpret KP_Down+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=+1);
+    };
+    interpret KP_3+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+1);
+    };
+    interpret KP_Next+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+1);
+    };
+    interpret KP_4+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+0);
+    };
+    interpret KP_Left+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+0);
+    };
+    interpret KP_6+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+0);
+    };
+    interpret KP_Right+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+0);
+    };
+    interpret KP_7+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=-1);
+    };
+    interpret KP_Home+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=-1);
+    };
+    interpret KP_8+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=-1);
+    };
+    interpret KP_Up+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=-1);
+    };
+    interpret KP_9+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=-1);
+    };
+    interpret KP_Prior+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=-1);
+    };
+    interpret KP_5+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default);
+    };
+    interpret KP_Begin+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default);
+    };
+    interpret KP_F2+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=1);
+    };
+    interpret KP_Divide+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=1);
+    };
+    interpret KP_F3+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=2);
+    };
+    interpret KP_Multiply+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=2);
+    };
+    interpret KP_F4+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=3);
+    };
+    interpret KP_Subtract+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=3);
+    };
+    interpret KP_Separator+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default,count=2);
+    };
+    interpret KP_Add+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default,count=2);
+    };
+    interpret KP_0+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=lock);
+    };
+    interpret KP_Insert+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=lock);
+    };
+    interpret KP_Decimal+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=unlock);
+    };
+    interpret KP_Delete+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=unlock);
+    };
+    interpret F25+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=1);
+    };
+    interpret F26+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=2);
+    };
+    interpret F27+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=-1);
+    };
+    interpret F29+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=-1);
+    };
+    interpret F31+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default);
+    };
+    interpret F33+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+1);
+    };
+    interpret F35+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+1);
+    };
+    interpret Pointer_Button_Dflt+AnyOfOrNone(all) {
+        action= PtrBtn(button=default);
+    };
+    interpret Pointer_Button1+AnyOfOrNone(all) {
+        action= PtrBtn(button=1);
+    };
+    interpret Pointer_Button2+AnyOfOrNone(all) {
+        action= PtrBtn(button=2);
+    };
+    interpret Pointer_Button3+AnyOfOrNone(all) {
+        action= PtrBtn(button=3);
+    };
+    interpret Pointer_DblClick_Dflt+AnyOfOrNone(all) {
+        action= PtrBtn(button=default,count=2);
+    };
+    interpret Pointer_DblClick1+AnyOfOrNone(all) {
+        action= PtrBtn(button=1,count=2);
+    };
+    interpret Pointer_DblClick2+AnyOfOrNone(all) {
+        action= PtrBtn(button=2,count=2);
+    };
+    interpret Pointer_DblClick3+AnyOfOrNone(all) {
+        action= PtrBtn(button=3,count=2);
+    };
+    interpret Pointer_Drag_Dflt+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=default,affect=both);
+    };
+    interpret Pointer_Drag1+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=1,affect=both);
+    };
+    interpret Pointer_Drag2+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=2,affect=both);
+    };
+    interpret Pointer_Drag3+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=3,affect=both);
+    };
+    interpret Pointer_EnableKeys+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeys);
+    };
+    interpret Pointer_Accelerate+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeysAccel);
+    };
+    interpret Pointer_DfltBtnNext+AnyOfOrNone(all) {
+        action= SetPtrDflt(affect=button,button=+1);
+    };
+    interpret Pointer_DfltBtnPrev+AnyOfOrNone(all) {
+        action= SetPtrDflt(affect=button,button=-1);
+    };
+    interpret AccessX_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AccessXKeys);
+    };
+    interpret AccessX_Feedback_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AccessXFeedback);
+    };
+    interpret RepeatKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=RepeatKeys);
+    };
+    interpret SlowKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=SlowKeys);
+    };
+    interpret BounceKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=BounceKeys);
+    };
+    interpret StickyKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=StickyKeys);
+    };
+    interpret MouseKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeys);
+    };
+    interpret MouseKeys_Accel_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeysAccel);
+    };
+    interpret Overlay1_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=Overlay1);
+    };
+    interpret Overlay2_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=Overlay2);
+    };
+    interpret AudibleBell_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AudibleBell);
+    };
+    interpret Terminate_Server+AnyOfOrNone(all) {
+        action= Terminate();
+    };
+    interpret Alt_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Alt,clearLocks);
+    };
+    interpret Alt_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Alt,clearLocks);
+    };
+    interpret Meta_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Meta,clearLocks);
+    };
+    interpret Meta_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Meta,clearLocks);
+    };
+    interpret Super_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Super,clearLocks);
+    };
+    interpret Super_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Super,clearLocks);
+    };
+    interpret Hyper_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Hyper,clearLocks);
+    };
+    interpret Hyper_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Hyper,clearLocks);
+    };
+    interpret Shift_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Shift,clearLocks);
+    };
+    interpret XF86Switch_VT_1+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=1,!same);
+    };
+    interpret XF86Switch_VT_2+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=2,!same);
+    };
+    interpret XF86Switch_VT_3+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=3,!same);
+    };
+    interpret XF86Switch_VT_4+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=4,!same);
+    };
+    interpret XF86Switch_VT_5+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=5,!same);
+    };
+    interpret XF86Switch_VT_6+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=6,!same);
+    };
+    interpret XF86Switch_VT_7+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=7,!same);
+    };
+    interpret XF86Switch_VT_8+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=8,!same);
+    };
+    interpret XF86Switch_VT_9+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=9,!same);
+    };
+    interpret XF86Switch_VT_10+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=10,!same);
+    };
+    interpret XF86Switch_VT_11+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=11,!same);
+    };
+    interpret XF86Switch_VT_12+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=12,!same);
+    };
+    interpret XF86LogGrabInfo+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x50,data[1]=0x72,data[2]=0x47,data[3]=0x72,data[4]=0x62,data[5]=0x73,data[6]=0x00);
+    };
+    interpret XF86LogWindowTree+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x50,data[1]=0x72,data[2]=0x57,data[3]=0x69,data[4]=0x6e,data[5]=0x73,data[6]=0x00);
+    };
+    interpret XF86Next_VMode+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x2b,data[1]=0x56,data[2]=0x4d,data[3]=0x6f,data[4]=0x64,data[5]=0x65,data[6]=0x00);
+    };
+    interpret XF86Prev_VMode+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x2d,data[1]=0x56,data[2]=0x4d,data[3]=0x6f,data[4]=0x64,data[5]=0x65,data[6]=0x00);
+    };
+    interpret ISO_Level5_Shift+AnyOfOrNone(all) {
+        action= SetMods(modifiers=LevelFive,clearLocks);
+    };
+    interpret ISO_Level5_Latch+AnyOfOrNone(all) {
+        action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock);
+    };
+    interpret ISO_Level5_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=LevelFive);
+    };
+    interpret Caps_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=Lock);
+    };
+    interpret Any+Exactly(Lock) {
+        action= LockMods(modifiers=Lock);
+    };
+    interpret Any+AnyOf(all) {
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    group 2 = AltGr;
+    group 3 = AltGr;
+    group 4 = AltGr;
+    indicator "Caps Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= Lock;
+    };
+    indicator "Num Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= NumLock;
+    };
+    indicator "Scroll Lock" {
+        whichModState= locked;
+        modifiers= ScrollLock;
+    };
+    indicator "Shift Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= Shift;
+    };
+    indicator "Group 2" {
+        !allowExplicit;
+        groups= 0xfe;
+    };
+    indicator "Mouse Keys" {
+        indicatorDrivesKeyboard;
+        controls= mouseKeys;
+    };
+};
+
+xkb_symbols "pc+us+inet(evdev)" {
+
+    name[group1]="English (US)";
+
+    key  <ESC> {         [          Escape ] };
+    key <AE01> {         [               1,          exclam ] };
+    key <AE02> {         [               2,              at ] };
+    key <AE03> {         [               3,      numbersign ] };
+    key <AE04> {         [               4,          dollar ] };
+    key <AE05> {         [               5,         percent ] };
+    key <AE06> {         [               6,     asciicircum ] };
+    key <AE07> {         [               7,       ampersand ] };
+    key <AE08> {         [               8,        asterisk ] };
+    key <AE09> {         [               9,       parenleft ] };
+    key <AE10> {         [               0,      parenright ] };
+    key <AE11> {         [           minus,      underscore ] };
+    key <AE12> {         [           equal,            plus ] };
+    key <BKSP> {         [       BackSpace,       BackSpace ] };
+    key  <TAB> {         [             Tab,    ISO_Left_Tab ] };
+    key <AD01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               q,               Q ]
+    };
+    key <AD02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               w,               W ]
+    };
+    key <AD03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               e,               E ]
+    };
+    key <AD04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               r,               R ]
+    };
+    key <AD05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               t,               T ]
+    };
+    key <AD06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               y,               Y ]
+    };
+    key <AD07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               u,               U ]
+    };
+    key <AD08> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               i,               I ]
+    };
+    key <AD09> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               o,               O ]
+    };
+    key <AD10> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               p,               P ]
+    };
+    key <AD11> {         [     bracketleft,       braceleft ] };
+    key <AD12> {         [    bracketright,      braceright ] };
+    key <RTRN> {         [          Return ] };
+    key <LCTL> {         [       Control_L ] };
+    key <AC01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               a,               A ]
+    };
+    key <AC02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               s,               S ]
+    };
+    key <AC03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               d,               D ]
+    };
+    key <AC04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               f,               F ]
+    };
+    key <AC05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               g,               G ]
+    };
+    key <AC06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               h,               H ]
+    };
+    key <AC07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               j,               J ]
+    };
+    key <AC08> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               k,               K ]
+    };
+    key <AC09> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               l,               L ]
+    };
+    key <AC10> {         [       semicolon,           colon ] };
+    key <AC11> {         [      apostrophe,        quotedbl ] };
+    key <TLDE> {         [           grave,      asciitilde ] };
+    key <LFSH> {         [         Shift_L ] };
+    key <BKSL> {         [       backslash,             bar ] };
+    key <AB01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               z,               Z ]
+    };
+    key <AB02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               x,               X ]
+    };
+    key <AB03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               c,               C ]
+    };
+    key <AB04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               v,               V ]
+    };
+    key <AB05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               b,               B ]
+    };
+    key <AB06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               n,               N ]
+    };
+    key <AB07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               m,               M ]
+    };
+    key <AB08> {         [           comma,            less ] };
+    key <AB09> {         [          period,         greater ] };
+    key <AB10> {         [           slash,        question ] };
+    key <RTSH> {         [         Shift_R ] };
+    key <KPMU> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [     KP_Multiply,     KP_Multiply,     KP_Multiply,     KP_Multiply,   XF86ClearGrab ]
+    };
+    key <LALT> {         [           Alt_L,          Meta_L ] };
+    key <SPCE> {         [           space ] };
+    key <CAPS> {         [       Caps_Lock ] };
+    key <FK01> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F1,              F1,              F1,              F1, XF86Switch_VT_1 ]
+    };
+    key <FK02> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F2,              F2,              F2,              F2, XF86Switch_VT_2 ]
+    };
+    key <FK03> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F3,              F3,              F3,              F3, XF86Switch_VT_3 ]
+    };
+    key <FK04> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F4,              F4,              F4,              F4, XF86Switch_VT_4 ]
+    };
+    key <FK05> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F5,              F5,              F5,              F5, XF86Switch_VT_5 ]
+    };
+    key <FK06> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F6,              F6,              F6,              F6, XF86Switch_VT_6 ]
+    };
+    key <FK07> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F7,              F7,              F7,              F7, XF86Switch_VT_7 ]
+    };
+    key <FK08> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F8,              F8,              F8,              F8, XF86Switch_VT_8 ]
+    };
+    key <FK09> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F9,              F9,              F9,              F9, XF86Switch_VT_9 ]
+    };
+    key <FK10> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F10,             F10,             F10,             F10, XF86Switch_VT_10 ]
+    };
+    key <NMLK> {         [        Num_Lock ] };
+    key <SCLK> {         [     Scroll_Lock ] };
+    key  <KP7> {         [         KP_Home,            KP_7 ] };
+    key  <KP8> {         [           KP_Up,            KP_8 ] };
+    key  <KP9> {         [        KP_Prior,            KP_9 ] };
+    key <KPSU> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [     KP_Subtract,     KP_Subtract,     KP_Subtract,     KP_Subtract,  XF86Prev_VMode ]
+    };
+    key  <KP4> {         [         KP_Left,            KP_4 ] };
+    key  <KP5> {         [        KP_Begin,            KP_5 ] };
+    key  <KP6> {         [        KP_Right,            KP_6 ] };
+    key <KPAD> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [          KP_Add,          KP_Add,          KP_Add,          KP_Add,  XF86Next_VMode ]
+    };
+    key  <KP1> {         [          KP_End,            KP_1 ] };
+    key  <KP2> {         [         KP_Down,            KP_2 ] };
+    key  <KP3> {         [         KP_Next,            KP_3 ] };
+    key  <KP0> {         [       KP_Insert,            KP_0 ] };
+    key <KPDL> {         [       KP_Delete,      KP_Decimal ] };
+    key <LVL3> {         [ ISO_Level3_Shift ] };
+    key <LSGT> {
+        type= "FOUR_LEVEL",
+        symbols[Group1]= [            less,         greater,             bar,       brokenbar ]
+    };
+    key <FK11> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F11,             F11,             F11,             F11, XF86Switch_VT_11 ]
+    };
+    key <FK12> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F12,             F12,             F12,             F12, XF86Switch_VT_12 ]
+    };
+    key <KATA> {         [        Katakana ] };
+    key <HIRA> {         [        Hiragana ] };
+    key <HENK> {         [     Henkan_Mode ] };
+    key <HKTG> {         [ Hiragana_Katakana ] };
+    key <MUHE> {         [        Muhenkan ] };
+    key <KPEN> {         [        KP_Enter ] };
+    key <RCTL> {         [       Control_R ] };
+    key <KPDV> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [       KP_Divide,       KP_Divide,       KP_Divide,       KP_Divide,      XF86Ungrab ]
+    };
+    key <PRSC> {
+        type= "PC_ALT_LEVEL2",
+        symbols[Group1]= [           Print,         Sys_Req ]
+    };
+    key <RALT> {
+        type= "TWO_LEVEL",
+        symbols[Group1]= [           Alt_R,          Meta_R ]
+    };
+    key <LNFD> {         [        Linefeed ] };
+    key <HOME> {         [            Home ] };
+    key   <UP> {         [              Up ] };
+    key <PGUP> {         [           Prior ] };
+    key <LEFT> {         [            Left ] };
+    key <RGHT> {         [           Right ] };
+    key  <END> {         [             End ] };
+    key <DOWN> {         [            Down ] };
+    key <PGDN> {         [            Next ] };
+    key  <INS> {         [          Insert ] };
+    key <DELE> {         [          Delete ] };
+    key <MUTE> {         [   XF86AudioMute ] };
+    key <VOL-> {         [ XF86AudioLowerVolume ] };
+    key <VOL+> {         [ XF86AudioRaiseVolume ] };
+    key <POWR> {         [    XF86PowerOff ] };
+    key <KPEQ> {         [        KP_Equal ] };
+    key <I126> {         [       plusminus ] };
+    key <PAUS> {
+        type= "PC_CONTROL_LEVEL2",
+        symbols[Group1]= [           Pause,           Break ]
+    };
+    key <I128> {         [     XF86LaunchA ] };
+    key <I129> {         [      KP_Decimal,      KP_Decimal ] };
+    key <HNGL> {         [          Hangul ] };
+    key <HJCV> {         [    Hangul_Hanja ] };
+    key <LWIN> {         [         Super_L ] };
+    key <RWIN> {         [         Super_R ] };
+    key <COMP> {         [            Menu ] };
+    key <STOP> {         [          Cancel ] };
+    key <AGAI> {         [            Redo ] };
+    key <PROP> {         [        SunProps ] };
+    key <UNDO> {         [            Undo ] };
+    key <FRNT> {         [        SunFront ] };
+    key <COPY> {         [        XF86Copy ] };
+    key <OPEN> {         [        XF86Open ] };
+    key <PAST> {         [       XF86Paste ] };
+    key <FIND> {         [            Find ] };
+    key  <CUT> {         [         XF86Cut ] };
+    key <HELP> {         [            Help ] };
+    key <I147> {         [      XF86MenuKB ] };
+    key <I148> {         [  XF86Calculator ] };
+    key <I150> {         [       XF86Sleep ] };
+    key <I151> {         [      XF86WakeUp ] };
+    key <I152> {         [    XF86Explorer ] };
+    key <I153> {         [        XF86Send ] };
+    key <I155> {         [        XF86Xfer ] };
+    key <I156> {         [     XF86Launch1 ] };
+    key <I157> {         [     XF86Launch2 ] };
+    key <I158> {         [         XF86WWW ] };
+    key <I159> {         [         XF86DOS ] };
+    key <I160> {         [ XF86ScreenSaver ] };
+    key <I161> {         [ XF86RotateWindows ] };
+    key <I162> {         [    XF86TaskPane ] };
+    key <I163> {         [        XF86Mail ] };
+    key <I164> {         [   XF86Favorites ] };
+    key <I165> {         [  XF86MyComputer ] };
+    key <I166> {         [        XF86Back ] };
+    key <I167> {         [     XF86Forward ] };
+    key <I169> {         [       XF86Eject ] };
+    key <I170> {         [       XF86Eject,       XF86Eject ] };
+    key <I171> {         [   XF86AudioNext ] };
+    key <I172> {         [   XF86AudioPlay,  XF86AudioPause ] };
+    key <I173> {         [   XF86AudioPrev ] };
+    key <I174> {         [   XF86AudioStop,       XF86Eject ] };
+    key <I175> {         [ XF86AudioRecord ] };
+    key <I176> {         [ XF86AudioRewind ] };
+    key <I177> {         [       XF86Phone ] };
+    key <I179> {         [       XF86Tools ] };
+    key <I180> {         [    XF86HomePage ] };
+    key <I181> {         [      XF86Reload ] };
+    key <I182> {         [       XF86Close ] };
+    key <I185> {         [    XF86ScrollUp ] };
+    key <I186> {         [  XF86ScrollDown ] };
+    key <I187> {         [       parenleft ] };
+    key <I188> {         [      parenright ] };
+    key <I189> {         [         XF86New ] };
+    key <I190> {         [            Redo ] };
+    key <FK13> {         [       XF86Tools ] };
+    key <FK14> {         [     XF86Launch5 ] };
+    key <FK15> {         [     XF86Launch6 ] };
+    key <FK16> {         [     XF86Launch7 ] };
+    key <FK17> {         [     XF86Launch8 ] };
+    key <FK18> {         [     XF86Launch9 ] };
+    key <FK20> {         [ XF86AudioMicMute ] };
+    key <FK21> {         [ XF86TouchpadToggle ] };
+    key <FK22> {         [  XF86TouchpadOn ] };
+    key <FK23> {         [ XF86TouchpadOff ] };
+    key <MDSW> {         [     Mode_switch ] };
+    key  <ALT> {         [        NoSymbol,           Alt_L ] };
+    key <META> {         [        NoSymbol,          Meta_L ] };
+    key <SUPR> {         [        NoSymbol,         Super_L ] };
+    key <HYPR> {         [        NoSymbol,         Hyper_L ] };
+    key <I208> {         [   XF86AudioPlay ] };
+    key <I209> {         [  XF86AudioPause ] };
+    key <I210> {         [     XF86Launch3 ] };
+    key <I211> {         [     XF86Launch4 ] };
+    key <I212> {         [     XF86LaunchB ] };
+    key <I213> {         [     XF86Suspend ] };
+    key <I214> {         [       XF86Close ] };
+    key <I215> {         [   XF86AudioPlay ] };
+    key <I216> {         [ XF86AudioForward ] };
+    key <I218> {         [           Print ] };
+    key <I220> {         [      XF86WebCam ] };
+    key <I223> {         [        XF86Mail ] };
+    key <I224> {         [   XF86Messenger ] };
+    key <I225> {         [      XF86Search ] };
+    key <I226> {         [          XF86Go ] };
+    key <I227> {         [     XF86Finance ] };
+    key <I228> {         [        XF86Game ] };
+    key <I229> {         [        XF86Shop ] };
+    key <I231> {         [          Cancel ] };
+    key <I232> {         [ XF86MonBrightnessDown ] };
+    key <I233> {         [ XF86MonBrightnessUp ] };
+    key <I234> {         [  XF86AudioMedia ] };
+    key <I235> {         [     XF86Display ] };
+    key <I236> {         [ XF86KbdLightOnOff ] };
+    key <I237> {         [ XF86KbdBrightnessDown ] };
+    key <I238> {         [ XF86KbdBrightnessUp ] };
+    key <I239> {         [        XF86Send ] };
+    key <I240> {         [       XF86Reply ] };
+    key <I241> {         [ XF86MailForward ] };
+    key <I242> {         [        XF86Save ] };
+    key <I243> {         [   XF86Documents ] };
+    key <I244> {         [     XF86Battery ] };
+    key <I245> {         [   XF86Bluetooth ] };
+    key <I246> {         [        XF86WLAN ] };
+    modifier_map Control { <LCTL> };
+    modifier_map Shift { <LFSH> };
+    modifier_map Shift { <RTSH> };
+    modifier_map Mod1 { <LALT> };
+    modifier_map Lock { <CAPS> };
+    modifier_map Mod2 { <NMLK> };
+    modifier_map Mod5 { <LVL3> };
+    modifier_map Control { <RCTL> };
+    modifier_map Mod1 { <RALT> };
+    modifier_map Mod4 { <LWIN> };
+    modifier_map Mod4 { <RWIN> };
+    modifier_map Mod5 { <MDSW> };
+    modifier_map Mod1 { <META> };
+    modifier_map Mod4 { <SUPR> };
+    modifier_map Mod4 { <HYPR> };
+};
+
+xkb_geometry "pc(pc105)" {
+
+    width=       470;
+    height=      180;
+
+    alias <AC00> = <CAPS>;
+    alias <AA00> = <LCTL>;
+
+    baseColor=   "white";
+    labelColor=  "black";
+    xfont=       "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+    description= "Generic 105";
+
+    shape "NORM" {
+        corner= 1,
+        { [  18,  18 ] },
+        { [   2,   1 ], [  16,  16 ] }
+    };
+    shape "BKSP" {
+        corner= 1,
+        { [  38,  18 ] },
+        { [   2,   1 ], [  36,  16 ] }
+    };
+    shape "TABK" {
+        corner= 1,
+        { [  28,  18 ] },
+        { [   2,   1 ], [  26,  16 ] }
+    };
+    shape "BKSL" {
+        corner= 1,
+        { [  28,  18 ] },
+        { [   2,   1 ], [  26,  16 ] }
+    };
+    shape "RTRN" {
+        corner= 1,
+        { [   0,   0 ], [  28,   0 ], [  28,  37 ], [   5,  37 ],
+          [   5,  18 ], [   0,  18 ] },
+        { [   2,   1 ], [  26,   1 ], [  26,  35 ], [   7,  35 ],
+          [   7,  16 ], [   2,  16 ] },
+        approx= { [   5,   0 ], [  28,  37 ] }
+    };
+    shape "CAPS" {
+        corner= 1,
+        { [  33,  18 ] },
+        { [   2,   1 ], [  31,  16 ] }
+    };
+    shape "LFSH" {
+        corner= 1,
+        { [  25,  18 ] },
+        { [   2,   1 ], [  23,  16 ] }
+    };
+    shape "RTSH" {
+        corner= 1,
+        { [  50,  18 ] },
+        { [   2,   1 ], [  48,  16 ] }
+    };
+    shape "MODK" {
+        corner= 1,
+        { [  27,  18 ] },
+        { [   2,   1 ], [  25,  16 ] }
+    };
+    shape "SMOD" {
+        corner= 1,
+        { [  23,  18 ] },
+        { [   2,   1 ], [  21,  16 ] }
+    };
+    shape "SPCE" {
+        corner= 1,
+        { [ 113,  18 ] },
+        { [   2,   1 ], [ 111,  16 ] }
+    };
+    shape "KP0" {
+        corner= 1,
+        { [  37,  18 ] },
+        { [   2,   1 ], [  35,  16 ] }
+    };
+    shape "KPAD" {
+        corner= 1,
+        { [  18,  37 ] },
+        { [   2,   1 ], [  16,  35 ] }
+    };
+    shape "LEDS" { { [  75,  20 ] } };
+    shape "LED" { { [   5,   1 ] } };
+    section "Function" {
+        key.color= "grey20";
+        priority=  7;
+        top=       22;
+        left=      19;
+        width=     351;
+        height=    19;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                {  <ESC>, "NORM",   1 },
+                { <FK01>, "NORM",  20, color="white" },
+                { <FK02>, "NORM",   1, color="white" },
+                { <FK03>, "NORM",   1, color="white" },
+                { <FK04>, "NORM",   1, color="white" },
+                { <FK05>, "NORM",  11, color="white" },
+                { <FK06>, "NORM",   1, color="white" },
+                { <FK07>, "NORM",   1, color="white" },
+                { <FK08>, "NORM",   1, color="white" },
+                { <FK09>, "NORM",  11, color="white" },
+                { <FK10>, "NORM",   1, color="white" },
+                { <FK11>, "NORM",   1, color="white" },
+                { <FK12>, "NORM",   1, color="white" },
+                { <PRSC>, "NORM",   8, color="white" },
+                { <SCLK>, "NORM",   1, color="white" },
+                { <PAUS>, "NORM",   1, color="white" }
+            };
+        };
+    }; // End of "Function" section
+
+    section "Alpha" {
+        key.color= "white";
+        priority=  8;
+        top=       61;
+        left=      19;
+        width=     287;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                { <TLDE>, "NORM",   1 }, { <AE01>, "NORM",   1 },
+                { <AE02>, "NORM",   1 }, { <AE03>, "NORM",   1 },
+                { <AE04>, "NORM",   1 }, { <AE05>, "NORM",   1 },
+                { <AE06>, "NORM",   1 }, { <AE07>, "NORM",   1 },
+                { <AE08>, "NORM",   1 }, { <AE09>, "NORM",   1 },
+                { <AE10>, "NORM",   1 }, { <AE11>, "NORM",   1 },
+                { <AE12>, "NORM",   1 },
+                { <BKSP>, "BKSP",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                {  <TAB>, "TABK",   1, color="grey20" },
+                { <AD01>, "NORM",   1 }, { <AD02>, "NORM",   1 },
+                { <AD03>, "NORM",   1 }, { <AD04>, "NORM",   1 },
+                { <AD05>, "NORM",   1 }, { <AD06>, "NORM",   1 },
+                { <AD07>, "NORM",   1 }, { <AD08>, "NORM",   1 },
+                { <AD09>, "NORM",   1 }, { <AD10>, "NORM",   1 },
+                { <AD11>, "NORM",   1 }, { <AD12>, "NORM",   1 },
+                { <RTRN>, "RTRN",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  39;
+            left= 1;
+            keys {
+                { <CAPS>, "CAPS",   1, color="grey20" },
+                { <AC01>, "NORM",   1 }, { <AC02>, "NORM",   1 },
+                { <AC03>, "NORM",   1 }, { <AC04>, "NORM",   1 },
+                { <AC05>, "NORM",   1 }, { <AC06>, "NORM",   1 },
+                { <AC07>, "NORM",   1 }, { <AC08>, "NORM",   1 },
+                { <AC09>, "NORM",   1 }, { <AC10>, "NORM",   1 },
+                { <AC11>, "NORM",   1 }, { <BKSL>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  58;
+            left= 1;
+            keys {
+                { <LFSH>, "LFSH",   1, color="grey20" },
+                { <LSGT>, "NORM",   1 }, { <AB01>, "NORM",   1 },
+                { <AB02>, "NORM",   1 }, { <AB03>, "NORM",   1 },
+                { <AB04>, "NORM",   1 }, { <AB05>, "NORM",   1 },
+                { <AB06>, "NORM",   1 }, { <AB07>, "NORM",   1 },
+                { <AB08>, "NORM",   1 }, { <AB09>, "NORM",   1 },
+                { <AB10>, "NORM",   1 },
+                { <RTSH>, "RTSH",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                { <LCTL>, "MODK",   1, color="grey20" },
+                { <LWIN>, "SMOD",   1, color="grey20" },
+                { <LALT>, "SMOD",   1, color="grey20" },
+                { <SPCE>, "SPCE",   1 },
+                { <RALT>, "SMOD",   1, color="grey20" },
+                { <RWIN>, "SMOD",   1, color="grey20" },
+                { <MENU>, "SMOD",   1, color="grey20" },
+                { <RCTL>, "SMOD",   1, color="grey20" }
+            };
+        };
+    }; // End of "Alpha" section
+
+    section "Editing" {
+        key.color= "grey20";
+        priority=  9;
+        top=       61;
+        left=      312;
+        width=     58;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                {  <INS>, "NORM",   1 }, { <HOME>, "NORM",   1 },
+                { <PGUP>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                { <DELE>, "NORM",   1 }, {  <END>, "NORM",   1 },
+                { <PGDN>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  58;
+            left= 20;
+            keys {
+                {   <UP>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                { <LEFT>, "NORM",   1 }, { <DOWN>, "NORM",   1 },
+                { <RGHT>, "NORM",   1 }
+            };
+        };
+    }; // End of "Editing" section
+
+    section "Keypad" {
+        key.color= "grey20";
+        priority=  10;
+        top=       61;
+        left=      376;
+        width=     77;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                { <NMLK>, "NORM",   1 }, { <KPDV>, "NORM",   1 },
+                { <KPMU>, "NORM",   1 }, { <KPSU>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                {  <KP7>, "NORM",   1, color="white" },
+                {  <KP8>, "NORM",   1, color="white" },
+                {  <KP9>, "NORM",   1, color="white" },
+                { <KPAD>, "KPAD",   1 }
+            };
+        };
+        row {
+            top=  39;
+            left= 1;
+            keys {
+                {  <KP4>, "NORM",   1, color="white" },
+                {  <KP5>, "NORM",   1, color="white" },
+                {  <KP6>, "NORM",   1, color="white" }
+            };
+        };
+        row {
+            top=  58;
+            left= 1;
+            keys {
+                {  <KP1>, "NORM",   1, color="white" },
+                {  <KP2>, "NORM",   1, color="white" },
+                {  <KP3>, "NORM",   1, color="white" },
+                { <KPEN>, "KPAD",   1 }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                {  <KP0>, "KP0",   1, color="white" },
+                { <KPDL>, "NORM",   1, color="white" }
+            };
+        };
+    }; // End of "Keypad" section
+
+    solid "LedPanel" {
+        top=      22;
+        left=     377;
+        priority= 0;
+        color= "grey10";
+        shape= "LEDS";
+    };
+    indicator "Num Lock" {
+        top=      37;
+        left=     382;
+        priority= 1;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    indicator "Caps Lock" {
+        top=      37;
+        left=     407;
+        priority= 2;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    indicator "Scroll Lock" {
+        top=      37;
+        left=     433;
+        priority= 3;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    text "NumLockLabel" {
+        top=      25;
+        left=     378;
+        priority= 4;
+        width=  19.8;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Num\nLock";
+    };
+    text "CapsLockLabel" {
+        top=      25;
+        left=     403;
+        priority= 5;
+        width=  26.4;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Caps\nLock";
+    };
+    text "ScrollLockLabel" {
+        top=      25;
+        left=     428;
+        priority= 6;
+        width=  39.6;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Scroll\nLock";
+    };
+};
+
+};
diff --git a/machine_specific/btrbk.hosts b/machine_specific/btrbk.hosts
new file mode 100644 (file)
index 0000000..3128123
--- /dev/null
@@ -0,0 +1,2 @@
+treetowl
+frodo
\ No newline at end of file
diff --git a/machine_specific/btrbk/filesystem/etc/systemd/system/btrbk.service b/machine_specific/btrbk/filesystem/etc/systemd/system/btrbk.service
new file mode 100644 (file)
index 0000000..4be721d
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Btrbk backup
+After=multi-user.target
+
+[Service]
+Type=oneshot
+ExecStart=/a/bin/log-quiet/sysd-mail-once btrbk /a/exe/btrbk-run
diff --git a/machine_specific/btrbk/filesystem/etc/systemd/system/btrbk.timer b/machine_specific/btrbk/filesystem/etc/systemd/system/btrbk.timer
new file mode 100644 (file)
index 0000000..27113f6
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Run btrbk-run once every 20 mins
+
+[Timer]
+OnCalendar=*:0/20
+
+[Install]
+WantedBy=timers.target
diff --git a/machine_specific/li/filesystem/etc/bind/bind-writable/db.b8.nz b/machine_specific/li/filesystem/etc/bind/bind-writable/db.b8.nz
new file mode 100644 (file)
index 0000000..fa2298d
--- /dev/null
@@ -0,0 +1,12 @@
+$ORIGIN .
+$TTL 60        ; 1 minute
+b8.nz                  IN SOA  b8.nz. admin.b8.nz. (
+                               25         ; serial
+                               604800     ; refresh (1 week)
+                               86400      ; retry (1 day)
+                               2419200    ; expire (4 weeks)
+                               60         ; minimum (1 minute)
+                               )
+                       NS      n1.iankelling.org.
+                       NS      n2.iankelling.org.
+                       A       73.249.108.7
diff --git a/machine_specific/li/filesystem/etc/bind/named.conf.local b/machine_specific/li/filesystem/etc/bind/named.conf.local
new file mode 100644 (file)
index 0000000..e3e3229
--- /dev/null
@@ -0,0 +1,20 @@
+//
+// Do any local configuration here
+//
+
+// Consider adding the 1918 zones here, if they are not used in your
+// organization
+// ian: ok. uncommented. 1918 zones are private ip ranges.
+include "/etc/bind/zones.rfc1918";
+
+
+zone "b8.nz" {
+type master;
+file "/etc/bind/bind-writable/db.b8.nz";
+  update-policy {
+    // allow host to update themselves with a key having their own name
+    grant b8.nz. name b8.nz. A TXT;
+    grant b8.nz. name *.b8.nz. A TXT;
+  };
+};
+include "/etc/bind/key.b8.nz";
diff --git a/machine_specific/treetowl/filesystem/etc/cron.d/treetowl b/machine_specific/treetowl/filesystem/etc/cron.d/treetowl
new file mode 100755 (executable)
index 0000000..485813f
--- /dev/null
@@ -0,0 +1,13 @@
+# disabled for various reasons
+# */5 * * * * /a/bin/offlineimap_cronjob
+# */1 * * * * unison ~/.mpv ssh://htpc/.mpv
+# small-backpup:
+# runs every 10 minutes, every day 4 am, every sunday at 3 am
+# stored for 3 days, 2 months, and unlimited respectively
+SHELL=/bin/bash
+0    4 * * * ian x=$(/usr/local/bin/logq /a/exe/small-backup --retry daily 2M); [[ $? != 0 ]] && echo "$x"
+0    3 * * sun ian echo weekly backup results:; /a/exe/small-backup --retry weekly
+*/5  * * * *   ian /a/bin/distro-setup/dynamic-ip-update.sh 2>&1 | /usr/local/bin/log-once -15
+*/10 * * * *   ian /a/exe/small-backup 10minutes 3D 2>&1 | /usr/local/bin/log-once small-backup-10min
+# put things we don't want to send mail about below this:
+# MAILTO=""
diff --git a/machine_specific/treetowl/filesystem/etc/cron.daily/check-lets-encrypt-ssl-settings b/machine_specific/treetowl/filesystem/etc/cron.daily/check-lets-encrypt-ssl-settings
new file mode 100755 (executable)
index 0000000..87e45c0
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/bash -l
+# Copyright (C) 2016 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eE -o pipefail
+trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" returned $?" >&2' ERR
+
+lock_file=/tmp/check-lets-encrypt-ssl-settings
+if [[ -e $lock_file ]]; then
+    exit 0
+fi
+
+d=/a/opt/certbot
+gitget https://github.com/certbot/certbot.git $d &>/tmp/${0##*/}.log
+cd $d
+
+f=certbot-apache/certbot_apache/options-ssl-apache.conf
+out=$(git log -p --since 2016-08-14 $f)
+
+if [[ $out ]]; then
+    cat <<EOF
+Let's encrypt has new ssl settings. Update your web servers, then
+edit the date in $0, and
+rm $lock_file
+The change is:
+$out
+EOF
+    touch $lock_file
+fi
diff --git a/machine_specific/treetowl/filesystem/etc/cron.daily/mediawiki_backup b/machine_specific/treetowl/filesystem/etc/cron.daily/mediawiki_backup
new file mode 100755 (executable)
index 0000000..51dcf6b
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/bash
+# if we get an error, keep going but return it at the end
+last_error=0
+trap 'last_error=$?' ERR
+source ~/mw_vars
+# No strict because the host is likely not named the same as
+# the domain.
+ssh="ssh -oStrictHostKeyChecking=no"
+logfile=/var/log/${mwdomain}_backup.log
+{
+    echo "#### starting backup at $(date) ####"
+    $ssh root@$mwdomain <<ENDSSH
+set -x
+tee -a $mwc<<'EOF'
+\$wgReadOnly = 'Dumping Database, Access will be restored shortly';
+EOF
+mkdir -p ~/wiki_backups
+mysqldump -p$dbpass --default-character-set=binary my_wiki  > ~/wiki_backups/wiki_db_backup
+sed -i '\$ d' $mwc # delete read only setting
+ENDSSH
+    # add no strict option to the defaults
+
+    rdiff() { rdiff-backup --remote-schema "$ssh -C  %s rdiff-backup --server" "$@"; }
+    set -x
+    rdiff root@$mwdomain::/root/wiki_backups ~/backup/${mwdomain}_wiki_db_backup
+    rdiff root@$mwdomain::$mw ~/backup/${mwdomain}_wiki_file_backup
+    set +x
+    echo "=== ending backup at $(date) ===="
+}  &>>$logfile
+if [[ $last_error != 0 ]]; then
+    echo "backup for $mwdomain failed. See $logfile"
+fi
+exit $last_error
diff --git a/machine_specific/treetowl/filesystem/etc/systemd/system/btrbk.service b/machine_specific/treetowl/filesystem/etc/systemd/system/btrbk.service
new file mode 100644 (file)
index 0000000..4be721d
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Btrbk backup
+After=multi-user.target
+
+[Service]
+Type=oneshot
+ExecStart=/a/bin/log-quiet/sysd-mail-once btrbk /a/exe/btrbk-run
diff --git a/machine_specific/treetowl/filesystem/etc/systemd/system/btrbk.timer b/machine_specific/treetowl/filesystem/etc/systemd/system/btrbk.timer
new file mode 100644 (file)
index 0000000..27113f6
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=Run btrbk-run once every 20 mins
+
+[Timer]
+OnCalendar=*:0/20
+
+[Install]
+WantedBy=timers.target
diff --git a/model01.xkb b/model01.xkb
new file mode 100644 (file)
index 0000000..0a2d1fb
--- /dev/null
@@ -0,0 +1,1820 @@
+xkb_keymap {
+xkb_keycodes "evdev+aliases(qwerty)" {
+    minimum = 8;
+    maximum = 255;
+     <ESC> = 9;
+    <AE01> = 10;
+    <AE02> = 11;
+    <AE03> = 12;
+    <AE04> = 13;
+    <AE05> = 14;
+    <AE06> = 15;
+    <AE07> = 16;
+    <AE08> = 17;
+    <AE09> = 18;
+    <AE10> = 19;
+    <AE11> = 20;
+    <AE12> = 21;
+    <BKSP> = 22;
+     <TAB> = 23;
+    <AD01> = 24;
+    <AD02> = 25;
+    <AD03> = 26;
+    <AD04> = 27;
+    <AD05> = 28;
+    <AD06> = 29;
+    <AD07> = 30;
+    <AD08> = 31;
+    <AD09> = 32;
+    <AD10> = 33;
+    <AD11> = 34;
+    <AD12> = 35;
+    <RTRN> = 36;
+    <LCTL> = 37;
+    <AC01> = 38;
+    <AC02> = 39;
+    <AC03> = 40;
+    <AC04> = 41;
+    <AC05> = 42;
+    <AC06> = 43;
+    <AC07> = 44;
+    <AC08> = 45;
+    <AC09> = 46;
+    <AC10> = 47;
+    <AC11> = 48;
+    <TLDE> = 49;
+    <LFSH> = 50;
+    <BKSL> = 51;
+    <AB01> = 52;
+    <AB02> = 53;
+    <AB03> = 54;
+    <AB04> = 55;
+    <AB05> = 56;
+    <AB06> = 57;
+    <AB07> = 58;
+    <AB08> = 59;
+    <AB09> = 60;
+    <AB10> = 61;
+    <RTSH> = 62;
+    <KPMU> = 63;
+    <LALT> = 64;
+    <SPCE> = 65;
+    <CAPS> = 66;
+    <FK01> = 67;
+    <FK02> = 68;
+    <FK03> = 69;
+    <FK04> = 70;
+    <FK05> = 71;
+    <FK06> = 72;
+    <FK07> = 73;
+    <FK08> = 74;
+    <FK09> = 75;
+    <FK10> = 76;
+    <NMLK> = 77;
+    <SCLK> = 78;
+     <KP7> = 79;
+     <KP8> = 80;
+     <KP9> = 81;
+    <KPSU> = 82;
+     <KP4> = 83;
+     <KP5> = 84;
+     <KP6> = 85;
+    <KPAD> = 86;
+     <KP1> = 87;
+     <KP2> = 88;
+     <KP3> = 89;
+     <KP0> = 90;
+    <KPDL> = 91;
+    <LVL3> = 92;
+    <LSGT> = 94;
+    <FK11> = 95;
+    <FK12> = 96;
+    <AB11> = 97;
+    <KATA> = 98;
+    <HIRA> = 99;
+    <HENK> = 100;
+    <HKTG> = 101;
+    <MUHE> = 102;
+    <JPCM> = 103;
+    <KPEN> = 104;
+    <RCTL> = 105;
+    <KPDV> = 106;
+    <PRSC> = 107;
+    <RALT> = 108;
+    <LNFD> = 109;
+    <HOME> = 110;
+      <UP> = 111;
+    <PGUP> = 112;
+    <LEFT> = 113;
+    <RGHT> = 114;
+     <END> = 115;
+    <DOWN> = 116;
+    <PGDN> = 117;
+     <INS> = 118;
+    <DELE> = 119;
+    <I120> = 120;
+    <MUTE> = 121;
+    <VOL-> = 122;
+    <VOL+> = 123;
+    <POWR> = 124;
+    <KPEQ> = 125;
+    <I126> = 126;
+    <PAUS> = 127;
+    <I128> = 128;
+    <I129> = 129;
+    <HNGL> = 130;
+    <HJCV> = 131;
+    <AE13> = 132;
+    <LWIN> = 133;
+    <RWIN> = 134;
+    <COMP> = 135;
+    <STOP> = 136;
+    <AGAI> = 137;
+    <PROP> = 138;
+    <UNDO> = 139;
+    <FRNT> = 140;
+    <COPY> = 141;
+    <OPEN> = 142;
+    <PAST> = 143;
+    <FIND> = 144;
+     <CUT> = 145;
+    <HELP> = 146;
+    <I147> = 147;
+    <I148> = 148;
+    <I149> = 149;
+    <I150> = 150;
+    <I151> = 151;
+    <I152> = 152;
+    <I153> = 153;
+    <I154> = 154;
+    <I155> = 155;
+    <I156> = 156;
+    <I157> = 157;
+    <I158> = 158;
+    <I159> = 159;
+    <I160> = 160;
+    <I161> = 161;
+    <I162> = 162;
+    <I163> = 163;
+    <I164> = 164;
+    <I165> = 165;
+    <I166> = 166;
+    <I167> = 167;
+    <I168> = 168;
+    <I169> = 169;
+    <I170> = 170;
+    <I171> = 171;
+    <I172> = 172;
+    <I173> = 173;
+    <I174> = 174;
+    <I175> = 175;
+    <I176> = 176;
+    <I177> = 177;
+    <I178> = 178;
+    <I179> = 179;
+    <I180> = 180;
+    <I181> = 181;
+    <I182> = 182;
+    <I183> = 183;
+    <I184> = 184;
+    <I185> = 185;
+    <I186> = 186;
+    <I187> = 187;
+    <I188> = 188;
+    <I189> = 189;
+    <I190> = 190;
+    <FK13> = 191;
+    <FK14> = 192;
+    <FK15> = 193;
+    <FK16> = 194;
+    <FK17> = 195;
+    <FK18> = 196;
+    <FK19> = 197;
+    <FK20> = 198;
+    <FK21> = 199;
+    <FK22> = 200;
+    <FK23> = 201;
+    <FK24> = 202;
+    <MDSW> = 203;
+     <ALT> = 204;
+    <META> = 205;
+    <SUPR> = 206;
+    <HYPR> = 207;
+    <I208> = 208;
+    <I209> = 209;
+    <I210> = 210;
+    <I211> = 211;
+    <I212> = 212;
+    <I213> = 213;
+    <I214> = 214;
+    <I215> = 215;
+    <I216> = 216;
+    <I217> = 217;
+    <I218> = 218;
+    <I219> = 219;
+    <I220> = 220;
+    <I221> = 221;
+    <I222> = 222;
+    <I223> = 223;
+    <I224> = 224;
+    <I225> = 225;
+    <I226> = 226;
+    <I227> = 227;
+    <I228> = 228;
+    <I229> = 229;
+    <I230> = 230;
+    <I231> = 231;
+    <I232> = 232;
+    <I233> = 233;
+    <I234> = 234;
+    <I235> = 235;
+    <I236> = 236;
+    <I237> = 237;
+    <I238> = 238;
+    <I239> = 239;
+    <I240> = 240;
+    <I241> = 241;
+    <I242> = 242;
+    <I243> = 243;
+    <I244> = 244;
+    <I245> = 245;
+    <I246> = 246;
+    <I247> = 247;
+    <I248> = 248;
+    <I249> = 249;
+    <I250> = 250;
+    <I251> = 251;
+    <I252> = 252;
+    <I253> = 253;
+    indicator 1 = "Caps Lock";
+    indicator 2 = "Num Lock";
+    indicator 3 = "Scroll Lock";
+    indicator 4 = "Compose";
+    indicator 5 = "Kana";
+    indicator 6 = "Sleep";
+    indicator 7 = "Suspend";
+    indicator 8 = "Mute";
+    indicator 9 = "Misc";
+    indicator 10 = "Mail";
+    indicator 11 = "Charging";
+    virtual indicator 12 = "Shift Lock";
+    virtual indicator 13 = "Group 2";
+    virtual indicator 14 = "Mouse Keys";
+    alias <AC12> = <BKSL>;
+    alias <MENU> = <COMP>;
+    alias <HZTG> = <TLDE>;
+    alias <LMTA> = <LWIN>;
+    alias <RMTA> = <RWIN>;
+    alias <ALGR> = <RALT>;
+    alias <KPPT> = <I129>;
+    alias <LatQ> = <AD01>;
+    alias <LatW> = <AD02>;
+    alias <LatE> = <AD03>;
+    alias <LatR> = <AD04>;
+    alias <LatT> = <AD05>;
+    alias <LatY> = <AD06>;
+    alias <LatU> = <AD07>;
+    alias <LatI> = <AD08>;
+    alias <LatO> = <AD09>;
+    alias <LatP> = <AD10>;
+    alias <LatA> = <AC01>;
+    alias <LatS> = <AC02>;
+    alias <LatD> = <AC03>;
+    alias <LatF> = <AC04>;
+    alias <LatG> = <AC05>;
+    alias <LatH> = <AC06>;
+    alias <LatJ> = <AC07>;
+    alias <LatK> = <AC08>;
+    alias <LatL> = <AC09>;
+    alias <LatZ> = <AB01>;
+    alias <LatX> = <AB02>;
+    alias <LatC> = <AB03>;
+    alias <LatV> = <AB04>;
+    alias <LatB> = <AB05>;
+    alias <LatN> = <AB06>;
+    alias <LatM> = <AB07>;
+};
+
+xkb_types "complete" {
+
+    virtual_modifiers NumLock,Alt,LevelThree,LAlt,RAlt,RControl,LControl,ScrollLock,LevelFive,AltGr,Meta,Super,Hyper;
+
+    type "ONE_LEVEL" {
+        modifiers= none;
+        level_name[Level1]= "Any";
+    };
+    type "TWO_LEVEL" {
+        modifiers= Shift;
+        map[Shift]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+    };
+    type "ALPHABETIC" {
+        modifiers= Shift+Lock;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Caps";
+    };
+    type "KEYPAD" {
+        modifiers= Shift+NumLock;
+        map[Shift]= Level2;
+        map[NumLock]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+    };
+    type "SHIFT+ALT" {
+        modifiers= Shift+Alt;
+        map[Shift+Alt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift+Alt";
+    };
+    type "PC_SUPER_LEVEL2" {
+        modifiers= Mod4;
+        map[Mod4]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Super";
+    };
+    type "PC_CONTROL_LEVEL2" {
+        modifiers= Control;
+        map[Control]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Control";
+    };
+    type "PC_LCONTROL_LEVEL2" {
+        modifiers= LControl;
+        map[LControl]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "LControl";
+    };
+    type "PC_RCONTROL_LEVEL2" {
+        modifiers= RControl;
+        map[RControl]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "RControl";
+    };
+    type "PC_ALT_LEVEL2" {
+        modifiers= Alt;
+        map[Alt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Alt";
+    };
+    type "PC_LALT_LEVEL2" {
+        modifiers= LAlt;
+        map[LAlt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "LAlt";
+    };
+    type "PC_RALT_LEVEL2" {
+        modifiers= RAlt;
+        map[RAlt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "RAlt";
+    };
+    type "CTRL+ALT" {
+        modifiers= Shift+Control+Alt+LevelThree;
+        map[Shift]= Level2;
+        preserve[Shift]= Shift;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        preserve[Shift+LevelThree]= Shift;
+        map[Control+Alt]= Level5;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "Ctrl+Alt";
+    };
+    type "LOCAL_EIGHT_LEVEL" {
+        modifiers= Shift+Lock+Control+LevelThree;
+        map[Shift+Lock]= Level1;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Control]= Level5;
+        map[Shift+Lock+Control]= Level5;
+        map[Shift+Control]= Level6;
+        map[Lock+Control]= Level6;
+        map[Control+LevelThree]= Level7;
+        map[Shift+Lock+Control+LevelThree]= Level7;
+        map[Shift+Control+LevelThree]= Level8;
+        map[Lock+Control+LevelThree]= Level8;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Level3";
+        level_name[Level4]= "Shift Level3";
+        level_name[Level5]= "Ctrl";
+        level_name[Level6]= "Shift Ctrl";
+        level_name[Level7]= "Level3 Ctrl";
+        level_name[Level8]= "Shift Level3 Ctrl";
+    };
+    type "THREE_LEVEL" {
+        modifiers= Shift+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Level3";
+    };
+    type "EIGHT_LEVEL" {
+        modifiers= Shift+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Shift+Lock+LevelThree]= Level3;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[Lock+LevelFive]= Level6;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[Lock+LevelThree+LevelFive]= Level8;
+        map[Shift+Lock+LevelThree+LevelFive]= Level7;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_SEMIALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level4;
+        preserve[Shift+Lock+LevelThree]= Lock;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[Lock+LevelFive]= Level6;
+        preserve[Lock+LevelFive]= Lock;
+        map[Shift+Lock+LevelFive]= Level6;
+        preserve[Shift+Lock+LevelFive]= Lock;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[Lock+LevelThree+LevelFive]= Level7;
+        preserve[Lock+LevelThree+LevelFive]= Lock;
+        map[Shift+Lock+LevelThree+LevelFive]= Level8;
+        preserve[Shift+Lock+LevelThree+LevelFive]= Lock;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "FOUR_LEVEL" {
+        modifiers= Shift+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Shift+Lock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_SEMIALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level4;
+        preserve[Shift+Lock+LevelThree]= Lock;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_MIXED_KEYPAD" {
+        modifiers= Shift+NumLock+LevelThree;
+        map[Shift+NumLock]= Level1;
+        map[NumLock]= Level2;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[NumLock+LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Shift+NumLock+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_X" {
+        modifiers= Shift+Control+Alt+LevelThree;
+        map[LevelThree]= Level2;
+        map[Shift+LevelThree]= Level3;
+        map[Control+Alt]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Alt Base";
+        level_name[Level3]= "Shift Alt";
+        level_name[Level4]= "Ctrl+Alt";
+    };
+    type "SEPARATE_CAPS_AND_SHIFT_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level4;
+        preserve[Lock]= Lock;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "AltGr Base";
+        level_name[Level4]= "Shift AltGr";
+    };
+    type "FOUR_LEVEL_PLUS_LOCK" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock]= Level5;
+        map[Shift+Lock]= Level2;
+        map[Lock+LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "Lock";
+    };
+    type "FOUR_LEVEL_KEYPAD" {
+        modifiers= Shift+NumLock+LevelThree;
+        map[Shift]= Level2;
+        map[NumLock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[NumLock+LevelThree]= Level4;
+        map[Shift+NumLock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Alt Number";
+    };
+};
+
+xkb_compatibility "complete" {
+
+    virtual_modifiers NumLock,Alt,LevelThree,LAlt,RAlt,RControl,LControl,ScrollLock,LevelFive,AltGr,Meta,Super,Hyper;
+
+    interpret.useModMapMods= AnyLevel;
+    interpret.repeat= False;
+    interpret.locking= False;
+    interpret ISO_Level2_Latch+Exactly(Shift) {
+        useModMapMods=level1;
+        action= LatchMods(modifiers=Shift,clearLocks,latchToLock);
+    };
+    interpret Shift_Lock+AnyOf(Shift+Lock) {
+        action= LockMods(modifiers=Shift);
+    };
+    interpret Num_Lock+AnyOf(all) {
+        virtualModifier= NumLock;
+        action= LockMods(modifiers=NumLock);
+    };
+    interpret ISO_Level3_Shift+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= SetMods(modifiers=LevelThree,clearLocks);
+    };
+    interpret ISO_Level3_Latch+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock);
+    };
+    interpret ISO_Level3_Lock+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= LockMods(modifiers=LevelThree);
+    };
+    interpret Alt_L+AnyOf(all) {
+        virtualModifier= Alt;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Alt_R+AnyOf(all) {
+        virtualModifier= Alt;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Meta_L+AnyOf(all) {
+        virtualModifier= Meta;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Meta_R+AnyOf(all) {
+        virtualModifier= Meta;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Super_L+AnyOf(all) {
+        virtualModifier= Super;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Super_R+AnyOf(all) {
+        virtualModifier= Super;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Hyper_L+AnyOf(all) {
+        virtualModifier= Hyper;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Hyper_R+AnyOf(all) {
+        virtualModifier= Hyper;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Scroll_Lock+AnyOf(all) {
+        virtualModifier= ScrollLock;
+        action= LockMods(modifiers=modMapMods);
+    };
+    interpret ISO_Level5_Shift+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= SetMods(modifiers=LevelFive,clearLocks);
+    };
+    interpret ISO_Level5_Latch+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock);
+    };
+    interpret ISO_Level5_Lock+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= LockMods(modifiers=LevelFive);
+    };
+    interpret Mode_switch+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= SetGroup(group=+1);
+    };
+    interpret ISO_Level3_Shift+AnyOfOrNone(all) {
+        action= SetMods(modifiers=LevelThree,clearLocks);
+    };
+    interpret ISO_Level3_Latch+AnyOfOrNone(all) {
+        action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock);
+    };
+    interpret ISO_Level3_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=LevelThree);
+    };
+    interpret ISO_Group_Latch+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LatchGroup(group=2);
+    };
+    interpret ISO_Next_Group+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LockGroup(group=+1);
+    };
+    interpret ISO_Prev_Group+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LockGroup(group=-1);
+    };
+    interpret ISO_First_Group+AnyOfOrNone(all) {
+        action= LockGroup(group=1);
+    };
+    interpret ISO_Last_Group+AnyOfOrNone(all) {
+        action= LockGroup(group=2);
+    };
+    interpret KP_1+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+1);
+    };
+    interpret KP_End+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+1);
+    };
+    interpret KP_2+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=+1);
+    };
+    interpret KP_Down+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=+1);
+    };
+    interpret KP_3+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+1);
+    };
+    interpret KP_Next+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+1);
+    };
+    interpret KP_4+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+0);
+    };
+    interpret KP_Left+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+0);
+    };
+    interpret KP_6+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+0);
+    };
+    interpret KP_Right+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+0);
+    };
+    interpret KP_7+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=-1);
+    };
+    interpret KP_Home+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=-1);
+    };
+    interpret KP_8+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=-1);
+    };
+    interpret KP_Up+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=-1);
+    };
+    interpret KP_9+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=-1);
+    };
+    interpret KP_Prior+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=-1);
+    };
+    interpret KP_5+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default);
+    };
+    interpret KP_Begin+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default);
+    };
+    interpret KP_F2+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=1);
+    };
+    interpret KP_Divide+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=1);
+    };
+    interpret KP_F3+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=2);
+    };
+    interpret KP_Multiply+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=2);
+    };
+    interpret KP_F4+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=3);
+    };
+    interpret KP_Subtract+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=3);
+    };
+    interpret KP_Separator+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default,count=2);
+    };
+    interpret KP_Add+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default,count=2);
+    };
+    interpret KP_0+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=lock);
+    };
+    interpret KP_Insert+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=lock);
+    };
+    interpret KP_Decimal+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=unlock);
+    };
+    interpret KP_Delete+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=unlock);
+    };
+    interpret F25+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=1);
+    };
+    interpret F26+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=2);
+    };
+    interpret F27+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=-1);
+    };
+    interpret F29+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=-1);
+    };
+    interpret F31+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default);
+    };
+    interpret F33+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+1);
+    };
+    interpret F35+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+1);
+    };
+    interpret Pointer_Button_Dflt+AnyOfOrNone(all) {
+        action= PtrBtn(button=default);
+    };
+    interpret Pointer_Button1+AnyOfOrNone(all) {
+        action= PtrBtn(button=1);
+    };
+    interpret Pointer_Button2+AnyOfOrNone(all) {
+        action= PtrBtn(button=2);
+    };
+    interpret Pointer_Button3+AnyOfOrNone(all) {
+        action= PtrBtn(button=3);
+    };
+    interpret Pointer_DblClick_Dflt+AnyOfOrNone(all) {
+        action= PtrBtn(button=default,count=2);
+    };
+    interpret Pointer_DblClick1+AnyOfOrNone(all) {
+        action= PtrBtn(button=1,count=2);
+    };
+    interpret Pointer_DblClick2+AnyOfOrNone(all) {
+        action= PtrBtn(button=2,count=2);
+    };
+    interpret Pointer_DblClick3+AnyOfOrNone(all) {
+        action= PtrBtn(button=3,count=2);
+    };
+    interpret Pointer_Drag_Dflt+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=default,affect=both);
+    };
+    interpret Pointer_Drag1+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=1,affect=both);
+    };
+    interpret Pointer_Drag2+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=2,affect=both);
+    };
+    interpret Pointer_Drag3+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=3,affect=both);
+    };
+    interpret Pointer_EnableKeys+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeys);
+    };
+    interpret Pointer_Accelerate+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeysAccel);
+    };
+    interpret Pointer_DfltBtnNext+AnyOfOrNone(all) {
+        action= SetPtrDflt(affect=button,button=+1);
+    };
+    interpret Pointer_DfltBtnPrev+AnyOfOrNone(all) {
+        action= SetPtrDflt(affect=button,button=-1);
+    };
+    interpret AccessX_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AccessXKeys);
+    };
+    interpret AccessX_Feedback_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AccessXFeedback);
+    };
+    interpret RepeatKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=RepeatKeys);
+    };
+    interpret SlowKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=SlowKeys);
+    };
+    interpret BounceKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=BounceKeys);
+    };
+    interpret StickyKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=StickyKeys);
+    };
+    interpret MouseKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeys);
+    };
+    interpret MouseKeys_Accel_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeysAccel);
+    };
+    interpret Overlay1_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=Overlay1);
+    };
+    interpret Overlay2_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=Overlay2);
+    };
+    interpret AudibleBell_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AudibleBell);
+    };
+    interpret Terminate_Server+AnyOfOrNone(all) {
+        action= Terminate();
+    };
+    interpret Alt_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Alt,clearLocks);
+    };
+    interpret Alt_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Alt,clearLocks);
+    };
+    interpret Meta_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Meta,clearLocks);
+    };
+    interpret Meta_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Meta,clearLocks);
+    };
+    interpret Super_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Super,clearLocks);
+    };
+    interpret Super_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Super,clearLocks);
+    };
+    interpret Hyper_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Hyper,clearLocks);
+    };
+    interpret Hyper_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Hyper,clearLocks);
+    };
+    interpret Shift_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Shift,clearLocks);
+    };
+    interpret XF86Switch_VT_1+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=1,!same);
+    };
+    interpret XF86Switch_VT_2+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=2,!same);
+    };
+    interpret XF86Switch_VT_3+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=3,!same);
+    };
+    interpret XF86Switch_VT_4+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=4,!same);
+    };
+    interpret XF86Switch_VT_5+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=5,!same);
+    };
+    interpret XF86Switch_VT_6+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=6,!same);
+    };
+    interpret XF86Switch_VT_7+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=7,!same);
+    };
+    interpret XF86Switch_VT_8+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=8,!same);
+    };
+    interpret XF86Switch_VT_9+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=9,!same);
+    };
+    interpret XF86Switch_VT_10+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=10,!same);
+    };
+    interpret XF86Switch_VT_11+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=11,!same);
+    };
+    interpret XF86Switch_VT_12+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=12,!same);
+    };
+    interpret XF86LogGrabInfo+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x50,data[1]=0x72,data[2]=0x47,data[3]=0x72,data[4]=0x62,data[5]=0x73,data[6]=0x00);
+    };
+    interpret XF86LogWindowTree+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x50,data[1]=0x72,data[2]=0x57,data[3]=0x69,data[4]=0x6e,data[5]=0x73,data[6]=0x00);
+    };
+    interpret XF86Next_VMode+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x2b,data[1]=0x56,data[2]=0x4d,data[3]=0x6f,data[4]=0x64,data[5]=0x65,data[6]=0x00);
+    };
+    interpret XF86Prev_VMode+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x2d,data[1]=0x56,data[2]=0x4d,data[3]=0x6f,data[4]=0x64,data[5]=0x65,data[6]=0x00);
+    };
+    interpret ISO_Level5_Shift+AnyOfOrNone(all) {
+        action= SetMods(modifiers=LevelFive,clearLocks);
+    };
+    interpret ISO_Level5_Latch+AnyOfOrNone(all) {
+        action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock);
+    };
+    interpret ISO_Level5_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=LevelFive);
+    };
+    interpret Caps_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=Lock);
+    };
+    interpret Any+Exactly(Lock) {
+        action= LockMods(modifiers=Lock);
+    };
+    interpret Any+AnyOf(all) {
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    group 2 = AltGr;
+    group 3 = AltGr;
+    group 4 = AltGr;
+    indicator "Caps Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= Lock;
+    };
+    indicator "Num Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= NumLock;
+    };
+    indicator "Scroll Lock" {
+        whichModState= locked;
+        modifiers= ScrollLock;
+    };
+    indicator "Shift Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= Shift;
+    };
+    indicator "Group 2" {
+        !allowExplicit;
+        groups= 0xfe;
+    };
+    indicator "Mouse Keys" {
+        indicatorDrivesKeyboard;
+        controls= mouseKeys;
+    };
+};
+
+xkb_symbols "pc+us+inet(evdev)" {
+
+    name[group1]="English (US)";
+
+    key  <ESC> {         [          Escape ] };
+    key <AE01> {         [               1,          exclam ] };
+    key <AE02> {         [               2,              at ] };
+    key <AE03> {         [               3,      numbersign ] };
+    key <AE04> {         [               4,          dollar ] };
+    key <AE05> {         [               5,         percent ] };
+    key <AE06> {         [               6,     asciicircum ] };
+    key <AE07> {         [               7,       ampersand ] };
+    key <AE08> {         [               8,        asterisk ] };
+    key <AE09> {         [               9,       parenleft ] };
+    key <AE10> {         [               0,      parenright ] };
+    key <AE11> {         [           minus,      underscore ] };
+    key <AE12> {         [           equal,            plus ] };
+    key <BKSP> {         [       BackSpace,       BackSpace ] };
+    key  <TAB> {         [             Tab,    ISO_Left_Tab ] };
+    key <AD01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               q,               Q ]
+    };
+    key <AD02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               w,               W ]
+    };
+    key <AD03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               e,               E ]
+    };
+    key <AD04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               r,               R ]
+    };
+    key <AD05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               t,               T ]
+    };
+    key <AD06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               y,               Y ]
+    };
+    key <AD07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               u,               U ]
+    };
+    key <AD08> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               i,               I ]
+    };
+    key <AD09> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               o,               O ]
+    };
+    key <AD10> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               p,               P ]
+    };
+    key <AD11> {         [     bracketleft,       braceleft ] };
+    key <AD12> {         [    bracketright,      braceright ] };
+    key <RTRN> {         [          Return ] };
+    key <LCTL> {         [       Control_L ] };
+    key <AC01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               a,               A ]
+    };
+    key <AC02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               s,               S ]
+    };
+    key <AC03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               d,               D ]
+    };
+    key <AC04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               f,               F ]
+    };
+    key <AC05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               g,               G ]
+    };
+    key <AC06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               h,               H ]
+    };
+    key <AC07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               j,               J ]
+    };
+    key <AC08> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               k,               K ]
+    };
+    key <AC09> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               l,               L ]
+    };
+    key <AC10> {         [       semicolon,           colon ] };
+    key <AC11> {         [      apostrophe,        quotedbl ] };
+    key <TLDE> {         [           grave,      asciitilde ] };
+    key <LFSH> {         [         Shift_L ] };
+    key <BKSL> {         [       backslash,             bar ] };
+    key <AB01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               z,               Z ]
+    };
+    key <AB02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               x,               X ]
+    };
+    key <AB03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               c,               C ]
+    };
+    key <AB04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               v,               V ]
+    };
+    key <AB05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               b,               B ]
+    };
+    key <AB06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               n,               N ]
+    };
+    key <AB07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               m,               M ]
+    };
+    key <AB08> {         [           comma,            less ] };
+    key <AB09> {         [          period,         greater ] };
+    key <AB10> {         [           slash,        question ] };
+    key <RTSH> {         [         Shift_R ] };
+    key <KPMU> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [     underscore,      grave,      KP_Multiply,      KP_Multiply,   XF86ClearGrab ]
+    };
+    key <LALT> {         [           Alt_L,          Meta_L ] };
+    key <SPCE> {         [           space ] };
+    key <CAPS> {         [       Caps_Lock ] };
+    key <FK01> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F1,              F1,              F1,              F1, XF86Switch_VT_1 ]
+    };
+    key <FK02> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F2,              F2,              F2,              F2, XF86Switch_VT_2 ]
+    };
+    key <FK03> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F3,              F3,              F3,              F3, XF86Switch_VT_3 ]
+    };
+    key <FK04> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F4,              F4,              F4,              F4, XF86Switch_VT_4 ]
+    };
+    key <FK05> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F5,              F5,              F5,              F5, XF86Switch_VT_5 ]
+    };
+    key <FK06> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F6,              F6,              F6,              F6, XF86Switch_VT_6 ]
+    };
+    key <FK07> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F7,              F7,              F7,              F7, XF86Switch_VT_7 ]
+    };
+    key <FK08> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F8,              F8,              F8,              F8, XF86Switch_VT_8 ]
+    };
+    key <FK09> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F9,              F9,              F9,              F9, XF86Switch_VT_9 ]
+    };
+    key <FK10> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F10,             F10,             F10,             F10, XF86Switch_VT_10 ]
+    };
+    key <NMLK> {         [        Num_Lock ] };
+    key <SCLK> {         [     Scroll_Lock ] };
+    key  <KP7> {         [         KP_Home,            KP_7 ] };
+    key  <KP8> {         [           KP_Up,            KP_8 ] };
+    key  <KP9> {         [        KP_Prior,            KP_9 ] };
+    key <KPSU> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [     KP_Subtract,     KP_Subtract,     KP_Subtract,     KP_Subtract,  XF86Prev_VMode ]
+    };
+    key  <KP4> {         [         KP_Left,            KP_4 ] };
+    key  <KP5> {         [        KP_Begin,            KP_5 ] };
+    key  <KP6> {         [        KP_Right,            KP_6 ] };
+    key <KPAD> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [          KP_Add,          KP_Add,          KP_Add,          KP_Add,  XF86Next_VMode ]
+    };
+    key  <KP1> {         [          KP_End,            KP_1 ] };
+    key  <KP2> {         [         KP_Down,            KP_2 ] };
+    key  <KP3> {         [         KP_Next,            KP_3 ] };
+    key  <KP0> {         [       KP_Insert,            KP_0 ] };
+    key <KPDL> {         [       KP_Delete,      KP_Decimal ] };
+    key <LVL3> {         [ ISO_Level3_Shift ] };
+    key <LSGT> {
+        type= "FOUR_LEVEL",
+        symbols[Group1]= [            less,         greater,             bar,       brokenbar ]
+    };
+    key <FK11> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F11,             F11,             F11,             F11, XF86Switch_VT_11 ]
+    };
+    key <FK12> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F12,             F12,             F12,             F12, XF86Switch_VT_12 ]
+    };
+    key <KATA> {         [        Katakana ] };
+    key <HIRA> {         [        Hiragana ] };
+    key <HENK> {         [     Henkan_Mode ] };
+    key <HKTG> {         [ Hiragana_Katakana ] };
+    key <MUHE> {         [        Muhenkan ] };
+    key <KPEN> {         [        KP_Enter ] };
+    key <RCTL> {         [       Control_R ] };
+    key <KPDV> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [       KP_Divide,       KP_Divide,       KP_Divide,       KP_Divide,      XF86Ungrab ]
+    };
+    key <PRSC> {
+        type= "PC_ALT_LEVEL2",
+        symbols[Group1]= [           Print,         Sys_Req ]
+    };
+    key <RALT> {
+        type= "TWO_LEVEL",
+        symbols[Group1]= [           Alt_R,          Meta_R ]
+    };
+    key <LNFD> {         [        Linefeed ] };
+    key <HOME> {         [            Home ] };
+    key   <UP> {         [              Up ] };
+    key <PGUP> {         [           Prior ] };
+    key <LEFT> {         [            Left ] };
+    key <RGHT> {         [           Right ] };
+    key  <END> {         [             End ] };
+    key <DOWN> {         [            Down ] };
+    key <PGDN> {         [            Next ] };
+    key  <INS> {         [          Insert ] };
+    key <DELE> {         [          Delete ] };
+    key <MUTE> {         [   XF86AudioMute ] };
+    key <VOL-> {         [ XF86AudioLowerVolume ] };
+    key <VOL+> {         [ XF86AudioRaiseVolume ] };
+    key <POWR> {         [    XF86PowerOff ] };
+    key <KPEQ> {         [        KP_Equal ] };
+    key <I126> {         [       plusminus ] };
+    key <PAUS> {
+        type= "PC_CONTROL_LEVEL2",
+        symbols[Group1]= [           Pause,           Break ]
+    };
+    key <I128> {         [     XF86LaunchA ] };
+    key <I129> {         [      KP_Decimal,      KP_Decimal ] };
+    key <HNGL> {         [          Hangul ] };
+    key <HJCV> {         [    Hangul_Hanja ] };
+    key <LWIN> {         [         Super_L ] };
+    key <RWIN> {         [         Super_R ] };
+    key <COMP> {         [            Menu ] };
+    key <STOP> {         [          Cancel ] };
+    key <AGAI> {         [            Redo ] };
+    key <PROP> {         [        SunProps ] };
+    key <UNDO> {         [            Undo ] };
+    key <FRNT> {         [        SunFront ] };
+    key <COPY> {         [        XF86Copy ] };
+    key <OPEN> {         [        XF86Open ] };
+    key <PAST> {         [       XF86Paste ] };
+    key <FIND> {         [            Find ] };
+    key  <CUT> {         [         XF86Cut ] };
+    key <HELP> {         [            Help ] };
+    key <I147> {         [      XF86MenuKB ] };
+    key <I148> {         [  XF86Calculator ] };
+    key <I150> {         [       XF86Sleep ] };
+    key <I151> {         [      XF86WakeUp ] };
+    key <I152> {         [    XF86Explorer ] };
+    key <I153> {         [        XF86Send ] };
+    key <I155> {         [        XF86Xfer ] };
+    key <I156> {         [     XF86Launch1 ] };
+    key <I157> {         [     XF86Launch2 ] };
+    key <I158> {         [         XF86WWW ] };
+    key <I159> {         [         XF86DOS ] };
+    key <I160> {         [ XF86ScreenSaver ] };
+    key <I161> {         [ XF86RotateWindows ] };
+    key <I162> {         [    XF86TaskPane ] };
+    key <I163> {         [        XF86Mail ] };
+    key <I164> {         [   XF86Favorites ] };
+    key <I165> {         [  XF86MyComputer ] };
+    key <I166> {         [        XF86Back ] };
+    key <I167> {         [     XF86Forward ] };
+    key <I169> {         [       XF86Eject ] };
+    key <I170> {         [       XF86Eject,       XF86Eject ] };
+    key <I171> {         [   XF86AudioNext ] };
+    key <I172> {         [   XF86AudioPlay,  XF86AudioPause ] };
+    key <I173> {         [   XF86AudioPrev ] };
+    key <I174> {         [   XF86AudioStop,       XF86Eject ] };
+    key <I175> {         [ XF86AudioRecord ] };
+    key <I176> {         [ XF86AudioRewind ] };
+    key <I177> {         [       XF86Phone ] };
+    key <I179> {         [       XF86Tools ] };
+    key <I180> {         [    XF86HomePage ] };
+    key <I181> {         [      XF86Reload ] };
+    key <I182> {         [       XF86Close ] };
+    key <I185> {         [    XF86ScrollUp ] };
+    key <I186> {         [  XF86ScrollDown ] };
+    key <I187> {         [       parenleft ] };
+    key <I188> {         [      parenright ] };
+    key <I189> {         [         XF86New ] };
+    key <I190> {         [            Redo ] };
+    key <FK13> {         [       XF86Tools ] };
+    key <FK14> {         [     XF86Launch5 ] };
+    key <FK15> {         [     XF86Launch6 ] };
+    key <FK16> {         [     XF86Launch7 ] };
+    key <FK17> {         [     XF86Launch8 ] };
+    key <FK18> {         [     XF86Launch9 ] };
+    key <FK20> {         [ XF86AudioMicMute ] };
+    key <FK21> {         [ XF86TouchpadToggle ] };
+    key <FK22> {         [  XF86TouchpadOn ] };
+    key <FK23> {         [ XF86TouchpadOff ] };
+    key <MDSW> {         [     Mode_switch ] };
+    key  <ALT> {         [        NoSymbol,           Alt_L ] };
+    key <META> {         [        NoSymbol,          Meta_L ] };
+    key <SUPR> {         [        NoSymbol,         Super_L ] };
+    key <HYPR> {         [        NoSymbol,         Hyper_L ] };
+    key <I208> {         [   XF86AudioPlay ] };
+    key <I209> {         [  XF86AudioPause ] };
+    key <I210> {         [     XF86Launch3 ] };
+    key <I211> {         [     XF86Launch4 ] };
+    key <I212> {         [     XF86LaunchB ] };
+    key <I213> {         [     XF86Suspend ] };
+    key <I214> {         [       XF86Close ] };
+    key <I215> {         [   XF86AudioPlay ] };
+    key <I216> {         [ XF86AudioForward ] };
+    key <I218> {         [           Print ] };
+    key <I220> {         [      XF86WebCam ] };
+    key <I223> {         [        XF86Mail ] };
+    key <I224> {         [   XF86Messenger ] };
+    key <I225> {         [      XF86Search ] };
+    key <I226> {         [          XF86Go ] };
+    key <I227> {         [     XF86Finance ] };
+    key <I228> {         [        XF86Game ] };
+    key <I229> {         [        XF86Shop ] };
+    key <I231> {         [          Cancel ] };
+    key <I232> {         [ XF86MonBrightnessDown ] };
+    key <I233> {         [ XF86MonBrightnessUp ] };
+    key <I234> {         [  XF86AudioMedia ] };
+    key <I235> {         [     XF86Display ] };
+    key <I236> {         [ XF86KbdLightOnOff ] };
+    key <I237> {         [ XF86KbdBrightnessDown ] };
+    key <I238> {         [ XF86KbdBrightnessUp ] };
+    key <I239> {         [        XF86Send ] };
+    key <I240> {         [       XF86Reply ] };
+    key <I241> {         [ XF86MailForward ] };
+    key <I242> {         [        XF86Save ] };
+    key <I243> {         [   XF86Documents ] };
+    key <I244> {         [     XF86Battery ] };
+    key <I245> {         [   XF86Bluetooth ] };
+    key <I246> {         [        XF86WLAN ] };
+    modifier_map Control { <LCTL> };
+    modifier_map Shift { <LFSH> };
+    modifier_map Shift { <RTSH> };
+    modifier_map Mod1 { <LALT> };
+    modifier_map Lock { <CAPS> };
+    modifier_map Mod2 { <NMLK> };
+    modifier_map Mod5 { <LVL3> };
+    modifier_map Control { <RCTL> };
+    modifier_map Mod1 { <RALT> };
+    modifier_map Mod4 { <LWIN> };
+    modifier_map Mod4 { <RWIN> };
+    modifier_map Mod5 { <MDSW> };
+    modifier_map Mod1 { <META> };
+    modifier_map Mod4 { <SUPR> };
+    modifier_map Mod4 { <HYPR> };
+};
+
+xkb_geometry "pc(pc105)" {
+
+    width=       470;
+    height=      180;
+
+    alias <AC00> = <CAPS>;
+    alias <AA00> = <LCTL>;
+
+    baseColor=   "white";
+    labelColor=  "black";
+    xfont=       "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+    description= "Generic 105";
+
+    shape "NORM" {
+        corner= 1,
+        { [  18,  18 ] },
+        { [   2,   1 ], [  16,  16 ] }
+    };
+    shape "BKSP" {
+        corner= 1,
+        { [  38,  18 ] },
+        { [   2,   1 ], [  36,  16 ] }
+    };
+    shape "TABK" {
+        corner= 1,
+        { [  28,  18 ] },
+        { [   2,   1 ], [  26,  16 ] }
+    };
+    shape "BKSL" {
+        corner= 1,
+        { [  28,  18 ] },
+        { [   2,   1 ], [  26,  16 ] }
+    };
+    shape "RTRN" {
+        corner= 1,
+        { [   0,   0 ], [  28,   0 ], [  28,  37 ], [   5,  37 ],
+          [   5,  18 ], [   0,  18 ] },
+        { [   2,   1 ], [  26,   1 ], [  26,  35 ], [   7,  35 ],
+          [   7,  16 ], [   2,  16 ] },
+        approx= { [   5,   0 ], [  28,  37 ] }
+    };
+    shape "CAPS" {
+        corner= 1,
+        { [  33,  18 ] },
+        { [   2,   1 ], [  31,  16 ] }
+    };
+    shape "LFSH" {
+        corner= 1,
+        { [  25,  18 ] },
+        { [   2,   1 ], [  23,  16 ] }
+    };
+    shape "RTSH" {
+        corner= 1,
+        { [  50,  18 ] },
+        { [   2,   1 ], [  48,  16 ] }
+    };
+    shape "MODK" {
+        corner= 1,
+        { [  27,  18 ] },
+        { [   2,   1 ], [  25,  16 ] }
+    };
+    shape "SMOD" {
+        corner= 1,
+        { [  23,  18 ] },
+        { [   2,   1 ], [  21,  16 ] }
+    };
+    shape "SPCE" {
+        corner= 1,
+        { [ 113,  18 ] },
+        { [   2,   1 ], [ 111,  16 ] }
+    };
+    shape "KP0" {
+        corner= 1,
+        { [  37,  18 ] },
+        { [   2,   1 ], [  35,  16 ] }
+    };
+    shape "KPAD" {
+        corner= 1,
+        { [  18,  37 ] },
+        { [   2,   1 ], [  16,  35 ] }
+    };
+    shape "LEDS" { { [  75,  20 ] } };
+    shape "LED" { { [   5,   1 ] } };
+    section "Function" {
+        key.color= "grey20";
+        priority=  7;
+        top=       22;
+        left=      19;
+        width=     351;
+        height=    19;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                {  <ESC>, "NORM",   1 },
+                { <FK01>, "NORM",  20, color="white" },
+                { <FK02>, "NORM",   1, color="white" },
+                { <FK03>, "NORM",   1, color="white" },
+                { <FK04>, "NORM",   1, color="white" },
+                { <FK05>, "NORM",  11, color="white" },
+                { <FK06>, "NORM",   1, color="white" },
+                { <FK07>, "NORM",   1, color="white" },
+                { <FK08>, "NORM",   1, color="white" },
+                { <FK09>, "NORM",  11, color="white" },
+                { <FK10>, "NORM",   1, color="white" },
+                { <FK11>, "NORM",   1, color="white" },
+                { <FK12>, "NORM",   1, color="white" },
+                { <PRSC>, "NORM",   8, color="white" },
+                { <SCLK>, "NORM",   1, color="white" },
+                { <PAUS>, "NORM",   1, color="white" }
+            };
+        };
+    }; // End of "Function" section
+
+    section "Alpha" {
+        key.color= "white";
+        priority=  8;
+        top=       61;
+        left=      19;
+        width=     287;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                { <TLDE>, "NORM",   1 }, { <AE01>, "NORM",   1 },
+                { <AE02>, "NORM",   1 }, { <AE03>, "NORM",   1 },
+                { <AE04>, "NORM",   1 }, { <AE05>, "NORM",   1 },
+                { <AE06>, "NORM",   1 }, { <AE07>, "NORM",   1 },
+                { <AE08>, "NORM",   1 }, { <AE09>, "NORM",   1 },
+                { <AE10>, "NORM",   1 }, { <AE11>, "NORM",   1 },
+                { <AE12>, "NORM",   1 },
+                { <BKSP>, "BKSP",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                {  <TAB>, "TABK",   1, color="grey20" },
+                { <AD01>, "NORM",   1 }, { <AD02>, "NORM",   1 },
+                { <AD03>, "NORM",   1 }, { <AD04>, "NORM",   1 },
+                { <AD05>, "NORM",   1 }, { <AD06>, "NORM",   1 },
+                { <AD07>, "NORM",   1 }, { <AD08>, "NORM",   1 },
+                { <AD09>, "NORM",   1 }, { <AD10>, "NORM",   1 },
+                { <AD11>, "NORM",   1 }, { <AD12>, "NORM",   1 },
+                { <RTRN>, "RTRN",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  39;
+            left= 1;
+            keys {
+                { <CAPS>, "CAPS",   1, color="grey20" },
+                { <AC01>, "NORM",   1 }, { <AC02>, "NORM",   1 },
+                { <AC03>, "NORM",   1 }, { <AC04>, "NORM",   1 },
+                { <AC05>, "NORM",   1 }, { <AC06>, "NORM",   1 },
+                { <AC07>, "NORM",   1 }, { <AC08>, "NORM",   1 },
+                { <AC09>, "NORM",   1 }, { <AC10>, "NORM",   1 },
+                { <AC11>, "NORM",   1 }, { <BKSL>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  58;
+            left= 1;
+            keys {
+                { <LFSH>, "LFSH",   1, color="grey20" },
+                { <LSGT>, "NORM",   1 }, { <AB01>, "NORM",   1 },
+                { <AB02>, "NORM",   1 }, { <AB03>, "NORM",   1 },
+                { <AB04>, "NORM",   1 }, { <AB05>, "NORM",   1 },
+                { <AB06>, "NORM",   1 }, { <AB07>, "NORM",   1 },
+                { <AB08>, "NORM",   1 }, { <AB09>, "NORM",   1 },
+                { <AB10>, "NORM",   1 },
+                { <RTSH>, "RTSH",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                { <LCTL>, "MODK",   1, color="grey20" },
+                { <LWIN>, "SMOD",   1, color="grey20" },
+                { <LALT>, "SMOD",   1, color="grey20" },
+                { <SPCE>, "SPCE",   1 },
+                { <RALT>, "SMOD",   1, color="grey20" },
+                { <RWIN>, "SMOD",   1, color="grey20" },
+                { <MENU>, "SMOD",   1, color="grey20" },
+                { <RCTL>, "SMOD",   1, color="grey20" }
+            };
+        };
+    }; // End of "Alpha" section
+
+    section "Editing" {
+        key.color= "grey20";
+        priority=  9;
+        top=       61;
+        left=      312;
+        width=     58;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                {  <INS>, "NORM",   1 }, { <HOME>, "NORM",   1 },
+                { <PGUP>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                { <DELE>, "NORM",   1 }, {  <END>, "NORM",   1 },
+                { <PGDN>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  58;
+            left= 20;
+            keys {
+                {   <UP>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                { <LEFT>, "NORM",   1 }, { <DOWN>, "NORM",   1 },
+                { <RGHT>, "NORM",   1 }
+            };
+        };
+    }; // End of "Editing" section
+
+    section "Keypad" {
+        key.color= "grey20";
+        priority=  10;
+        top=       61;
+        left=      376;
+        width=     77;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                { <NMLK>, "NORM",   1 }, { <KPDV>, "NORM",   1 },
+                { <KPMU>, "NORM",   1 }, { <KPSU>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                {  <KP7>, "NORM",   1, color="white" },
+                {  <KP8>, "NORM",   1, color="white" },
+                {  <KP9>, "NORM",   1, color="white" },
+                { <KPAD>, "KPAD",   1 }
+            };
+        };
+        row {
+            top=  39;
+            left= 1;
+            keys {
+                {  <KP4>, "NORM",   1, color="white" },
+                {  <KP5>, "NORM",   1, color="white" },
+                {  <KP6>, "NORM",   1, color="white" }
+            };
+        };
+        row {
+            top=  58;
+            left= 1;
+            keys {
+                {  <KP1>, "NORM",   1, color="white" },
+                {  <KP2>, "NORM",   1, color="white" },
+                {  <KP3>, "NORM",   1, color="white" },
+                { <KPEN>, "KPAD",   1 }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                {  <KP0>, "KP0",   1, color="white" },
+                { <KPDL>, "NORM",   1, color="white" }
+            };
+        };
+    }; // End of "Keypad" section
+
+    solid "LedPanel" {
+        top=      22;
+        left=     377;
+        priority= 0;
+        color= "grey10";
+        shape= "LEDS";
+    };
+    indicator "Num Lock" {
+        top=      37;
+        left=     382;
+        priority= 1;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    indicator "Caps Lock" {
+        top=      37;
+        left=     407;
+        priority= 2;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    indicator "Scroll Lock" {
+        top=      37;
+        left=     433;
+        priority= 3;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    text "NumLockLabel" {
+        top=      25;
+        left=     378;
+        priority= 4;
+        width=  19.8;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Num\nLock";
+    };
+    text "CapsLockLabel" {
+        top=      25;
+        left=     403;
+        priority= 5;
+        width=  26.4;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Caps\nLock";
+    };
+    text "ScrollLockLabel" {
+        top=      25;
+        left=     428;
+        priority= 6;
+        width=  39.6;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Scroll\nLock";
+    };
+};
+
+};
diff --git a/model01.xkm b/model01.xkm
new file mode 100644 (file)
index 0000000..f8ce959
Binary files /dev/null and b/model01.xkm differ
diff --git a/path_add-function b/path_add-function
new file mode 100644 (file)
index 0000000..f03451a
--- /dev/null
@@ -0,0 +1,80 @@
+#!/bin/bash
+# Copyright (C) 2016 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+# avoiding bashisms so it can be used in edge cases where I don't have bash,
+# however, I'm not super confident that I've avoided them all
+#
+path_add() {
+    local help="usage: path_add [options] PATH
+--help:     print this
+--end:      adds to end of path, which will give it lowest priority
+--force:    add to path even if directory does not exist"
+    local found x y z ifexists end loop newpath
+    force=false
+    end=false
+    loop=true
+    # portable substring matching is ugly http://mywiki.wooledge.org/BashFAQ/041
+    while $loop; do
+        case $1 in
+            --*)
+                if [ "$1" = --end ]; then
+                    end=true
+                elif [ "$1" = --force ]; then
+                    force=true
+                elif [ "$1" = --help ]; then
+                    echo "$help"
+                    return
+                fi
+                shift
+                ;;
+            *)
+                loop=false
+                ;;
+        esac
+    done
+    IFS=:
+    # build up the path without the components we want to add
+    for y in $PATH; do
+        for x in "$@"; do
+            if [ "$x" = "$y" ]; then
+                found=true
+            else
+                found=false
+            fi
+        done
+        if ! $found; then
+            if [ ! "$newpath" ]; then
+                newpath="$y"
+            else
+                newpath="$newpath:$y"
+            fi
+        fi
+    done
+
+    unset IFS
+    PATH="$newpath"
+    for x in "$@"; do
+        if $force || [ -d "$x" ]; then
+            if [ ! "$PATH" ]; then
+                PATH="$x"
+            elif $end; then
+                PATH="$PATH:$x"
+            else
+                PATH="$x:$PATH"
+            fi
+        fi
+    done
+}
diff --git a/playlists/music/10.xsp b/playlists/music/10.xsp
new file mode 100644 (file)
index 0000000..af815a4
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<smartplaylist type="songs">
+    <name></name>
+    <match>all</match>
+    <rule field="genre" operator="doesnotcontain">
+        <value>Avant-Garde</value>
+        <value>Noise</value>
+        <value>Skit</value>
+        <value>Spoken Word</value>
+    </rule>
+    <rule field="userrating" operator="greaterthan">
+        <value>9</value>
+    </rule>
+</smartplaylist>
diff --git a/playlists/music/8.xsp b/playlists/music/8.xsp
new file mode 100644 (file)
index 0000000..51722aa
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<smartplaylist type="songs">
+    <name>8</name>
+    <match>all</match>
+    <rule field="userrating" operator="greaterthan">
+        <value>7</value>
+    </rule>
+    <rule field="genre" operator="isnot">
+        <value>Avant-Garde</value>
+        <value>Noise</value>
+        <value>Skit</value>
+        <value>Spoken Word</value>
+    </rule>
+</smartplaylist>
diff --git a/playlists/music/sad.xsp b/playlists/music/sad.xsp
new file mode 100644 (file)
index 0000000..be00384
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<smartplaylist type="songs">
+    <name>sad</name>
+    <match>all</match>
+    <rule field="genre" operator="is">
+        <value>sad</value>
+    </rule>
+    <rule field="userrating" operator="greaterthan">
+        <value>7</value>
+    </rule>
+</smartplaylist>
diff --git a/playlists/music/sad5.xsp b/playlists/music/sad5.xsp
new file mode 100644 (file)
index 0000000..7b86c5e
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<smartplaylist type="songs">
+    <name>sad5</name>
+    <match>all</match>
+    <rule field="genre" operator="contains">
+        <value>sad</value>
+    </rule>
+    <rule field="userrating" operator="greaterthan">
+        <value>9</value>
+    </rule>
+</smartplaylist>
diff --git a/playlists/music/sy most.xsp b/playlists/music/sy most.xsp
new file mode 100644 (file)
index 0000000..a5a971c
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+<smartplaylist type="songs">
+    <name>sy most</name>
+    <match>all</match>
+    <rule field="artist" operator="contains">
+        <value>Sonic Youth</value>
+    </rule>
+    <rule field="userrating" operator="greaterthan">
+        <value>5</value>
+    </rule>
+</smartplaylist>
diff --git a/subdir_files/.bazaar/authentication.conf b/subdir_files/.bazaar/authentication.conf
new file mode 100644 (file)
index 0000000..3b8d3cb
--- /dev/null
@@ -0,0 +1,4 @@
+[Launchpad]
+host = .launchpad.net
+scheme = ssh
+user = iank
diff --git a/subdir_files/.bazaar/bazaar.conf b/subdir_files/.bazaar/bazaar.conf
new file mode 100644 (file)
index 0000000..1ca33b4
--- /dev/null
@@ -0,0 +1,2 @@
+[DEFAULT]
+launchpad_username = iank
diff --git a/subdir_files/.config/beets/config.yaml b/subdir_files/.config/beets/config.yaml
new file mode 100644 (file)
index 0000000..3452831
--- /dev/null
@@ -0,0 +1,20 @@
+library: /a/bin/data/musiclibrary.blb
+directory: /i/music
+import:
+  log: /a/dt/beetlog.log
+  move: yes
+  quiet_fallback: skip
+
+# be a little more liberal for strong matches
+match:
+  strong_rec_thresh: 0.07
+plugins: discogs duplicates web info
+# all the usefull plugins I've used:
+#plugins: copyartifacts chroma discogs duplicates web
+
+#duplicates:
+# duplicates can be found based on tagged keys or a checksum program.
+# the sugggested checksum program takes hours :(
+# the default is tags, but it is broken.
+# the default ffmpeg command, using the libav equivalent  based on debian recommendations
+    #checksum: avconv -i {file} -f crc -
diff --git a/subdir_files/.config/i3/config b/subdir_files/.config/i3/config
new file mode 100644 (file)
index 0000000..ba4dc68
--- /dev/null
@@ -0,0 +1,77 @@
+# This file has been auto-generated by i3-config-wizard(1).
+# It will not be overwritten, so edit it as you like.
+#
+# Should you change your keyboard layout some time, delete
+# this file and re-run i3-config-wizard(1).
+#
+
+# i3 config file (v4)
+#
+# Please see http://i3wm.org/docs/userguide.html for a complete reference!
+
+set $mod Mod4
+
+
+# exit i3 (logs you out of your X session)
+bindsym $mod+q exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'"
+
+bindsym $mod+2 exec "pavucontrol"
+bindsym $mod+3 exec "firefox"
+bindsym $mod+4 exec "input-setup"
+bindsym $mod+w exec "/a/bin/redshift.sh 2"
+bindsym $mod+e exec "/a/bin/redshift.sh 1"
+bindsym $mod+r exec "/a/bin/redshift.sh 0"
+# todo, add mod+t for cycle layout
+bindsym $mod+t layout toggle split
+# move focused window
+bindsym $mod+Delete move left
+bindsym $mod+parenright move right
+# todo, some other binds in here and beyond.
+# reload the configuration file. change to restart if needed.
+bindsym $mod+p restart
+# start a terminal
+bindsym $mod+k exec i3-sensible-terminal
+# start dmenu (a program launcher)
+bindsym $mod+l exec dmenu_run
+# kill focused window
+bindsym $mod+c kill
+
+# Use Mouse+$mod to drag floating windows to their wanted position
+floating_modifier $mod
+
+bindsym F8 focus down
+
+bindsym $mod+b layout stacking
+
+# todo, other workspaces
+# switch to workspace
+bindsym $mod+f workspace 1
+bindsym $mod+d workspace 2
+bindsym $mod+s workspace 3
+bindsym $mod+a workspace 4
+bindsym $mod+7 workspace 5
+bindsym $mod+0 workspace 6
+
+# move focused container to workspace
+bindsym $mod+Shift+f move container to workspace 1
+bindsym $mod+Shift+d move container to workspace 2
+bindsym $mod+Shift+s move container to workspace 3
+bindsym $mod+Shift+a move container to workspace 4
+bindsym $mod+Shift+7 move container to workspace 5
+bindsym $mod+Shift+0 move container to workspace 6
+
+
+# Font for window titles. Will also be used by the bar unless a different font
+# is used in the bar {} block below.
+font pango:monospace 8
+
+# This font is widely installed, provides lots of unicode glyphs, right-to-left
+# text rendering and scalability on retina/hidpi displays (thanks to pango).
+#font pango:DejaVu Sans Mono 8
+
+# Before i3 v4.8, we used to recommend this one as the default:
+# font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+# The font above is very space-efficient, that is, it looks good, sharp and
+# clear in small sizes. However, its unicode glyph coverage is limited, the old
+# X core fonts rendering does not support right-to-left and this being a bitmap
+# font, it doesn’t scale on retina/hidpi displays.
diff --git a/subdir_files/.config/menus/mate-applications.menu b/subdir_files/.config/menus/mate-applications.menu
new file mode 100644 (file)
index 0000000..11836ca
--- /dev/null
@@ -0,0 +1,14 @@
+<!DOCTYPE Menu
+  PUBLIC '-//freedesktop//DTD Menu 1.0//EN'
+  'http://standards.freedesktop.org/menu-spec/menu-1.0.dtd'>
+<!-- ian: this links to files in ~/.local/share/applications -->
+<Menu>
+       <Name>Applications</Name>
+       <MergeFile type="parent">/etc/xdg/menus/mate-applications.menu</MergeFile>
+        <Include>
+               <Filename>firefox.desktop</Filename>
+       </Include>
+       <Include>
+               <Filename>chromium.desktop</Filename>
+       </Include>
+</Menu>
diff --git a/subdir_files/.config/mpv/mpv.conf b/subdir_files/.config/mpv/mpv.conf
new file mode 100644 (file)
index 0000000..923bb30
--- /dev/null
@@ -0,0 +1,13 @@
+#loop=inf
+
+
+#loop-file=inf
+loop-file=no
+shuffle
+#volume=50
+save-position-on-quit
+
+# use --profile d
+[d]
+loop-file=inf
+shuffle
diff --git a/subdir_files/.config/sakura/sakura.conf b/subdir_files/.config/sakura/sakura.conf
new file mode 100644 (file)
index 0000000..266fd06
--- /dev/null
@@ -0,0 +1,64 @@
+[sakura]
+scrollbar=true
+scroll_lines=10000000
+disable_numbered_tabswitch=true
+visible_bell=No
+audible_bell=No
+less_questions=true
+colorset1_back=rgb(33,37,39
+stop_tab_cycling_at_end_tabs=No
+colorset1_fore=rgb(192,192,192)
+colorset1_curs=rgb(255,255,255)
+colorset1_key=F1
+colorset2_fore=rgb(192,192,192)
+colorset2_back=rgb(0,0,0)
+colorset2_curs=rgb(255,255,255)
+colorset2_key=F2
+colorset3_fore=rgb(192,192,192)
+colorset3_back=rgb(0,0,0)
+colorset3_curs=rgb(255,255,255)
+colorset3_key=F3
+colorset4_fore=rgb(192,192,192)
+colorset4_back=rgb(0,0,0)
+colorset4_curs=rgb(255,255,255)
+colorset4_key=F4
+colorset5_fore=rgb(192,192,192)
+colorset5_back=rgb(0,0,0)
+colorset5_curs=rgb(255,255,255)
+colorset5_key=F5
+colorset6_fore=rgb(192,192,192)
+colorset6_back=rgb(0,0,0)
+colorset6_curs=rgb(255,255,255)
+colorset6_key=F6
+last_colorset=1
+font=Ubuntu Mono,monospace 17
+show_always_first_tab=No
+closebutton=true
+tabs_on_bottom=false
+urgent_bell=Yes
+blinking_cursor=No
+allow_bold=Yes
+cursor_type=VTE_CURSOR_SHAPE_BLOCK
+palette=solarized_dark
+add_tab_accelerator=5
+del_tab_accelerator=5
+switch_tab_accelerator=8
+move_tab_accelerator=5
+copy_accelerator=5
+scrollbar_accelerator=5
+open_url_accelerator=5
+font_size_accelerator=4
+set_tab_name_accelerator=5
+add_tab_key=T
+del_tab_key=W
+prev_tab_key=Left
+next_tab_key=Right
+copy_key=C
+paste_key=V
+scrollbar_key=S
+set_tab_name_key=N
+increase_font_size_key=plus
+decrease_font_size_key=minus
+fullscreen_key=F11
+set_colorset_accelerator=5
+icon_file=terminal-tango.svg
diff --git a/subdir_files/.gnupg/gpg.conf b/subdir_files/.gnupg/gpg.conf
new file mode 100644 (file)
index 0000000..f9da1b5
--- /dev/null
@@ -0,0 +1,28 @@
+# Note: initial files in this directory were created by running
+# gpg -k
+
+# for aur, automatically dl & add gpg keys.
+keyserver-options auto-key-retrieve
+# start gpg agent on login
+use-agent
+
+# standard short key ids are easy to collide
+# https://security.stackexchange.com/questions/84280/short-openpgp-key-ids-are-insecure-how-to-configure-gnupg-to-use-long-key-ids-i
+# https://evil32.com/
+# another option is 0xshort
+keyid-format 0xlong
+
+# iank@fsf.org
+default-key B125F60B7B287FF6A2B7DF8F170AF0E2954295DF
+
+# note, i did this so that gpg-agent would not hold open the .gnupg dir.
+# so I could unmount the filesystem which holds the .gnupg dir while
+# running gpg-agent.
+# also, symlinking things like keys in the .gnupg dir is a recipe
+# for trouble
+# https://askubuntu.com/questions/777900/how-to-configure-gnupgs-s-gpg-agent-socket-location
+
+# rm -f ~/.gnupg/S.gpg-agent
+# echo -e "%Assuan%\nsocket=${HOME}/gpg-agent-socket/s" > ~/.gnupg/S.gpg-agent
+# # this is also in my conflink scrpt:
+# install -d -m700 ~/gpg-agent-socket
diff --git a/subdir_files/.local/share/applications/chromium.desktop b/subdir_files/.local/share/applications/chromium.desktop
new file mode 100644 (file)
index 0000000..260cb7d
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env xdg-open
+# ian: note, this is linked to from ~/.config/menus/mate-applications.menu
+[Desktop Entry]
+Version=1.0
+Type=Application
+Terminal=false
+Icon[en]=mate-panel-launcher
+Name[en]=chromium
+Exec=schroot -c stretch -- chromium --enable-remote-extensions
+Comment[en]=chromium
+Name=chromium
+Comment=chromium
+Icon=mate-panel-launcher
diff --git a/subdir_files/.local/share/applications/firefox.desktop b/subdir_files/.local/share/applications/firefox.desktop
new file mode 100644 (file)
index 0000000..3bd7324
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env xdg-open
+# ian: note, this is linked to from ~/.config/menus/mate-applications.menu
+[Desktop Entry]
+Version=1.0
+Type=Application
+Terminal=false
+Icon[en]=mate-panel-launcher
+Name[en]=firefox
+Exec=schroot -c stretch firefox
+Comment[en]=firefox
+Name=firefox
+Comment=firefox
+Icon=mate-panel-launcher
diff --git a/subdir_files/.local/share/konsole/README.ian b/subdir_files/.local/share/konsole/README.ian
new file mode 100644 (file)
index 0000000..dcde179
--- /dev/null
@@ -0,0 +1,3 @@
+only thing I added was
+key Backspace+Ctrl : "\x17"
+I did it through the ui. it makes ctrl-backspace delete the prev word
\ No newline at end of file
diff --git a/subdir_files/.local/share/konsole/default.keytab b/subdir_files/.local/share/konsole/default.keytab
new file mode 100644 (file)
index 0000000..0d12e6b
--- /dev/null
@@ -0,0 +1,96 @@
+keyboard "Default (XFree 4)"
+key Space+Ctrl : "\x00"
+key Backtab+Ansi : "\E[Z"
+key Backtab-Ansi : "\t"
+key Backspace+Ctrl : "\x17"
+key Backspace : "\x7f"
+key Backspace+Ctrl : "\x17"
+key Esc : "\E"
+key Tab-Shift : "\t"
+key Tab+Shift+Ansi : "\E[Z"
+key Tab+Shift-Ansi : "\t"
+key Ins+KeyPad : "\E[2~"
+key Ins-AnyModifier : "\E[2~"
+key Ins+AnyModifier : "\E[2;*~"
+key Del+KeyPad : "\E[3~"
+key Del-AnyModifier : "\E[3~"
+key Del+AnyModifier : "\E[3;*~"
+key Return-Shift-NewLine : "\r"
+key Return-Shift+NewLine : "\r\n"
+key Return+Shift : "\EOM"
+key Enter+NewLine : "\r\n"
+key Enter-NewLine : "\r"
+key Left-Shift-Ansi : "\ED"
+key Left-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOD"
+key Left-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[D"
+key Left-Shift+Ansi+AnyModifier : "\E[1;*D"
+key Left+Shift+AppScreen : "\E[1;*D"
+key Left-Shift+KeyPad+Ansi+AppCursorKeys : "\EOD"
+key Left-Shift+KeyPad+Ansi-AppCursorKeys : "\E[D"
+key Up-Shift-Ansi : "\EA"
+key Up-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOA"
+key Up-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[A"
+key Up-Shift+Ansi+AnyModifier : "\E[1;*A"
+key Up+Shift+AppScreen : "\E[1;*A"
+key Up-Shift+KeyPad+Ansi+AppCursorKeys : "\EOA"
+key Up-Shift+KeyPad+Ansi-AppCursorKeys : "\E[A"
+key Up+Shift-AppScreen : ScrollLineUp
+key Home+KeyPad+AppCursorKeys : "\EOH"
+key Home+KeyPad-AppCursorKeys : "\E[H"
+key Home-AppCursorKeys-AnyModifier : "\E[H"
+key Home+AppCursorKeys-AnyModifier : "\EOH"
+key Home+AnyModifier : "\E[1;*H"
+key Home+Shift-AppScreen : ScrollUpToTop
+key End+KeyPad+AppCursorKeys : "\EOF"
+key End+KeyPad-AppCursorKeys : "\E[F"
+key End-AppCursorKeys-AnyModifier : "\E[F"
+key End+AppCursorKeys-AnyModifier : "\EOF"
+key End+AnyModifier : "\E[1;*F"
+key End+Shift-AppScreen : ScrollDownToBottom
+key PgUp-Shift+KeyPad : "\E[5~"
+key PgUp-Shift-AnyModifier : "\E[5~"
+key PgUp-Shift+AnyModifier : "\E[5;*~"
+key PgUp+Shift-AppScreen : ScrollPageUp
+key PgDown-Shift+KeyPad : "\E[6~"
+key PgDown-Shift-AnyModifier : "\E[6~"
+key PgDown-Shift+AnyModifier : "\E[6;*~"
+key PgDown+Shift-AppScreen : ScrollPageDown
+key Right-Shift-Ansi : "\EC"
+key Right-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOC"
+key Right-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[C"
+key Right-Shift+Ansi+AnyModifier : "\E[1;*C"
+key Right+Shift+AppScreen : "\E[1;*C"
+key Right-Shift+KeyPad+Ansi+AppCursorKeys : "\EOC"
+key Right-Shift+KeyPad+Ansi-AppCursorKeys : "\E[C"
+key Down-Shift-Ansi : "\EB"
+key Down-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOB"
+key Down-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[B"
+key Down-Shift+Ansi+AnyModifier : "\E[1;*B"
+key Down+Shift+AppScreen : "\E[1;*B"
+key Down-Shift+KeyPad+Ansi+AppCursorKeys : "\EOB"
+key Down-Shift+KeyPad+Ansi-AppCursorKeys : "\E[B"
+key Down+Shift-AppScreen : ScrollLineDown
+key F11-AnyModifier : "\E[23~"
+key F11+AnyModifier : "\E[23;*~"
+key F12-AnyModifier : "\E[24~"
+key F12+AnyModifier : "\E[24;*~"
+key F9-AnyModifier : "\E[20~"
+key F9+AnyModifier : "\E[20;*~"
+key F10-AnyModifier : "\E[21~"
+key F10+AnyModifier : "\E[21;*~"
+key F3-AnyModifier : "\EOR"
+key F3+AnyModifier : "\EO*R"
+key F4-AnyModifier : "\EOS"
+key F4+AnyModifier : "\EO*S"
+key F1-AnyModifier : "\EOP"
+key F1+AnyModifier : "\EO*P"
+key F2-AnyModifier : "\EOQ"
+key F2+AnyModifier : "\EO*Q"
+key F7-AnyModifier : "\E[18~"
+key F7+AnyModifier : "\E[18;*~"
+key F8-AnyModifier : "\E[19~"
+key F8+AnyModifier : "\E[19;*~"
+key F5-AnyModifier : "\E[15~"
+key F5+AnyModifier : "\E[15;*~"
+key F6-AnyModifier : "\E[17~"
+key F6+AnyModifier : "\E[17;*~"
diff --git a/subdir_files/.local/share/konsole/profileian.profile b/subdir_files/.local/share/konsole/profileian.profile
new file mode 100644 (file)
index 0000000..d000a1e
--- /dev/null
@@ -0,0 +1,12 @@
+[Appearance]
+ColorScheme=Linux
+Font=Ubuntu Mono,14,-1,5,50,0,0,0,0,0
+
+[General]
+LocalTabTitleFormat=%w
+Name=profileian
+Parent=FALLBACK/
+RemoteTabTitleFormat=%w
+
+[Keyboard]
+KeyBindings=default
diff --git a/subdir_files/.local/share/kxmlgui5/konsole/README.ian b/subdir_files/.local/share/kxmlgui5/konsole/README.ian
new file mode 100644 (file)
index 0000000..f0271e0
--- /dev/null
@@ -0,0 +1 @@
+sessionui.rc is the result of starting konsole with no profile, and setting one keybind: open file manager with ctrl-shift-2.
\ No newline at end of file
diff --git a/subdir_files/.local/share/kxmlgui5/konsole/sessionui.rc b/subdir_files/.local/share/kxmlgui5/konsole/sessionui.rc
new file mode 100644 (file)
index 0000000..62c9e1a
--- /dev/null
@@ -0,0 +1,63 @@
+<!DOCTYPE kpartgui>
+<kpartgui version="24" name="session">
+ <MenuBar>
+  <Menu name="file">
+   <Action group="session-operations" name="file_save_as"/>
+   <Separator group="session-operations"/>
+   <Action group="session-operations" name="file_print"/>
+   <Separator group="session-operations"/>
+   <Action group="session-operations" name="open-browser"/>
+   <Action group="session-tab-operations" name="close-session"/>
+  </Menu>
+  <Menu name="edit">
+   <Action group="session-edit-operations" name="edit_copy"/>
+   <Action group="session-edit-operations" name="edit_paste"/>
+   <Separator group="session-edit-operations"/>
+   <Action group="session-edit-operations" name="select-all"/>
+   <Separator group="session-edit-operations"/>
+   <Action group="session-edit-operations" name="copy-input-to"/>
+   <Action group="session-edit-operations" name="send-signal"/>
+   <Action group="session-edit-operations" name="rename-session"/>
+   <Action group="session-edit-operations" name="zmodem-upload"/>
+   <Separator group="session-edit-operations"/>
+   <Action group="session-edit-operations" name="edit_find"/>
+   <Action group="session-edit-operations" name="edit_find_next"/>
+   <Action group="session-edit-operations" name="edit_find_prev"/>
+  </Menu>
+  <Menu name="view">
+   <Action group="session-view-operations" name="monitor-silence"/>
+   <Action group="session-view-operations" name="monitor-activity"/>
+   <Separator group="session-view-operations"/>
+   <Action group="session-view-operations" name="enlarge-font"/>
+   <Action group="session-view-operations" name="shrink-font"/>
+   <Action group="session-view-operations" name="set-encoding"/>
+   <Separator group="session-view-operations"/>
+   <Action group="session-view-operations" name="clear-history"/>
+   <Action group="session-view-operations" name="clear-history-and-reset"/>
+  </Menu>
+  <Menu name="settings">
+   <Action group="session-settings" name="edit-current-profile"/>
+   <Action group="session-settings" name="switch-profile"/>
+  </Menu>
+ </MenuBar>
+ <Menu name="session-popup-menu">
+  <Action name="edit_copy"/>
+  <Action name="edit_paste"/>
+  <Action name="web-search"/>
+  <Action name="open-browser"/>
+  <Separator/>
+  <Action name="set-encoding"/>
+  <Action name="clear-history"/>
+  <Action name="adjust-history"/>
+  <Separator/>
+  <Action name="switch-profile"/>
+  <Action name="edit-current-profile"/>
+  <Separator/>
+  <Action name="close-session"/>
+ </Menu>
+ <ActionProperties scheme="Default">
+  <Action shortcut="Ctrl+@" name="open-browser"/>
+  <Action shortcut="Ctrl+Shift+R" name="edit_find_next"/>
+  <Action shortcut="Ctrl+Shift+E" name="edit_find_prev"/>
+ </ActionProperties>
+</kpartgui>
diff --git a/subdir_files/.xmonad/xmonad.hs b/subdir_files/.xmonad/xmonad.hs
new file mode 100644 (file)
index 0000000..b385ad5
--- /dev/null
@@ -0,0 +1,241 @@
+
+import XMonad
+--import XMonad.Config.Gnome
+
+import Data.Monoid
+import System.Exit
+
+-- to get mouse move with window
+import XMonad.Actions.UpdatePointer
+
+-- needed for the panel toggle keybind
+import XMonad.Hooks.ManageDocks
+
+-- not sure if this is redundant with Xfce import
+import XMonad.Config.Desktop
+
+-- based on http://www.haskell.org/haskellwiki/Xmonad/Using_xmonad_in_XFCE
+import XMonad.Hooks.EwmhDesktops
+import XMonad.Config.Xfce
+import XMonad.Hooks.ManageDocks
+import XMonad.Layout.NoFrillsDecoration
+
+import qualified XMonad.StackSet as W
+import qualified Data.Map        as M
+
+import XMonad.Actions.CycleWindows    --  Reset the layouts on the current workspace to default
+
+import XMonad.Hooks.SetWMName
+
+
+-- /usr/share/xmonad/xmonad.hs on fedora has integration with gnome etc. but I
+-- had lots of problems with gnome integration.
+myL = noFrillsDeco shrinkText def (layoutHook def)
+
+main = xmonad $ defaults
+--main = xmonad def { layoutHook = myL } $ defaults
+
+
+defaults = desktopConfig
+       {
+         terminal            = "konsole"
+       , borderWidth        = 0
+       , modMask            = mod4Mask
+       , keys               = myKeys
+       --, logHook            = myLogHook <+> logHook desktopConfig
+       , mouseBindings      = myMouseBindings
+
+
+    --   manageDocks & ewmh stuff is based on xfce integration wiki page
+       , manageHook    = manageDocks <+> myManageHook <+> manageHook defaultConfig
+       , logHook    = ewmhDesktopsLogHook
+-- todo, look into the suggested avoidStruts. my nieve attempt to integrate it failed
+       , layoutHook         = myLayout
+       , handleEventHook = ewmhDesktopsEventHook
+-- this makes some java programs work, like intellij idea,
+-- but it also makes the ui buggy for some gtk apps, like transmission
+-- and gnome-terminal
+-- https://bbs.archlinux.org/viewtopic.php?pid=744577
+       , startupHook = setWMName "LG3D"
+       -- combining the 2 with <+> didn't make java apps work.
+       --, startupHook = ewmhDesktopsStartup
+
+}
+
+
+-- for android emulator buggy window
+myManageHook = composeAll [ className =? "emulator64-arm" --> unfloat]
+    where unfloat = ask >>= doF . W.sink
+
+-- this is a nice idea but needs some tweaking cuz i don't want it to
+--  have an affect when switching to a floating window,
+--  and I remember sometimes being stuck in floating dialog boxes
+-- myLogHook = updatePointer (Relative 0.5 0.5)
+
+
+
+myKeys conf@(XConfig {XMonad.modMask = modm}) = M.fromList $
+
+
+-- Move focus to the previous window
+--    , ((modm,               xK_q     ), windows W.focusUp  )
+
+
+    [ ((modm, xK_q), io (exitWith ExitSuccess)) -- %! Quit xmonad
+
+    , ((modm, xK_2), spawn "pavucontrol")
+--    , ((modm, xK_3), spawn "firefox  -no-remote -P sfw")
+--    todo: clicking links in other applications won't open in
+--    a firefox opened with -no-remote, so we have to hardcode a
+--    primary firefox profile here. I'd rather have a command which
+--    sets which is the pimary for opening links. It could alter a file,
+--    and we could launch firefox with a wrapper script which looks at that file.
+    , ((modm, xK_3), spawn "firefox -P sfw")
+    , ((modm, xK_4), spawn "firefox -no-remote -P firefox-main-profile")
+
+    , ((modm, xK_5), spawn "/a/exe/input-setup l")
+    , ((modm, xK_1), spawn "/a/exe/input-setup k")
+
+    -- we would really want to keep track of which workspace is darkened,
+    -- and change things depending on where that workspace is
+
+      -- fedora vs debian ordering
+--    , ((modm, xK_w),  spawn "redshift.sh 0")
+--    , ((modm, xK_e),  spawn "redshift.sh 2")
+--    , ((modm, xK_r),  spawn "redshift.sh 1")
+    , ((modm, xK_w),  spawn "/a/bin/redshift.sh 2")
+    , ((modm, xK_e),  spawn "/a/bin/redshift.sh 1")
+    , ((modm, xK_r),  spawn "/a/bin/redshift.sh 0")
+
+  -- Rotate through the available layout algorithms
+    , ((modm,               xK_t ), sendMessage NextLayout)
+
+    , ((modm              , xK_Home     ), sendMessage ToggleStruts)
+
+-- disabled default of cycling windows. instead use traditional stack based alt-tab behavior
+-- todo, this locks up everytyhing, and the only thing that works is
+-- futher cycle key presses until i restart X
+  --  , ((modm,               xK_g   ), cycleRecentWindows [xK_Super_L,xK_Super_R] xK_g xK_Tab)
+
+    -- close focused window
+    , ((modm , xK_c     ), kill)
+
+
+    --  Reset the layouts on the current workspace to default
+    , ((modm, xK_b ), setLayout $ XMonad.layoutHook conf)
+
+
+    -- todo, make the pointer follow the window for these
+
+    -- fedora's order
+--    , ((modm              , xK_Delete), screenWorkspace 0 >>= flip whenJust (windows . W.shift))
+--    , ((modm              , xK_parenleft), screenWorkspace 2 >>= flip whenJust (windows . W.shift))
+--    , ((modm              , xK_parenright), screenWorkspace 1 >>= flip whenJust (windows . W.shift))
+-- debian's order
+    , ((modm              , xK_Delete), screenWorkspace 2 >>= flip whenJust (windows . W.shift))
+    , ((modm              , xK_parenleft), screenWorkspace 1 >>= flip whenJust (windows . W.shift))
+    , ((modm              , xK_parenright), screenWorkspace 0 >>= flip whenJust (windows . W.shift))
+
+    -- Move focus to the next window
+    , ((0,               xK_F8   ), windows W.focusDown)
+
+    , ((modm              , xK_6), windows $ W.greedyView "6")
+
+-- Swap the focused window with the next window
+    , ((modm, xK_asterisk     ), windows W.swapDown  )
+
+    -- Swap the focused window with the previous window
+    , ((modm, xK_9     ), windows W.swapUp    )
+
+
+-- fedora's order
+--    , ((modm              , xK_u), screenWorkspace 0 >>= flip whenJust (windows . W.view))
+--    , ((modm              , xK_i), screenWorkspace 2 >>= flip whenJust (windows . W.view))
+--    , ((modm              , xK_o), screenWorkspace 1 >>= flip whenJust (windows . W.view))
+-- debian's order
+    , ((modm              , xK_u), screenWorkspace 2 >>= flip whenJust (windows . W.view))
+    , ((modm              , xK_i), screenWorkspace 1 >>= flip whenJust (windows . W.view))
+    , ((modm              , xK_o), screenWorkspace 0 >>= flip whenJust (windows . W.view))
+
+
+     -- Restart xmonad
+    , ((modm              , xK_p), spawn "xmonad --recompile; xmonad --restart")
+   -- Push window back into tiling
+    , ((modm,               xK_h    ), withFocused $ windows . W.sink)
+
+    -- this working is dependent upon env vars set in distro-start
+    , ((modm,               xK_j     ), spawn "emacsclient -c")
+
+
+    -- launch a terminal
+    , ((modm, xK_k), spawn $ XMonad.terminal conf)
+
+
+    , ((modm, xK_l), spawn "dmenu_run")
+
+    -- Expand the master area
+    , ((modm,               xK_n     ), sendMessage Expand)
+
+   -- Shrink the master area
+    , ((modm,               xK_m    ), sendMessage Shrink)
+
+    -- Move focus to the master window
+    , ((modm,               xK_y     ), windows W.focusMaster  )
+
+
+
+    -- Increment the number of windows in the master area
+    , ((modm              , xK_comma ), sendMessage (IncMasterN 1))
+
+    -- Deincrement the number of windows in the master area
+    , ((modm              , xK_period), sendMessage (IncMasterN (-1)))
+
+    -- Swap the focused window and the master window
+    --, ((modm,               xK_Return), windows W.swapMaster)
+-- in recent versions, a different name works better
+     , ((modm,               xK_Return), windows W.shiftMaster)
+
+    ]
+    ++
+
+
+
+    -- open workspace, and send to workspace with same key plus shift
+    [((m .|. modm, k), windows $ f i)
+         | (i, k) <- zip (XMonad.workspaces conf) [xK_f, xK_d, xK_s, xK_a, xK_z, xK_grave, xK_7, xK_0]
+    , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]]
+--        fedora's default ordering of workspaces
+ --         xK_s, xK_f, xK_d,
+
+
+
+
+myMouseBindings (XConfig {XMonad.modMask = modm}) = M.fromList $
+
+    -- mod-button1, Set the window to floating mode and move by dragging
+    [ ((modm, button1), (\w -> focus w >> mouseMoveWindow w
+                                       >> windows W.shiftMaster))
+
+    -- mod-button2, Raise the window to the top of the stack
+    , ((modm, button2), (\w -> focus w >> windows W.shiftMaster))
+
+    -- mod-button3, Set the window to floating mode and resize by dragging
+    , ((modm, button3), (\w -> focus w >> mouseResizeWindow w
+                                       >> windows W.shiftMaster))
+
+    -- you may also bind events to the mouse scroll wheel (button4 and button5)
+    ]
+
+myLayout = desktopLayoutModifiers $ Full ||| Mirror tiled ||| tiled
+  where
+    -- default tiling algorithm partitions the screen into two panes
+    tiled   = Tall nmaster delta ratio
+
+    -- The default number of windows in the master pane
+    nmaster = 1
+
+    -- Default proportion of screen occupied by master pane
+    ratio   = 1/2
+
+    -- Percent of screen to increment by when resizing panes
+    delta   = 3/100
diff --git a/subdir_files/sieve/fsf-test.sieve b/subdir_files/sieve/fsf-test.sieve
new file mode 100644 (file)
index 0000000..9c631af
--- /dev/null
@@ -0,0 +1,29 @@
+require [ "regex", "variables", "fileinto", "envelope", "mailbox", "imap4flags", "include" ];
+
+if anyof (
+    address :is "from" "sysadmin@gnu.org",
+    address :is "to" "sysadmin-nonrt@gnu.org",
+    address :is "from" "sysadmin-comment@gnu.org"
+    ) {
+    fileinto :create "sysadmin";
+    stop;
+         }
+elsif anyof (
+    header :contains "list-id" "<office-commits.gnu.org>",
+    header :is "Return-path" "<rtbounces@gnu.org>"
+    ) {
+fileinto :create "rtcc";
+    stop;
+}
+
+if anyof (
+    header :regex "subject" "mailing list memberships reminder",
+    address :is "to" "rtbounces@gnu.org",
+    address :is "to" "faxmaster@fsf.org",
+    address :is "from" "FaxMaster@fsf.org"
+    ) {
+    fileinto :create "log";
+    stop;
+         }
+
+include :personal "lists";
diff --git a/subdir_files/sieve/fsf.sieve b/subdir_files/sieve/fsf.sieve
new file mode 100644 (file)
index 0000000..9c631af
--- /dev/null
@@ -0,0 +1,29 @@
+require [ "regex", "variables", "fileinto", "envelope", "mailbox", "imap4flags", "include" ];
+
+if anyof (
+    address :is "from" "sysadmin@gnu.org",
+    address :is "to" "sysadmin-nonrt@gnu.org",
+    address :is "from" "sysadmin-comment@gnu.org"
+    ) {
+    fileinto :create "sysadmin";
+    stop;
+         }
+elsif anyof (
+    header :contains "list-id" "<office-commits.gnu.org>",
+    header :is "Return-path" "<rtbounces@gnu.org>"
+    ) {
+fileinto :create "rtcc";
+    stop;
+}
+
+if anyof (
+    header :regex "subject" "mailing list memberships reminder",
+    address :is "to" "rtbounces@gnu.org",
+    address :is "to" "faxmaster@fsf.org",
+    address :is "from" "FaxMaster@fsf.org"
+    ) {
+    fileinto :create "log";
+    stop;
+         }
+
+include :personal "lists";
diff --git a/subdir_files/sieve/lists.sieve b/subdir_files/sieve/lists.sieve
new file mode 100644 (file)
index 0000000..6dbafa2
--- /dev/null
@@ -0,0 +1,85 @@
+require [ "regex", "variables", "fileinto", "envelope", "mailbox", "imap4flags", "include" ];
+
+if anyof (
+    header :contains "list-id" "<debian-security-announce.lists.debian.org>",
+    header :contains "list-id" "<ubuntu-security-announce.lists.ubuntu.com>"
+    ) {
+    fileinto :create "sec";
+    stop;
+         }
+
+if anyof (
+    header :contains "list-id" "<bbdb-info.lists.sourceforge.net>",
+    header :contains "list-id" "<bug-bash.gnu.org>",
+    header :contains "list-id" "<bug-gnu-emacs.gnu.org>",
+    header :contains "list-id" "<debian-backports.lists.debian.org>",
+    header :contains "list-id" "<debian-security-announce.lists.debian.org>",
+    header :contains "list-id" "<debian-user.lists.debian.org>",
+    header :contains "list-id" "<ding.gnus.org>",
+    header :contains "list-id" "<emacs-devel.gnu.org>",
+    header :contains "list-id" "<emacs-orgmode.gnu.org>",
+    header :contains "list-id" "<git.vger.kernel.org>",
+    header :contains "list-id" "<help-bash.gnu.org>",
+    header :contains "list-id" "<help-gnu-emacs.gnu.org>",
+    header :contains "list-id" "<hyperkitty-devel.lists.fedorahosted.org>",
+    header :contains "list-id" "<ipxe-devel.lists.ipxe.org>",
+    header :contains "list-id" "<kplug-list.kernel-panic.org>",
+    header :contains "list-id" "<kplug-newbie.kernel-panic.org>",
+    header :contains "list-id" "<libreplanet-discuss.libreplanet.org>",
+    header :contains "list-id" "<linux-btrfs.vger.kernel.org>",
+    header :contains "list-id" "<linux-fai.uni-koeln.de>",
+    header :contains "list-id" "<linux-fai-devel.uni-koeln.de>",
+    header :contains "list-id" "<linux-libre.fsfla.org>",
+    header :contains "list-id" "<maru-os-dev.googlegroups.com>",
+    header :contains "list-id" "<mediawiki-l.lists.wikimedia.org>",
+    header :contains "list-id" "<pywikibot.lists.wikimedia.org>",
+    header :contains "list-id" "<pywikipedia-l.lists.wikimedia.org>",
+    header :contains "list-id" "<unison-hackers.lists.seas.upenn.edu>",
+    header :contains "list-id" "<unison-users.yahoogroups.com>",
+    header :contains "list-id" "<whispersystems.lists.riseup.net>",
+    header :contains "list-id" "<wikitech-l.lists.wikimedia.org>",
+    header :contains "list-id" "<xapian-devel.lists.xapian.org>",
+    header :contains "list-id" "<kdeconnect.kde.org>",
+    header :contains "list-id" "<mu-discuss.googlegroups.com>",
+    header :contains "list-id" "<<devel.mediagoblin.org>",
+    header :contains "list-id" "<maru-os.googlegroups.com>",
+    header :contains "list-id" "<listhelper-moderate.nongnu.org>",
+    header :contains "list-id" "<coreboot.coreboot.org>",
+    header :contains "list-id" "<mailman-users.python.org>",
+    header :contains "list-id" "<duplicity-talk.nongnu.org>",
+    header :contains "list-id" "<qemu-devel.nongnu.org>",
+    header :contains "list-id" "<exim-dev.exim.org>",
+    header :contains "list-id" "<exim-users.exim.org>",
+    header :contains "list-id" "<xmonad.haskell.org>") {
+    if header :regex "list-id" "<([a-z_0-9-]+)[.@]" {
+        set :lower "listname" "${1}";
+        fileinto :create "l/${listname}";
+    }
+}
+if anyof (
+    header :contains "list-id" "<websites.lists.fedoraproject.org>",
+    header :contains "list-id" "<docs.lists.fedoraproject.org>",
+    header :contains "list-id" "<users.lists.fedoraproject.org>") {
+    if header :regex "list-id" "<([a-z_0-9-]+)[.@]" {
+        set :lower "listname" "${1}";
+        fileinto :create "l/fedora.${listname}";
+    }
+}
+
+if anyof (
+    header :contains "list-id" "<activists_masspirates.org.lists.mayfirst.org>",
+    header :contains "list-id" "<gnu-prog-discuss.gnu.org>",
+    header :contains "list-id" "<gnu-prog.gnu.org>",
+    header :contains "list-id" "<discuss.blu.org>"
+    ) {
+    fileinto :create "community";
+    stop;
+         }
+
+if allof (
+    address :is "from" "mailman-owner@zope.org",
+    header :is "subject" "zope.org mailing list memberships reminder"
+    ) {
+    discard;
+    stop;
+         }
diff --git a/subdir_files/sieve/main.sieve b/subdir_files/sieve/main.sieve
new file mode 100644 (file)
index 0000000..1098ea0
--- /dev/null
@@ -0,0 +1,25 @@
+# Sieve filter
+# note: main is just a random name, since dovecot required a name
+##
+require [ "regex", "variables", "fileinto", "envelope", "mailbox", "imap4flags", "include" ];
+
+# many examples out there check for "X-Spam-Status" "^Yes", but we do
+# this in exim, which doesn't add that by default.  We could modify it's
+# config to add $spam_action to a header, like other headers, but simply
+# using an integer threshold here is simpler: the default threshold for
+# spamassassin is 5, so we have 5 plus symbols here.
+if header :regex "x-spam_bar" "^\\+{5}" {
+    fileinto :create "Junk";
+    stop;
+}
+
+include :personal "personal";
+include :personal "lists";
+
+
+# note, i originally used something like this to filter mailing lists
+# http://www.dovecot.org/list/dovecot/2014-September/097846.html
+# However, I found that there were a lot of special cases
+# where I did not want them filtered, like small mailing lists, etc,
+# so I prefer to keep a list of lists that I want filed into folders.
+# However, it was useful as an example for constructing that code.
diff --git a/subdir_files/sieve/maintest.sieve b/subdir_files/sieve/maintest.sieve
new file mode 100644 (file)
index 0000000..431fd67
--- /dev/null
@@ -0,0 +1,24 @@
+# Sieve filter
+# note: main is just a random name, since dovecot required a name
+##
+require [ "regex", "variables", "fileinto", "envelope", "mailbox", "imap4flags", "include" ];
+
+# many examples out there check for "X-Spam-Status" "^Yes", but we do
+# this in exim, which doesn't add that by default.  We could modify it's
+# config to add $spam_action to a header, like other headers, but simply
+# using an integer threshold here is simpler: the default threshold for
+# spamassassin is 5, so we have 5 plus symbols here.
+if header :regex "x-spam_bar" "^\\+{5}" {
+    fileinto :create "Junk";
+    stop;
+}
+
+include :personal "personaltest";
+
+
+# note, i originally used something like this to filter mailing lists
+# http://www.dovecot.org/list/dovecot/2014-September/097846.html
+# However, I found that there were a lot of special cases
+# where I did not want them filtered, like small mailing lists, etc,
+# so I prefer to keep a list of lists that I want filed into folders.
+# However, it was useful as an example for constructing that code.
diff --git a/x2.xkb b/x2.xkb
new file mode 100644 (file)
index 0000000..1dd571a
--- /dev/null
+++ b/x2.xkb
@@ -0,0 +1,1907 @@
+xkb_keymap {
+xkb_keycodes "evdev+aliases(qwerty)" {
+    minimum = 8;
+    maximum = 255;
+     <ESC> = 9;
+    <AE01> = 10;
+    <AE02> = 11;
+    <AE03> = 12;
+    <AE04> = 13;
+    <AE05> = 14;
+    <AE06> = 15;
+    <AE07> = 16;
+    <AE08> = 17;
+    <AE09> = 18;
+    <AE10> = 19;
+    <AE11> = 20;
+    <AE12> = 21;
+    <BKSP> = 22;
+     <TAB> = 23;
+    <AD01> = 24;
+    <AD02> = 25;
+    <AD03> = 26;
+    <AD04> = 27;
+    <AD05> = 28;
+    <AD06> = 29;
+    <AD07> = 30;
+    <AD08> = 31;
+    <AD09> = 32;
+    <AD10> = 33;
+    <AD11> = 34;
+    <AD12> = 35;
+    <RTRN> = 36;
+    <LCTL> = 64;
+    <AC01> = 38;
+    <AC02> = 39;
+    <AC03> = 40;
+    <AC04> = 41;
+    <AC05> = 42;
+    <AC06> = 43;
+    <AC07> = 44;
+    <AC08> = 45;
+    <AC09> = 46;
+    <AC10> = 47;
+    <AC11> = 48;
+    <TLDE> = 49;
+    <LFSH> = 50;
+    <BKSL> = 51;
+    <AB01> = 52;
+    <AB02> = 53;
+    <AB03> = 54;
+    <AB04> = 55;
+    <AB05> = 56;
+    <AB06> = 57;
+    <AB07> = 58;
+    <AB08> = 59;
+    <AB09> = 60;
+    <AB10> = 61;
+    <RTSH> = 62;
+    <KPMU> = 63;
+   # <LALT> = 64;
+    <SPCE> = 65;
+   # <CAPS> = 66;
+    <FK01> = 67;
+    <FK02> = 68;
+    <FK03> = 69;
+    <FK04> = 70;
+    <FK05> = 71;
+    <FK06> = 72;
+    <FK07> = 73;
+    <FK08> = 74;
+    <FK09> = 75;
+    <FK10> = 76;
+    <NMLK> = 77;
+    <SCLK> = 78;
+     <KP7> = 79;
+     <KP8> = 80;
+     <KP9> = 81;
+    <KPSU> = 82;
+     <KP4> = 83;
+     <KP5> = 84;
+     <KP6> = 85;
+    <KPAD> = 66;
+     <KP1> = 87;
+     <KP2> = 88;
+     <KP3> = 89;
+     <KP0> = 90;
+    <KPDL> = 91;
+    <LVL3> = 92;
+    <LSGT> = 94;
+    <FK11> = 95;
+    <FK12> = 96;
+    <AB11> = 97;
+    <KATA> = 98;
+    <HIRA> = 99;
+    <HENK> = 100;
+    <HKTG> = 101;
+    <MUHE> = 102;
+    <JPCM> = 103;
+    <KPEN> = 135;
+   # <RCTL> = 105;
+    <KPDV> = 105;
+    <PRSC> = 107;
+    <RALT> = 108;
+    <LNFD> = 109;
+    <HOME> = 110;
+      <UP> = 111;
+    <PGUP> = 112;
+    <LEFT> = 113;
+    <RGHT> = 114;
+     <END> = 115;
+    <DOWN> = 116;
+    <PGDN> = 117;
+     <INS> = 118;
+    <DELE> = 119;
+    <I120> = 120;
+    <MUTE> = 121;
+    <VOL-> = 122;
+    <VOL+> = 123;
+    <POWR> = 124;
+    <KPEQ> = 37;
+    <I126> = 126;
+    <PAUS> = 127;
+    <I128> = 128;
+    <I129> = 129;
+    <HNGL> = 130;
+    <HJCV> = 131;
+    <AE13> = 132;
+    <LWIN> = 133;
+    <RWIN> = 134;
+   # <COMP> = 135;
+    <STOP> = 136;
+    <AGAI> = 137;
+    <PROP> = 138;
+    <UNDO> = 139;
+    <FRNT> = 140;
+    <COPY> = 141;
+    <OPEN> = 142;
+    <PAST> = 143;
+    <FIND> = 144;
+     <CUT> = 145;
+    <HELP> = 146;
+    <I147> = 147;
+    <I148> = 148;
+    <I149> = 149;
+    <I150> = 150;
+    <I151> = 151;
+    <I152> = 152;
+    <I153> = 153;
+    <I154> = 154;
+    <I155> = 155;
+    <I156> = 156;
+    <I157> = 157;
+    <I158> = 158;
+    <I159> = 159;
+    <I160> = 160;
+    <I161> = 161;
+    <I162> = 162;
+    <I163> = 163;
+    <I164> = 164;
+    <I165> = 165;
+    <I166> = 166;
+    <I167> = 167;
+    <I168> = 168;
+    <I169> = 169;
+    <I170> = 170;
+    <I171> = 171;
+    <I172> = 172;
+    <I173> = 173;
+    <I174> = 174;
+    <I175> = 175;
+    <I176> = 176;
+    <I177> = 177;
+    <I178> = 178;
+    <I179> = 179;
+    <I180> = 180;
+    <I181> = 181;
+    <I182> = 182;
+    <I183> = 183;
+    <I184> = 184;
+    <I185> = 185;
+    <I186> = 186;
+    <I187> = 187;
+    <I188> = 188;
+    <I189> = 189;
+    <I190> = 190;
+    <FK13> = 191;
+    <FK14> = 192;
+    <FK15> = 193;
+    <FK16> = 194;
+    <FK17> = 195;
+    <FK18> = 196;
+    <FK19> = 197;
+    <FK20> = 198;
+    <FK21> = 199;
+    <FK22> = 200;
+    <FK23> = 201;
+    <FK24> = 202;
+    <MDSW> = 203;
+     <ALT> = 204;
+    <META> = 205;
+    <SUPR> = 206;
+    <HYPR> = 207;
+    <I208> = 208;
+    <I209> = 209;
+    <I210> = 210;
+    <I211> = 211;
+    <I212> = 212;
+    <I213> = 213;
+    <I214> = 214;
+    <I215> = 215;
+    <I216> = 216;
+    <I217> = 217;
+    <I218> = 218;
+    <I219> = 219;
+    <I220> = 220;
+    <I221> = 221;
+    <I222> = 222;
+    <I223> = 223;
+    <I224> = 224;
+    <I225> = 225;
+    <I226> = 226;
+    <I227> = 227;
+    <I228> = 228;
+    <I229> = 229;
+    <I230> = 230;
+    <I231> = 231;
+    <I232> = 232;
+    <I233> = 233;
+    <I234> = 234;
+    <I235> = 235;
+    <I236> = 236;
+    <I237> = 237;
+    <I238> = 238;
+    <I239> = 239;
+    <I240> = 240;
+    <I241> = 241;
+    <I242> = 242;
+    <I243> = 243;
+    <I244> = 244;
+    <I245> = 245;
+    <I246> = 246;
+    <I247> = 247;
+    <I248> = 248;
+    <I249> = 249;
+    <I250> = 250;
+    <I251> = 251;
+    <I252> = 252;
+    <I253> = 253;
+    indicator 1 = "Caps Lock";
+    indicator 2 = "Num Lock";
+    indicator 3 = "Scroll Lock";
+    indicator 4 = "Compose";
+    indicator 5 = "Kana";
+    indicator 6 = "Sleep";
+    indicator 7 = "Suspend";
+    indicator 8 = "Mute";
+    indicator 9 = "Misc";
+    indicator 10 = "Mail";
+    indicator 11 = "Charging";
+    virtual indicator 12 = "Shift Lock";
+    virtual indicator 13 = "Group 2";
+    virtual indicator 14 = "Mouse Keys";
+    alias <AC12> = <BKSL>;
+    alias <MENU> = <COMP>;
+    alias <HZTG> = <TLDE>;
+    alias <LMTA> = <LWIN>;
+    alias <RMTA> = <RWIN>;
+    alias <ALGR> = <RALT>;
+    alias <KPPT> = <I129>;
+    alias <LatQ> = <AD01>;
+    alias <LatW> = <AD02>;
+    alias <LatE> = <AD03>;
+    alias <LatR> = <AD04>;
+    alias <LatT> = <AD05>;
+    alias <LatY> = <AD06>;
+    alias <LatU> = <AD07>;
+    alias <LatI> = <AD08>;
+    alias <LatO> = <AD09>;
+    alias <LatP> = <AD10>;
+    alias <LatA> = <AC01>;
+    alias <LatS> = <AC02>;
+    alias <LatD> = <AC03>;
+    alias <LatF> = <AC04>;
+    alias <LatG> = <AC05>;
+    alias <LatH> = <AC06>;
+    alias <LatJ> = <AC07>;
+    alias <LatK> = <AC08>;
+    alias <LatL> = <AC09>;
+    alias <LatZ> = <AB01>;
+    alias <LatX> = <AB02>;
+    alias <LatC> = <AB03>;
+    alias <LatV> = <AB04>;
+    alias <LatB> = <AB05>;
+    alias <LatN> = <AB06>;
+    alias <LatM> = <AB07>;
+};
+
+xkb_types "complete" {
+
+    virtual_modifiers NumLock,Alt,LevelThree,LAlt,RAlt,RControl,LControl,ScrollLock,LevelFive,AltGr,Meta,Super,Hyper;
+
+    type "ONE_LEVEL" {
+        modifiers= none;
+        level_name[Level1]= "Any";
+    };
+    type "TWO_LEVEL" {
+        modifiers= Shift;
+        map[Shift]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+    };
+    type "ALPHABETIC" {
+        modifiers= Shift+Lock;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Caps";
+    };
+    type "KEYPAD" {
+        modifiers= Shift+NumLock;
+        map[Shift]= Level2;
+        map[NumLock]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+    };
+    type "SHIFT+ALT" {
+        modifiers= Shift+Alt;
+        map[Shift+Alt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift+Alt";
+    };
+    type "PC_SUPER_LEVEL2" {
+        modifiers= Mod4;
+        map[Mod4]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Super";
+    };
+    type "PC_CONTROL_LEVEL2" {
+        modifiers= Control;
+        map[Control]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Control";
+    };
+    type "PC_LCONTROL_LEVEL2" {
+        modifiers= LControl;
+        map[LControl]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "LControl";
+    };
+    type "PC_RCONTROL_LEVEL2" {
+        modifiers= RControl;
+        map[RControl]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "RControl";
+    };
+    type "PC_ALT_LEVEL2" {
+        modifiers= Alt;
+        map[Alt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Alt";
+    };
+    type "PC_LALT_LEVEL2" {
+        modifiers= LAlt;
+        map[LAlt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "LAlt";
+    };
+    type "PC_RALT_LEVEL2" {
+        modifiers= RAlt;
+        map[RAlt]= Level2;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "RAlt";
+    };
+    type "CTRL+ALT" {
+        modifiers= Shift+Control+Alt+LevelThree;
+        map[Shift]= Level2;
+        preserve[Shift]= Shift;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        preserve[Shift+LevelThree]= Shift;
+        map[Control+Alt]= Level5;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "Ctrl+Alt";
+    };
+    type "LOCAL_EIGHT_LEVEL" {
+        modifiers= Shift+Lock+Control+LevelThree;
+        map[Shift+Lock]= Level1;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Control]= Level5;
+        map[Shift+Lock+Control]= Level5;
+        map[Shift+Control]= Level6;
+        map[Lock+Control]= Level6;
+        map[Control+LevelThree]= Level7;
+        map[Shift+Lock+Control+LevelThree]= Level7;
+        map[Shift+Control+LevelThree]= Level8;
+        map[Lock+Control+LevelThree]= Level8;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Level3";
+        level_name[Level4]= "Shift Level3";
+        level_name[Level5]= "Ctrl";
+        level_name[Level6]= "Shift Ctrl";
+        level_name[Level7]= "Level3 Ctrl";
+        level_name[Level8]= "Shift Level3 Ctrl";
+    };
+    type "THREE_LEVEL" {
+        modifiers= Shift+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Level3";
+    };
+    type "EIGHT_LEVEL" {
+        modifiers= Shift+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Shift+Lock+LevelThree]= Level3;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[Lock+LevelFive]= Level6;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[Lock+LevelThree+LevelFive]= Level8;
+        map[Shift+Lock+LevelThree+LevelFive]= Level7;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_LEVEL_FIVE_LOCK" {
+        modifiers= Shift+Lock+NumLock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        preserve[Shift+LevelFive]= Shift;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[NumLock]= Level5;
+        map[Shift+NumLock]= Level6;
+        preserve[Shift+NumLock]= Shift;
+        map[NumLock+LevelThree]= Level7;
+        map[Shift+NumLock+LevelThree]= Level8;
+        map[Shift+NumLock+LevelFive]= Level2;
+        map[NumLock+LevelThree+LevelFive]= Level3;
+        map[Shift+NumLock+LevelThree+LevelFive]= Level4;
+        map[Shift+Lock]= Level2;
+        map[Lock+LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level4;
+        map[Lock+LevelFive]= Level5;
+        map[Shift+Lock+LevelFive]= Level6;
+        preserve[Shift+Lock+LevelFive]= Shift;
+        map[Lock+LevelThree+LevelFive]= Level7;
+        map[Shift+Lock+LevelThree+LevelFive]= Level8;
+        map[Lock+NumLock]= Level5;
+        map[Shift+Lock+NumLock]= Level6;
+        preserve[Shift+Lock+NumLock]= Shift;
+        map[Lock+NumLock+LevelThree]= Level7;
+        map[Shift+Lock+NumLock+LevelThree]= Level8;
+        map[Shift+Lock+NumLock+LevelFive]= Level2;
+        map[Lock+NumLock+LevelThree+LevelFive]= Level3;
+        map[Shift+Lock+NumLock+LevelThree+LevelFive]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_ALPHABETIC_LEVEL_FIVE_LOCK" {
+        modifiers= Shift+Lock+NumLock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        preserve[Shift+LevelFive]= Shift;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[NumLock]= Level5;
+        map[Shift+NumLock]= Level6;
+        preserve[Shift+NumLock]= Shift;
+        map[NumLock+LevelThree]= Level7;
+        map[Shift+NumLock+LevelThree]= Level8;
+        map[Shift+NumLock+LevelFive]= Level2;
+        map[NumLock+LevelThree+LevelFive]= Level3;
+        map[Shift+NumLock+LevelThree+LevelFive]= Level4;
+        map[Lock]= Level2;
+        map[Lock+LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level4;
+        map[Lock+LevelFive]= Level5;
+        map[Shift+Lock+LevelFive]= Level6;
+        map[Lock+LevelThree+LevelFive]= Level7;
+        map[Shift+Lock+LevelThree+LevelFive]= Level8;
+        map[Lock+NumLock]= Level5;
+        map[Shift+Lock+NumLock]= Level6;
+        map[Lock+NumLock+LevelThree]= Level7;
+        map[Shift+Lock+NumLock+LevelThree]= Level8;
+        map[Lock+NumLock+LevelFive]= Level2;
+        map[Lock+NumLock+LevelThree+LevelFive]= Level4;
+        map[Shift+Lock+NumLock+LevelThree+LevelFive]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "EIGHT_LEVEL_SEMIALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree+LevelFive;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level4;
+        preserve[Shift+Lock+LevelThree]= Lock;
+        map[LevelFive]= Level5;
+        map[Shift+LevelFive]= Level6;
+        map[Lock+LevelFive]= Level6;
+        preserve[Lock+LevelFive]= Lock;
+        map[Shift+Lock+LevelFive]= Level6;
+        preserve[Shift+Lock+LevelFive]= Lock;
+        map[LevelThree+LevelFive]= Level7;
+        map[Shift+LevelThree+LevelFive]= Level8;
+        map[Lock+LevelThree+LevelFive]= Level7;
+        preserve[Lock+LevelThree+LevelFive]= Lock;
+        map[Shift+Lock+LevelThree+LevelFive]= Level8;
+        preserve[Shift+Lock+LevelThree+LevelFive]= Lock;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "X";
+        level_name[Level6]= "X Shift";
+        level_name[Level7]= "X Alt Base";
+        level_name[Level8]= "X Shift Alt";
+    };
+    type "FOUR_LEVEL" {
+        modifiers= Shift+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level4;
+        map[Shift+Lock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_SEMIALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level4;
+        preserve[Shift+Lock+LevelThree]= Lock;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_MIXED_KEYPAD" {
+        modifiers= Shift+NumLock+LevelThree;
+        map[Shift+NumLock]= Level1;
+        map[NumLock]= Level2;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[NumLock+LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Shift+NumLock+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+    };
+    type "FOUR_LEVEL_X" {
+        modifiers= Shift+Control+Alt+LevelThree;
+        map[LevelThree]= Level2;
+        map[Shift+LevelThree]= Level3;
+        map[Control+Alt]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Alt Base";
+        level_name[Level3]= "Shift Alt";
+        level_name[Level4]= "Ctrl+Alt";
+    };
+    type "SEPARATE_CAPS_AND_SHIFT_ALPHABETIC" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[Lock]= Level4;
+        preserve[Lock]= Lock;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock+LevelThree]= Level3;
+        preserve[Lock+LevelThree]= Lock;
+        map[Shift+Lock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "AltGr Base";
+        level_name[Level4]= "Shift AltGr";
+    };
+    type "FOUR_LEVEL_PLUS_LOCK" {
+        modifiers= Shift+Lock+LevelThree;
+        map[Shift]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[Lock]= Level5;
+        map[Shift+Lock]= Level2;
+        map[Lock+LevelThree]= Level3;
+        map[Shift+Lock+LevelThree]= Level4;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Shift";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Shift Alt";
+        level_name[Level5]= "Lock";
+    };
+    type "FOUR_LEVEL_KEYPAD" {
+        modifiers= Shift+NumLock+LevelThree;
+        map[Shift]= Level2;
+        map[NumLock]= Level2;
+        map[LevelThree]= Level3;
+        map[Shift+LevelThree]= Level4;
+        map[NumLock+LevelThree]= Level4;
+        map[Shift+NumLock+LevelThree]= Level3;
+        level_name[Level1]= "Base";
+        level_name[Level2]= "Number";
+        level_name[Level3]= "Alt Base";
+        level_name[Level4]= "Alt Number";
+    };
+};
+
+xkb_compatibility "complete" {
+
+    virtual_modifiers NumLock,Alt,LevelThree,LAlt,RAlt,RControl,LControl,ScrollLock,LevelFive,AltGr,Meta,Super,Hyper;
+
+    interpret.useModMapMods= AnyLevel;
+    interpret.repeat= False;
+    interpret.locking= False;
+    interpret ISO_Level2_Latch+Exactly(Shift) {
+        useModMapMods=level1;
+        action= LatchMods(modifiers=Shift,clearLocks,latchToLock);
+    };
+    interpret Shift_Lock+AnyOf(Shift+Lock) {
+        action= LockMods(modifiers=Shift);
+    };
+    interpret Num_Lock+AnyOf(all) {
+        virtualModifier= NumLock;
+        action= LockMods(modifiers=NumLock);
+    };
+    interpret ISO_Level3_Shift+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= SetMods(modifiers=LevelThree,clearLocks);
+    };
+    interpret ISO_Level3_Latch+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock);
+    };
+    interpret ISO_Level3_Lock+AnyOf(all) {
+        virtualModifier= LevelThree;
+        useModMapMods=level1;
+        action= LockMods(modifiers=LevelThree);
+    };
+    interpret Alt_L+AnyOf(all) {
+        virtualModifier= Alt;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Alt_R+AnyOf(all) {
+        virtualModifier= Alt;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Meta_L+AnyOf(all) {
+        virtualModifier= Meta;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Meta_R+AnyOf(all) {
+        virtualModifier= Meta;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Super_L+AnyOf(all) {
+        virtualModifier= Super;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Super_R+AnyOf(all) {
+        virtualModifier= Super;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Hyper_L+AnyOf(all) {
+        virtualModifier= Hyper;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Hyper_R+AnyOf(all) {
+        virtualModifier= Hyper;
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    interpret Scroll_Lock+AnyOf(all) {
+        virtualModifier= ScrollLock;
+        action= LockMods(modifiers=modMapMods);
+    };
+    interpret ISO_Level5_Shift+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= SetMods(modifiers=LevelFive,clearLocks);
+    };
+    interpret ISO_Level5_Latch+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock);
+    };
+    interpret ISO_Level5_Lock+AnyOf(all) {
+        virtualModifier= LevelFive;
+        useModMapMods=level1;
+        action= LockMods(modifiers=LevelFive);
+    };
+    interpret Mode_switch+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= SetGroup(group=+1);
+    };
+    interpret ISO_Level3_Shift+AnyOfOrNone(all) {
+        action= SetMods(modifiers=LevelThree,clearLocks);
+    };
+    interpret ISO_Level3_Latch+AnyOfOrNone(all) {
+        action= LatchMods(modifiers=LevelThree,clearLocks,latchToLock);
+    };
+    interpret ISO_Level3_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=LevelThree);
+    };
+    interpret ISO_Group_Latch+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LatchGroup(group=2);
+    };
+    interpret ISO_Next_Group+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LockGroup(group=+1);
+    };
+    interpret ISO_Prev_Group+AnyOfOrNone(all) {
+        virtualModifier= AltGr;
+        useModMapMods=level1;
+        action= LockGroup(group=-1);
+    };
+    interpret ISO_First_Group+AnyOfOrNone(all) {
+        action= LockGroup(group=1);
+    };
+    interpret ISO_Last_Group+AnyOfOrNone(all) {
+        action= LockGroup(group=2);
+    };
+    interpret KP_1+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+1);
+    };
+    interpret KP_End+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+1);
+    };
+    interpret KP_2+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=+1);
+    };
+    interpret KP_Down+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=+1);
+    };
+    interpret KP_3+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+1);
+    };
+    interpret KP_Next+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+1);
+    };
+    interpret KP_4+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+0);
+    };
+    interpret KP_Left+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+0);
+    };
+    interpret KP_6+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+0);
+    };
+    interpret KP_Right+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+0);
+    };
+    interpret KP_7+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=-1);
+    };
+    interpret KP_Home+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=-1);
+    };
+    interpret KP_8+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=-1);
+    };
+    interpret KP_Up+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+0,y=-1);
+    };
+    interpret KP_9+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=-1);
+    };
+    interpret KP_Prior+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=-1);
+    };
+    interpret KP_5+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default);
+    };
+    interpret KP_Begin+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default);
+    };
+    interpret KP_F2+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=1);
+    };
+    interpret KP_Divide+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=1);
+    };
+    interpret KP_F3+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=2);
+    };
+    interpret KP_Multiply+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=2);
+    };
+    interpret KP_F4+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=3);
+    };
+    interpret KP_Subtract+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=3);
+    };
+    interpret KP_Separator+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default,count=2);
+    };
+    interpret KP_Add+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default,count=2);
+    };
+    interpret KP_0+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=lock);
+    };
+    interpret KP_Insert+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=lock);
+    };
+    interpret KP_Decimal+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=unlock);
+    };
+    interpret KP_Delete+AnyOfOrNone(all) {
+        repeat= True;
+        action= LockPtrBtn(button=default,affect=unlock);
+    };
+    interpret F25+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=1);
+    };
+    interpret F26+AnyOfOrNone(all) {
+        repeat= True;
+        action= SetPtrDflt(affect=button,button=2);
+    };
+    interpret F27+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=-1);
+    };
+    interpret F29+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=-1);
+    };
+    interpret F31+AnyOfOrNone(all) {
+        repeat= True;
+        action= PtrBtn(button=default);
+    };
+    interpret F33+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=-1,y=+1);
+    };
+    interpret F35+AnyOfOrNone(all) {
+        repeat= True;
+        action= MovePtr(x=+1,y=+1);
+    };
+    interpret Pointer_Button_Dflt+AnyOfOrNone(all) {
+        action= PtrBtn(button=default);
+    };
+    interpret Pointer_Button1+AnyOfOrNone(all) {
+        action= PtrBtn(button=1);
+    };
+    interpret Pointer_Button2+AnyOfOrNone(all) {
+        action= PtrBtn(button=2);
+    };
+    interpret Pointer_Button3+AnyOfOrNone(all) {
+        action= PtrBtn(button=3);
+    };
+    interpret Pointer_DblClick_Dflt+AnyOfOrNone(all) {
+        action= PtrBtn(button=default,count=2);
+    };
+    interpret Pointer_DblClick1+AnyOfOrNone(all) {
+        action= PtrBtn(button=1,count=2);
+    };
+    interpret Pointer_DblClick2+AnyOfOrNone(all) {
+        action= PtrBtn(button=2,count=2);
+    };
+    interpret Pointer_DblClick3+AnyOfOrNone(all) {
+        action= PtrBtn(button=3,count=2);
+    };
+    interpret Pointer_Drag_Dflt+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=default,affect=both);
+    };
+    interpret Pointer_Drag1+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=1,affect=both);
+    };
+    interpret Pointer_Drag2+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=2,affect=both);
+    };
+    interpret Pointer_Drag3+AnyOfOrNone(all) {
+        action= LockPtrBtn(button=3,affect=both);
+    };
+    interpret Pointer_EnableKeys+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeys);
+    };
+    interpret Pointer_Accelerate+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeysAccel);
+    };
+    interpret Pointer_DfltBtnNext+AnyOfOrNone(all) {
+        action= SetPtrDflt(affect=button,button=+1);
+    };
+    interpret Pointer_DfltBtnPrev+AnyOfOrNone(all) {
+        action= SetPtrDflt(affect=button,button=-1);
+    };
+    interpret AccessX_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AccessXKeys);
+    };
+    interpret AccessX_Feedback_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AccessXFeedback);
+    };
+    interpret RepeatKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=RepeatKeys);
+    };
+    interpret SlowKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=SlowKeys);
+    };
+    interpret BounceKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=BounceKeys);
+    };
+    interpret StickyKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=StickyKeys);
+    };
+    interpret MouseKeys_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeys);
+    };
+    interpret MouseKeys_Accel_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=MouseKeysAccel);
+    };
+    interpret Overlay1_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=Overlay1);
+    };
+    interpret Overlay2_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=Overlay2);
+    };
+    interpret AudibleBell_Enable+AnyOfOrNone(all) {
+        action= LockControls(controls=AudibleBell);
+    };
+    interpret Terminate_Server+AnyOfOrNone(all) {
+        action= Terminate();
+    };
+    interpret Alt_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Alt,clearLocks);
+    };
+    interpret Alt_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Alt,clearLocks);
+    };
+    interpret Meta_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Meta,clearLocks);
+    };
+    interpret Meta_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Meta,clearLocks);
+    };
+    interpret Super_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Super,clearLocks);
+    };
+    interpret Super_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Super,clearLocks);
+    };
+    interpret Hyper_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Hyper,clearLocks);
+    };
+    interpret Hyper_R+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Hyper,clearLocks);
+    };
+    interpret Shift_L+AnyOfOrNone(all) {
+        action= SetMods(modifiers=Shift,clearLocks);
+    };
+    interpret XF86Switch_VT_1+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=1,!same);
+    };
+    interpret XF86Switch_VT_2+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=2,!same);
+    };
+    interpret XF86Switch_VT_3+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=3,!same);
+    };
+    interpret XF86Switch_VT_4+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=4,!same);
+    };
+    interpret XF86Switch_VT_5+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=5,!same);
+    };
+    interpret XF86Switch_VT_6+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=6,!same);
+    };
+    interpret XF86Switch_VT_7+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=7,!same);
+    };
+    interpret XF86Switch_VT_8+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=8,!same);
+    };
+    interpret XF86Switch_VT_9+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=9,!same);
+    };
+    interpret XF86Switch_VT_10+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=10,!same);
+    };
+    interpret XF86Switch_VT_11+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=11,!same);
+    };
+    interpret XF86Switch_VT_12+AnyOfOrNone(all) {
+        repeat= True;
+        action= SwitchScreen(screen=12,!same);
+    };
+    interpret XF86LogGrabInfo+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x50,data[1]=0x72,data[2]=0x47,data[3]=0x72,data[4]=0x62,data[5]=0x73,data[6]=0x00);
+    };
+    interpret XF86LogWindowTree+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x50,data[1]=0x72,data[2]=0x57,data[3]=0x69,data[4]=0x6e,data[5]=0x73,data[6]=0x00);
+    };
+    interpret XF86Next_VMode+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x2b,data[1]=0x56,data[2]=0x4d,data[3]=0x6f,data[4]=0x64,data[5]=0x65,data[6]=0x00);
+    };
+    interpret XF86Prev_VMode+AnyOfOrNone(all) {
+        repeat= True;
+        action= Private(type=0x86,data[0]=0x2d,data[1]=0x56,data[2]=0x4d,data[3]=0x6f,data[4]=0x64,data[5]=0x65,data[6]=0x00);
+    };
+    interpret ISO_Level5_Shift+AnyOfOrNone(all) {
+        action= SetMods(modifiers=LevelFive,clearLocks);
+    };
+    interpret ISO_Level5_Latch+AnyOfOrNone(all) {
+        action= LatchMods(modifiers=LevelFive,clearLocks,latchToLock);
+    };
+    interpret ISO_Level5_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=LevelFive);
+    };
+    interpret Caps_Lock+AnyOfOrNone(all) {
+        action= LockMods(modifiers=Lock);
+    };
+    interpret Any+Exactly(Lock) {
+        action= LockMods(modifiers=Lock);
+    };
+    interpret Any+AnyOf(all) {
+        action= SetMods(modifiers=modMapMods,clearLocks);
+    };
+    group 2 = AltGr;
+    group 3 = AltGr;
+    group 4 = AltGr;
+    indicator "Caps Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= Lock;
+    };
+    indicator "Num Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= NumLock;
+    };
+    indicator "Scroll Lock" {
+        whichModState= locked;
+        modifiers= ScrollLock;
+    };
+    indicator "Shift Lock" {
+        !allowExplicit;
+        whichModState= locked;
+        modifiers= Shift;
+    };
+    indicator "Group 2" {
+        !allowExplicit;
+        groups= 0xfe;
+    };
+    indicator "Mouse Keys" {
+        indicatorDrivesKeyboard;
+        controls= mouseKeys;
+    };
+};
+
+xkb_symbols "pc+us+inet(evdev)+ctrl(nocaps)+terminate(ctrl_alt_bksp)" {
+
+    name[group1]="English (US)";
+
+    key  <ESC> {         [          Escape ] };
+    key <AE01> {         [               1,          exclam ] };
+    key <AE02> {         [               2,              at ] };
+    key <AE03> {         [               3,      numbersign ] };
+    key <AE04> {         [               4,          dollar ] };
+    key <AE05> {         [               5,         percent ] };
+    key <AE06> {         [               6,     asciicircum ] };
+    key <AE07> {         [               7,       ampersand ] };
+    key <AE08> {         [               8,        asterisk ] };
+    key <AE09> {         [               9,       parenleft ] };
+    key <AE10> {         [               0,      parenright ] };
+    key <AE11> {         [           minus,      underscore ] };
+    key <AE12> {         [           equal,            plus ] };
+    key <BKSP> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [       BackSpace,       BackSpace,        NoSymbol,        NoSymbol, Terminate_Server ]
+    };
+    key  <TAB> {         [             Tab,    ISO_Left_Tab ] };
+    key <AD01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               q,               Q ]
+    };
+    key <AD02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               w,               W ]
+    };
+    key <AD03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               e,               E ]
+    };
+    key <AD04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               r,               R ]
+    };
+    key <AD05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               t,               T ]
+    };
+    key <AD06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               y,               Y ]
+    };
+    key <AD07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               u,               U ]
+    };
+    key <AD08> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               i,               I ]
+    };
+    key <AD09> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               o,               O ]
+    };
+    key <AD10> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               p,               P ]
+    };
+    key <AD11> {         [     bracketleft,       braceleft ] };
+    key <AD12> {         [    bracketright,      braceright ] };
+    key <RTRN> {         [          Return ] };
+    key <LCTL> {         [       Control_L ] };
+    key <AC01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               a,               A ]
+    };
+    key <AC02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               s,               S ]
+    };
+    key <AC03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               d,               D ]
+    };
+    key <AC04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               f,               F ]
+    };
+    key <AC05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               g,               G ]
+    };
+    key <AC06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               h,               H ]
+    };
+    key <AC07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               j,               J ]
+    };
+    key <AC08> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               k,               K ]
+    };
+    key <AC09> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               l,               L ]
+    };
+    key <AC10> {         [       semicolon,           colon ] };
+    key <AC11> {         [      apostrophe,        quotedbl ] };
+    key <TLDE> {         [           grave,      asciitilde ] };
+    key <LFSH> {         [         Shift_L ] };
+    key <BKSL> {         [       backslash,             bar ] };
+    key <AB01> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               z,               Z ]
+    };
+    key <AB02> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               x,               X ]
+    };
+    key <AB03> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               c,               C ]
+    };
+    key <AB04> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               v,               V ]
+    };
+    key <AB05> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               b,               B ]
+    };
+    key <AB06> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               n,               N ]
+    };
+    key <AB07> {
+        type= "ALPHABETIC",
+        symbols[Group1]= [               m,               M ]
+    };
+    key <AB08> {         [           comma,            less ] };
+    key <AB09> {         [          period,         greater ] };
+    key <AB10> {         [           slash,        question ] };
+    key <RTSH> {         [         Shift_R ] };
+    key <KPMU> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [     KP_Multiply,     KP_Multiply,     KP_Multiply,     KP_Multiply,   XF86ClearGrab ]
+    };
+    key <LALT> {         [           Alt_L,          Meta_L ] };
+    key <SPCE> {         [           space ] };
+    key <CAPS> {         [       Control_L,       Control_L ] };
+    key <FK01> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F1,              F1,              F1,              F1, XF86Switch_VT_1 ]
+    };
+    key <FK02> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F2,              F2,              F2,              F2, XF86Switch_VT_2 ]
+    };
+    key <FK03> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F3,              F3,              F3,              F3, XF86Switch_VT_3 ]
+    };
+    key <FK04> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F4,              F4,              F4,              F4, XF86Switch_VT_4 ]
+    };
+    key <FK05> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F5,              F5,              F5,              F5, XF86Switch_VT_5 ]
+    };
+    key <FK06> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F6,              F6,              F6,              F6, XF86Switch_VT_6 ]
+    };
+    key <FK07> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F7,              F7,              F7,              F7, XF86Switch_VT_7 ]
+    };
+    key <FK08> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F8,              F8,              F8,              F8, XF86Switch_VT_8 ]
+    };
+    key <FK09> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [              F9,              F9,              F9,              F9, XF86Switch_VT_9 ]
+    };
+    key <FK10> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F10,             F10,             F10,             F10, XF86Switch_VT_10 ]
+    };
+    key <NMLK> {         [        Num_Lock ] };
+    key <SCLK> {         [     Scroll_Lock ] };
+    key  <KP7> {         [         KP_Home,            KP_7 ] };
+    key  <KP8> {         [           KP_Up,            KP_8 ] };
+    key  <KP9> {         [        KP_Prior,            KP_9 ] };
+    key <KPSU> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [     KP_Subtract,     KP_Subtract,     KP_Subtract,     KP_Subtract,  XF86Prev_VMode ]
+    };
+    key  <KP4> {         [         KP_Left,            KP_4 ] };
+    key  <KP5> {         [        KP_Begin,            KP_5 ] };
+    key  <KP6> {         [        KP_Right,            KP_6 ] };
+    key <KPAD> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [          KP_Add,          KP_Add,          KP_Add,          KP_Add,  XF86Next_VMode ]
+    };
+    key  <KP1> {         [          KP_End,            KP_1 ] };
+    key  <KP2> {         [         KP_Down,            KP_2 ] };
+    key  <KP3> {         [         KP_Next,            KP_3 ] };
+    key  <KP0> {         [       KP_Insert,            KP_0 ] };
+    key <KPDL> {         [       KP_Delete,      KP_Decimal ] };
+    key <LVL3> {         [ ISO_Level3_Shift ] };
+    key <LSGT> {
+        type= "FOUR_LEVEL",
+        symbols[Group1]= [            less,         greater,             bar,       brokenbar ]
+    };
+    key <FK11> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F11,             F11,             F11,             F11, XF86Switch_VT_11 ]
+    };
+    key <FK12> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [             F12,             F12,             F12,             F12, XF86Switch_VT_12 ]
+    };
+    key <KATA> {         [        Katakana ] };
+    key <HIRA> {         [        Hiragana ] };
+    key <HENK> {         [     Henkan_Mode ] };
+    key <HKTG> {         [ Hiragana_Katakana ] };
+    key <MUHE> {         [        Muhenkan ] };
+    key <KPEN> {         [        KP_Enter ] };
+    key <RCTL> {         [       Control_R ] };
+    key <KPDV> {
+        type= "CTRL+ALT",
+        symbols[Group1]= [       KP_Divide,       KP_Divide,       KP_Divide,       KP_Divide,      XF86Ungrab ]
+    };
+    key <PRSC> {
+        type= "PC_ALT_LEVEL2",
+        symbols[Group1]= [           Print,         Sys_Req ]
+    };
+    key <RALT> {
+        type= "TWO_LEVEL",
+        symbols[Group1]= [           Alt_R,          Meta_R ]
+    };
+    key <LNFD> {         [        Linefeed ] };
+    key <HOME> {         [            Home ] };
+    key   <UP> {         [              Up ] };
+    key <PGUP> {         [           Prior ] };
+    key <LEFT> {         [            Left ] };
+    key <RGHT> {         [           Right ] };
+    key  <END> {         [             End ] };
+    key <DOWN> {         [            Down ] };
+    key <PGDN> {         [            Next ] };
+    key  <INS> {         [          Insert ] };
+    key <DELE> {         [          Delete ] };
+    key <MUTE> {         [   XF86AudioMute ] };
+    key <VOL-> {         [ XF86AudioLowerVolume ] };
+    key <VOL+> {         [ XF86AudioRaiseVolume ] };
+    key <POWR> {         [    XF86PowerOff ] };
+    key <KPEQ> {         [        KP_Equal ] };
+    key <I126> {         [       plusminus ] };
+    key <PAUS> {
+        type= "PC_CONTROL_LEVEL2",
+        symbols[Group1]= [           Pause,           Break ]
+    };
+    key <I128> {         [     XF86LaunchA ] };
+    key <I129> {         [      KP_Decimal,      KP_Decimal ] };
+    key <HNGL> {         [          Hangul ] };
+    key <HJCV> {         [    Hangul_Hanja ] };
+    key <LWIN> {         [         Super_L ] };
+    key <RWIN> {         [         Super_R ] };
+    key <COMP> {         [            Menu ] };
+    key <STOP> {         [          Cancel ] };
+    key <AGAI> {         [            Redo ] };
+    key <PROP> {         [        SunProps ] };
+    key <UNDO> {         [            Undo ] };
+    key <FRNT> {         [        SunFront ] };
+    key <COPY> {         [        XF86Copy ] };
+    key <OPEN> {         [        XF86Open ] };
+    key <PAST> {         [       XF86Paste ] };
+    key <FIND> {         [            Find ] };
+    key  <CUT> {         [         XF86Cut ] };
+    key <HELP> {         [            Help ] };
+    key <I147> {         [      XF86MenuKB ] };
+    key <I148> {         [  XF86Calculator ] };
+    key <I150> {         [       XF86Sleep ] };
+    key <I151> {         [      XF86WakeUp ] };
+    key <I152> {         [    XF86Explorer ] };
+    key <I153> {         [        XF86Send ] };
+    key <I155> {         [        XF86Xfer ] };
+    key <I156> {         [     XF86Launch1 ] };
+    key <I157> {         [     XF86Launch2 ] };
+    key <I158> {         [         XF86WWW ] };
+    key <I159> {         [         XF86DOS ] };
+    key <I160> {         [ XF86ScreenSaver ] };
+    key <I161> {         [ XF86RotateWindows ] };
+    key <I162> {         [    XF86TaskPane ] };
+    key <I163> {         [        XF86Mail ] };
+    key <I164> {         [   XF86Favorites ] };
+    key <I165> {         [  XF86MyComputer ] };
+    key <I166> {         [        XF86Back ] };
+    key <I167> {         [     XF86Forward ] };
+    key <I169> {         [       XF86Eject ] };
+    key <I170> {         [       XF86Eject,       XF86Eject ] };
+    key <I171> {         [   XF86AudioNext ] };
+    key <I172> {         [   XF86AudioPlay,  XF86AudioPause ] };
+    key <I173> {         [   XF86AudioPrev ] };
+    key <I174> {         [   XF86AudioStop,       XF86Eject ] };
+    key <I175> {         [ XF86AudioRecord ] };
+    key <I176> {         [ XF86AudioRewind ] };
+    key <I177> {         [       XF86Phone ] };
+    key <I179> {         [       XF86Tools ] };
+    key <I180> {         [    XF86HomePage ] };
+    key <I181> {         [      XF86Reload ] };
+    key <I182> {         [       XF86Close ] };
+    key <I185> {         [    XF86ScrollUp ] };
+    key <I186> {         [  XF86ScrollDown ] };
+    key <I187> {         [       parenleft ] };
+    key <I188> {         [      parenright ] };
+    key <I189> {         [         XF86New ] };
+    key <I190> {         [            Redo ] };
+    key <FK13> {         [       XF86Tools ] };
+    key <FK14> {         [     XF86Launch5 ] };
+    key <FK15> {         [     XF86Launch6 ] };
+    key <FK16> {         [     XF86Launch7 ] };
+    key <FK17> {         [     XF86Launch8 ] };
+    key <FK18> {         [     XF86Launch9 ] };
+    key <FK20> {         [ XF86AudioMicMute ] };
+    key <FK21> {         [ XF86TouchpadToggle ] };
+    key <FK22> {         [  XF86TouchpadOn ] };
+    key <FK23> {         [ XF86TouchpadOff ] };
+    key <MDSW> {         [     Mode_switch ] };
+    key  <ALT> {         [        NoSymbol,           Alt_L ] };
+    key <META> {         [        NoSymbol,          Meta_L ] };
+    key <SUPR> {         [        NoSymbol,         Super_L ] };
+    key <HYPR> {         [        NoSymbol,         Hyper_L ] };
+    key <I208> {         [   XF86AudioPlay ] };
+    key <I209> {         [  XF86AudioPause ] };
+    key <I210> {         [     XF86Launch3 ] };
+    key <I211> {         [     XF86Launch4 ] };
+    key <I212> {         [     XF86LaunchB ] };
+    key <I213> {         [     XF86Suspend ] };
+    key <I214> {         [       XF86Close ] };
+    key <I215> {         [   XF86AudioPlay ] };
+    key <I216> {         [ XF86AudioForward ] };
+    key <I218> {         [           Print ] };
+    key <I220> {         [      XF86WebCam ] };
+    key <I223> {         [        XF86Mail ] };
+    key <I224> {         [   XF86Messenger ] };
+    key <I225> {         [      XF86Search ] };
+    key <I226> {         [          XF86Go ] };
+    key <I227> {         [     XF86Finance ] };
+    key <I228> {         [        XF86Game ] };
+    key <I229> {         [        XF86Shop ] };
+    key <I231> {         [          Cancel ] };
+    key <I232> {         [ XF86MonBrightnessDown ] };
+    key <I233> {         [ XF86MonBrightnessUp ] };
+    key <I234> {         [  XF86AudioMedia ] };
+    key <I235> {         [     XF86Display ] };
+    key <I236> {         [ XF86KbdLightOnOff ] };
+    key <I237> {         [ XF86KbdBrightnessDown ] };
+    key <I238> {         [ XF86KbdBrightnessUp ] };
+    key <I239> {         [        XF86Send ] };
+    key <I240> {         [       XF86Reply ] };
+    key <I241> {         [ XF86MailForward ] };
+    key <I242> {         [        XF86Save ] };
+    key <I243> {         [   XF86Documents ] };
+    key <I244> {         [     XF86Battery ] };
+    key <I245> {         [   XF86Bluetooth ] };
+    key <I246> {         [        XF86WLAN ] };
+    modifier_map Control { <LCTL> };
+    modifier_map Shift { <LFSH> };
+    modifier_map Shift { <RTSH> };
+    modifier_map Mod1 { <LALT> };
+    modifier_map Control { <CAPS> };
+    modifier_map Mod2 { <NMLK> };
+    modifier_map Mod5 { <LVL3> };
+    modifier_map Control { <RCTL> };
+    modifier_map Mod1 { <RALT> };
+    modifier_map Mod4 { <LWIN> };
+    modifier_map Mod4 { <RWIN> };
+    modifier_map Mod5 { <MDSW> };
+    modifier_map Mod1 { <META> };
+    modifier_map Mod4 { <SUPR> };
+    modifier_map Mod4 { <HYPR> };
+};
+
+xkb_geometry "pc(pc105)" {
+
+    width=       470;
+    height=      180;
+
+    alias <AC00> = <CAPS>;
+    alias <AA00> = <LCTL>;
+
+    baseColor=   "white";
+    labelColor=  "black";
+    xfont=       "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+    description= "Generic 105";
+
+    shape "NORM" {
+        corner= 1,
+        { [  18,  18 ] },
+        { [   2,   1 ], [  16,  16 ] }
+    };
+    shape "BKSP" {
+        corner= 1,
+        { [  38,  18 ] },
+        { [   2,   1 ], [  36,  16 ] }
+    };
+    shape "TABK" {
+        corner= 1,
+        { [  28,  18 ] },
+        { [   2,   1 ], [  26,  16 ] }
+    };
+    shape "BKSL" {
+        corner= 1,
+        { [  28,  18 ] },
+        { [   2,   1 ], [  26,  16 ] }
+    };
+    shape "RTRN" {
+        corner= 1,
+        { [   0,   0 ], [  28,   0 ], [  28,  37 ], [   5,  37 ],
+          [   5,  18 ], [   0,  18 ] },
+        { [   2,   1 ], [  26,   1 ], [  26,  35 ], [   7,  35 ],
+          [   7,  16 ], [   2,  16 ] },
+        approx= { [   5,   0 ], [  28,  37 ] }
+    };
+    shape "CAPS" {
+        corner= 1,
+        { [  33,  18 ] },
+        { [   2,   1 ], [  31,  16 ] }
+    };
+    shape "LFSH" {
+        corner= 1,
+        { [  25,  18 ] },
+        { [   2,   1 ], [  23,  16 ] }
+    };
+    shape "RTSH" {
+        corner= 1,
+        { [  50,  18 ] },
+        { [   2,   1 ], [  48,  16 ] }
+    };
+    shape "MODK" {
+        corner= 1,
+        { [  27,  18 ] },
+        { [   2,   1 ], [  25,  16 ] }
+    };
+    shape "SMOD" {
+        corner= 1,
+        { [  23,  18 ] },
+        { [   2,   1 ], [  21,  16 ] }
+    };
+    shape "SPCE" {
+        corner= 1,
+        { [ 113,  18 ] },
+        { [   2,   1 ], [ 111,  16 ] }
+    };
+    shape "KP0" {
+        corner= 1,
+        { [  37,  18 ] },
+        { [   2,   1 ], [  35,  16 ] }
+    };
+    shape "KPAD" {
+        corner= 1,
+        { [  18,  37 ] },
+        { [   2,   1 ], [  16,  35 ] }
+    };
+    shape "LEDS" { { [  75,  20 ] } };
+    shape "LED" { { [   5,   1 ] } };
+    section "Function" {
+        key.color= "grey20";
+        priority=  7;
+        top=       22;
+        left=      19;
+        width=     351;
+        height=    19;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                {  <ESC>, "NORM",   1 },
+                { <FK01>, "NORM",  20, color="white" },
+                { <FK02>, "NORM",   1, color="white" },
+                { <FK03>, "NORM",   1, color="white" },
+                { <FK04>, "NORM",   1, color="white" },
+                { <FK05>, "NORM",  11, color="white" },
+                { <FK06>, "NORM",   1, color="white" },
+                { <FK07>, "NORM",   1, color="white" },
+                { <FK08>, "NORM",   1, color="white" },
+                { <FK09>, "NORM",  11, color="white" },
+                { <FK10>, "NORM",   1, color="white" },
+                { <FK11>, "NORM",   1, color="white" },
+                { <FK12>, "NORM",   1, color="white" },
+                { <PRSC>, "NORM",   8, color="white" },
+                { <SCLK>, "NORM",   1, color="white" },
+                { <PAUS>, "NORM",   1, color="white" }
+            };
+        };
+    }; // End of "Function" section
+
+    section "Alpha" {
+        key.color= "white";
+        priority=  8;
+        top=       61;
+        left=      19;
+        width=     287;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                { <TLDE>, "NORM",   1 }, { <AE01>, "NORM",   1 },
+                { <AE02>, "NORM",   1 }, { <AE03>, "NORM",   1 },
+                { <AE04>, "NORM",   1 }, { <AE05>, "NORM",   1 },
+                { <AE06>, "NORM",   1 }, { <AE07>, "NORM",   1 },
+                { <AE08>, "NORM",   1 }, { <AE09>, "NORM",   1 },
+                { <AE10>, "NORM",   1 }, { <AE11>, "NORM",   1 },
+                { <AE12>, "NORM",   1 },
+                { <BKSP>, "BKSP",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                {  <TAB>, "TABK",   1, color="grey20" },
+                { <AD01>, "NORM",   1 }, { <AD02>, "NORM",   1 },
+                { <AD03>, "NORM",   1 }, { <AD04>, "NORM",   1 },
+                { <AD05>, "NORM",   1 }, { <AD06>, "NORM",   1 },
+                { <AD07>, "NORM",   1 }, { <AD08>, "NORM",   1 },
+                { <AD09>, "NORM",   1 }, { <AD10>, "NORM",   1 },
+                { <AD11>, "NORM",   1 }, { <AD12>, "NORM",   1 },
+                { <RTRN>, "RTRN",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  39;
+            left= 1;
+            keys {
+                { <CAPS>, "CAPS",   1, color="grey20" },
+                { <AC01>, "NORM",   1 }, { <AC02>, "NORM",   1 },
+                { <AC03>, "NORM",   1 }, { <AC04>, "NORM",   1 },
+                { <AC05>, "NORM",   1 }, { <AC06>, "NORM",   1 },
+                { <AC07>, "NORM",   1 }, { <AC08>, "NORM",   1 },
+                { <AC09>, "NORM",   1 }, { <AC10>, "NORM",   1 },
+                { <AC11>, "NORM",   1 }, { <BKSL>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  58;
+            left= 1;
+            keys {
+                { <LFSH>, "LFSH",   1, color="grey20" },
+                { <LSGT>, "NORM",   1 }, { <AB01>, "NORM",   1 },
+                { <AB02>, "NORM",   1 }, { <AB03>, "NORM",   1 },
+                { <AB04>, "NORM",   1 }, { <AB05>, "NORM",   1 },
+                { <AB06>, "NORM",   1 }, { <AB07>, "NORM",   1 },
+                { <AB08>, "NORM",   1 }, { <AB09>, "NORM",   1 },
+                { <AB10>, "NORM",   1 },
+                { <RTSH>, "RTSH",   1, color="grey20" }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                { <LCTL>, "MODK",   1, color="grey20" },
+                { <LWIN>, "SMOD",   1, color="grey20" },
+                { <LALT>, "SMOD",   1, color="grey20" },
+                { <SPCE>, "SPCE",   1 },
+                { <RALT>, "SMOD",   1, color="grey20" },
+                { <RWIN>, "SMOD",   1, color="grey20" },
+                { <MENU>, "SMOD",   1, color="grey20" },
+                { <RCTL>, "SMOD",   1, color="grey20" }
+            };
+        };
+    }; // End of "Alpha" section
+
+    section "Editing" {
+        key.color= "grey20";
+        priority=  9;
+        top=       61;
+        left=      312;
+        width=     58;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                {  <INS>, "NORM",   1 }, { <HOME>, "NORM",   1 },
+                { <PGUP>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                { <DELE>, "NORM",   1 }, {  <END>, "NORM",   1 },
+                { <PGDN>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  58;
+            left= 20;
+            keys {
+                {   <UP>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                { <LEFT>, "NORM",   1 }, { <DOWN>, "NORM",   1 },
+                { <RGHT>, "NORM",   1 }
+            };
+        };
+    }; // End of "Editing" section
+
+    section "Keypad" {
+        key.color= "grey20";
+        priority=  10;
+        top=       61;
+        left=      376;
+        width=     77;
+        height=    95;
+        row {
+            top=  1;
+            left= 1;
+            keys {
+                { <NMLK>, "NORM",   1 }, { <KPDV>, "NORM",   1 },
+                { <KPMU>, "NORM",   1 }, { <KPSU>, "NORM",   1 }
+            };
+        };
+        row {
+            top=  20;
+            left= 1;
+            keys {
+                {  <KP7>, "NORM",   1, color="white" },
+                {  <KP8>, "NORM",   1, color="white" },
+                {  <KP9>, "NORM",   1, color="white" },
+                { <KPAD>, "KPAD",   1 }
+            };
+        };
+        row {
+            top=  39;
+            left= 1;
+            keys {
+                {  <KP4>, "NORM",   1, color="white" },
+                {  <KP5>, "NORM",   1, color="white" },
+                {  <KP6>, "NORM",   1, color="white" }
+            };
+        };
+        row {
+            top=  58;
+            left= 1;
+            keys {
+                {  <KP1>, "NORM",   1, color="white" },
+                {  <KP2>, "NORM",   1, color="white" },
+                {  <KP3>, "NORM",   1, color="white" },
+                { <KPEN>, "KPAD",   1 }
+            };
+        };
+        row {
+            top=  77;
+            left= 1;
+            keys {
+                {  <KP0>, "KP0",   1, color="white" },
+                { <KPDL>, "NORM",   1, color="white" }
+            };
+        };
+    }; // End of "Keypad" section
+
+    solid "LedPanel" {
+        top=      22;
+        left=     377;
+        priority= 0;
+        color= "grey10";
+        shape= "LEDS";
+    };
+    indicator "Num Lock" {
+        top=      37;
+        left=     382;
+        priority= 1;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    indicator "Caps Lock" {
+        top=      37;
+        left=     407;
+        priority= 2;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    indicator "Scroll Lock" {
+        top=      37;
+        left=     433;
+        priority= 3;
+        onColor= "green";
+        offColor= "green30";
+        shape= "LED";
+    };
+    text "NumLockLabel" {
+        top=      25;
+        left=     378;
+        priority= 4;
+        width=  19.8;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Num\nLock";
+    };
+    text "CapsLockLabel" {
+        top=      25;
+        left=     403;
+        priority= 5;
+        width=  26.4;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Caps\nLock";
+    };
+    text "ScrollLockLabel" {
+        top=      25;
+        left=     428;
+        priority= 6;
+        width=  39.6;
+        height=  10;
+        XFont= "-*-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1";
+        text=  "Scroll\nLock";
+    };
+};
+
+};