From: Ian Kelling Date: Mon, 22 Jan 2018 02:09:19 +0000 (-0500) Subject: Merge independent repo of config files X-Git-Url: https://iankelling.org/git/?a=commitdiff_plain;h=eb9b839bb5a91c60cc4f6eb9d7e38ffbf73f0e90;hp=dd2ab37728aeaf8b887d128c50601fb5d9bd8841;p=distro-setup Merge independent repo of config files They were really tied together, so bring them into the same repo --- diff --git a/.Xkeymap b/.Xkeymap new file mode 100644 index 0000000..b0d4293 --- /dev/null +++ b/.Xkeymap @@ -0,0 +1,1761 @@ +xkb_keymap { +xkb_keycodes "evdev+aliases(qwerty)" { + minimum = 8; + maximum = 255; + = 9; + = 10; + = 11; + = 12; + = 13; + = 14; + = 15; + = 16; + = 17; + = 18; + = 19; + = 20; + = 21; + = 22; + = 23; + = 24; + = 25; + = 26; + = 27; + = 28; + = 29; + = 30; + = 31; + = 32; + = 33; + = 34; + = 35; + = 36; + = 37; + = 38; + = 39; + = 40; + = 41; + = 42; + = 43; + = 44; + = 45; + = 46; + = 47; + = 48; + = 49; + = 50; + = 51; + = 52; + = 53; + = 54; + = 55; + = 56; + = 57; + = 58; + = 59; + = 60; + = 61; + = 62; + = 63; + = 64; + = 65; + = 66; + = 67; + = 68; + = 69; + = 70; + = 71; + = 72; + = 73; + = 74; + = 75; + = 76; + = 77; + = 78; + = 79; + = 80; + = 81; + = 82; + = 83; + = 84; + = 85; + = 86; + = 87; + = 88; + = 89; + = 90; + = 91; + = 92; + = 94; + = 95; + = 96; + = 97; + = 98; + = 99; + = 100; + = 101; + = 102; + = 103; + = 104; + = 105; + = 106; + = 107; + = 108; + = 109; + = 110; + = 111; + = 112; + = 113; + = 114; + = 115; + = 116; + = 117; + = 118; + = 119; + = 120; + = 121; + = 122; + = 123; + = 124; + = 125; + = 126; + = 127; + = 128; + = 129; + = 130; + = 131; + = 132; + = 133; + = 134; + = 135; + = 136; + = 137; + = 138; + = 139; + = 140; + = 141; + = 142; + = 143; + = 144; + = 145; + = 146; + = 147; + = 148; + = 149; + = 150; + = 151; + = 152; + = 153; + = 154; + = 155; + = 156; + = 157; + = 158; + = 159; + = 160; + = 161; + = 162; + = 163; + = 164; + = 165; + = 166; + = 167; + = 168; + = 169; + = 170; + = 171; + = 172; + = 173; + = 174; + = 175; + = 176; + = 177; + = 178; + = 179; + = 180; + = 181; + = 182; + = 183; + = 184; + = 185; + = 186; + = 187; + = 188; + = 189; + = 190; + = 191; + = 192; + = 193; + = 194; + = 195; + = 196; + = 197; + = 198; + = 199; + = 200; + = 201; + = 202; + = 203; + = 204; + = 205; + = 206; + = 207; + = 208; + = 209; + = 210; + = 211; + = 212; + = 213; + = 214; + = 215; + = 216; + = 217; + = 218; + = 219; + = 220; + = 221; + = 222; + = 223; + = 224; + = 225; + = 226; + = 227; + = 228; + = 229; + = 230; + = 231; + = 232; + = 233; + = 234; + = 235; + = 236; + = 237; + = 238; + = 239; + = 240; + = 241; + = 242; + = 243; + = 244; + = 245; + = 246; + = 247; + = 248; + = 249; + = 250; + = 251; + = 252; + = 253; + 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 = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; +}; + +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 { [ Escape ] }; + key { [ 1, exclam ] }; + key { [ 2, at ] }; + key { [ 3, numbersign ] }; + key { [ 4, dollar ] }; + key { [ 5, percent ] }; + key { [ 6, asciicircum ] }; + key { [ 7, ampersand ] }; + key { [ 8, asterisk ] }; + key { [ 9, parenleft ] }; + key { [ 0, parenright ] }; + key { [ minus, underscore ] }; + key { [ equal, plus ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ BackSpace, BackSpace, NoSymbol, NoSymbol, Terminate_Server ] + }; + key { [ Tab, ISO_Left_Tab ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ q, Q ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ w, W ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ e, E ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ r, R ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ t, T ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ y, Y ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ u, U ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ i, I ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ o, O ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ p, P ] + }; + key { [ bracketleft, braceleft ] }; + key { [ bracketright, braceright ] }; + key { [ Return ] }; + key { [ Control_L ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ a, A ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ s, S ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ d, D ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ f, F ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ g, G ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ h, H ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ j, J ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ k, K ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ l, L ] + }; + key { [ semicolon, colon ] }; + key { [ apostrophe, quotedbl ] }; + key { [ grave, asciitilde ] }; + key { [ Shift_L ] }; + key { [ backslash, bar ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ z, Z ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ x, X ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ c, C ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ v, V ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ b, B ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ n, N ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ m, M ] + }; + key { [ comma, less ] }; + key { [ period, greater ] }; + key { [ slash, question ] }; + key { [ Shift_R ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ underscore, grave, KP_Multiply, KP_Multiply, XF86ClearGrab ] + }; + key { [ Alt_L, Meta_L ] }; + key { [ space ] }; + key { [ Control_L, Control_L ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F1, F1, F1, F1, XF86Switch_VT_1 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F2, F2, F2, F2, XF86Switch_VT_2 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F3, F3, F3, F3, XF86Switch_VT_3 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F4, F4, F4, F4, XF86Switch_VT_4 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F5, F5, F5, F5, XF86Switch_VT_5 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F6, F6, F6, F6, XF86Switch_VT_6 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F7, F7, F7, F7, XF86Switch_VT_7 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F8, F8, F8, F8, XF86Switch_VT_8 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F9, F9, F9, F9, XF86Switch_VT_9 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ Pointer_Button1, Pointer_Button1, Pointer_Button1, Pointer_Button1, XF86Switch_VT_10 ] + }; + key { [ Num_Lock ] }; + key { [ Scroll_Lock ] }; + key { [ KP_Home, KP_7 ] }; + key { [ KP_Up, KP_8 ] }; + key { [ KP_Prior, KP_9 ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ asterisk, grave, KP_Subtract, KP_Subtract, XF86Prev_VMode ] + }; + key { [ KP_Left, KP_4 ] }; + key { [ KP_Begin, KP_5 ] }; + key { [ KP_Right, KP_6 ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Add, asciitilde, KP_Add, KP_Add, XF86Next_VMode ] + }; + key { [ KP_End, KP_1 ] }; + key { [ KP_Down, KP_2 ] }; + key { [ KP_Next, KP_3 ] }; + key { [ KP_Insert, KP_0 ] }; + key { [ KP_Delete, KP_Decimal ] }; + key { [ ISO_Level3_Shift ] }; + key { + type= "FOUR_LEVEL", + symbols[Group1]= [ less, greater, bar, brokenbar ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F11, F11, F11, F11, XF86Switch_VT_11 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F12, F12, F12, F12, XF86Switch_VT_12 ] + }; + key { [ Katakana ] }; + key { [ Hiragana ] }; + key { [ Henkan_Mode ] }; + key { [ Hiragana_Katakana ] }; + key { [ Muhenkan ] }; + key { [ KP_Enter ] }; + key { [ Control_R ] }; + key { type= "CTRL+ALT", + symbols[Group1]= [ parenleft, KP_Equal, KP_Equal, KP_Equal, XF86Ungrab ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ parenright, KP_Divide, KP_Divide, KP_Divide, XF86Ungrab ] + }; + key { + type= "PC_ALT_LEVEL2", + symbols[Group1]= [ Print, Sys_Req ] + }; + key { + type= "TWO_LEVEL", + symbols[Group1]= [ Alt_R, Meta_R ] + }; + key { [ Linefeed ] }; + key { [ Home ] }; + key { [ Up ] }; + key { [ Pointer_Button2 ] }; + key { [ Left ] }; + key { [ Right ] }; + key { [ End ] }; + key { [ Down ] }; + key { [ Pointer_Button3 ] }; + key { [ Insert ] }; + key { [ Delete ] }; + key { [ XF86AudioMute ] }; + key { [ XF86AudioLowerVolume ] }; + key { [ XF86AudioRaiseVolume ] }; + key { [ XF86PowerOff ] }; + key { [ plusminus ] }; + key { + type= "PC_CONTROL_LEVEL2", + symbols[Group1]= [ Pause, Break ] + }; + key { [ XF86LaunchA ] }; + key { [ KP_Decimal, KP_Decimal ] }; + key { [ Hangul ] }; + key { [ Hangul_Hanja ] }; + key { [ Super_L ] }; + key { [ Super_R ] }; + key { [ Menu ] }; + key { [ Cancel ] }; + key { [ Redo ] }; + key { [ SunProps ] }; + key { [ Undo ] }; + key { [ SunFront ] }; + key { [ XF86Copy ] }; + key { [ XF86Open ] }; + key { [ XF86Paste ] }; + key { [ Find ] }; + key { [ XF86Cut ] }; + key { [ Help ] }; + key { [ XF86MenuKB ] }; + key { [ XF86Calculator ] }; + key { [ XF86Sleep ] }; + key { [ XF86WakeUp ] }; + key { [ XF86Explorer ] }; + key { [ XF86Send ] }; + key { [ XF86Xfer ] }; + key { [ XF86Launch1 ] }; + key { [ XF86Launch2 ] }; + key { [ XF86WWW ] }; + key { [ XF86DOS ] }; + key { [ XF86ScreenSaver ] }; + key { [ XF86RotateWindows ] }; + key { [ XF86TaskPane ] }; + key { [ XF86Mail ] }; + key { [ XF86Favorites ] }; + key { [ XF86MyComputer ] }; + key { [ XF86Back ] }; + key { [ XF86Forward ] }; + key { [ XF86Eject ] }; + key { [ XF86Eject, XF86Eject ] }; + key { [ XF86AudioNext ] }; + key { [ XF86AudioPlay, XF86AudioPause ] }; + key { [ XF86AudioPrev ] }; + key { [ XF86AudioStop, XF86Eject ] }; + key { [ XF86AudioRecord ] }; + key { [ XF86AudioRewind ] }; + key { [ XF86Phone ] }; + key { [ XF86Tools ] }; + key { [ XF86HomePage ] }; + key { [ XF86Reload ] }; + key { [ XF86Close ] }; + key { [ XF86ScrollUp ] }; + key { [ XF86ScrollDown ] }; + key { [ parenleft ] }; + key { [ parenright ] }; + key { [ XF86New ] }; + key { [ Redo ] }; + key { [ XF86Tools ] }; + key { [ XF86Launch5 ] }; + key { [ XF86Launch6 ] }; + key { [ XF86Launch7 ] }; + key { [ XF86Launch8 ] }; + key { [ XF86Launch9 ] }; + key { [ XF86AudioMicMute ] }; + key { [ XF86TouchpadToggle ] }; + key { [ XF86TouchpadOn ] }; + key { [ XF86TouchpadOff ] }; + key { [ Mode_switch ] }; + key { [ NoSymbol, Alt_L ] }; + key { [ NoSymbol, Meta_L ] }; + key { [ NoSymbol, Super_L ] }; + key { [ NoSymbol, Hyper_L ] }; + key { [ XF86AudioPlay ] }; + key { [ XF86AudioPause ] }; + key { [ XF86Launch3 ] }; + key { [ XF86Launch4 ] }; + key { [ XF86LaunchB ] }; + key { [ XF86Suspend ] }; + key { [ XF86Close ] }; + key { [ XF86AudioPlay ] }; + key { [ XF86AudioForward ] }; + key { [ Print ] }; + key { [ XF86WebCam ] }; + key { [ XF86Mail ] }; + key { [ XF86Messenger ] }; + key { [ XF86Search ] }; + key { [ XF86Go ] }; + key { [ XF86Finance ] }; + key { [ XF86Game ] }; + key { [ XF86Shop ] }; + key { [ Cancel ] }; + key { [ XF86MonBrightnessDown ] }; + key { [ XF86MonBrightnessUp ] }; + key { [ XF86AudioMedia ] }; + key { [ XF86Display ] }; + key { [ XF86KbdLightOnOff ] }; + key { [ XF86KbdBrightnessDown ] }; + key { [ XF86KbdBrightnessUp ] }; + key { [ XF86Send ] }; + key { [ XF86Reply ] }; + key { [ XF86MailForward ] }; + key { [ XF86Save ] }; + key { [ XF86Documents ] }; + key { [ XF86Battery ] }; + key { [ XF86Bluetooth ] }; + key { [ XF86WLAN ] }; + modifier_map Control { }; + modifier_map Shift { }; + modifier_map Shift { }; + modifier_map Mod1 { }; + modifier_map Control { }; + modifier_map Mod2 { }; + modifier_map Mod5 { }; + modifier_map Control { }; + modifier_map Mod1 { }; + modifier_map Mod4 { }; + modifier_map Mod4 { }; + modifier_map Mod5 { }; + modifier_map Mod1 { }; + modifier_map Mod4 { }; + modifier_map Mod4 { }; +}; + +xkb_geometry "pc(pc105)" { + + width= 470; + height= 180; + + alias = ; + alias = ; + + 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 { + { , "NORM", 1 }, + { , "NORM", 20, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 11, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 11, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 8, color="white" }, + { , "NORM", 1, color="white" }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, + { , "BKSP", 1, color="grey20" } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "TABK", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "RTRN", 1, color="grey20" } + }; + }; + row { + top= 39; + left= 1; + keys { + { , "CAPS", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 } + }; + }; + row { + top= 58; + left= 1; + keys { + { , "LFSH", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, + { , "RTSH", 1, color="grey20" } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "MODK", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SPCE", 1 }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 } + }; + }; + row { + top= 58; + left= 20; + keys { + { , "NORM", 1 } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "KPAD", 1 } + }; + }; + row { + top= 39; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" } + }; + }; + row { + top= 58; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "KPAD", 1 } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "KP0", 1, color="white" }, + { , "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 index 0000000..9cddbd9 --- /dev/null +++ b/.Xresources @@ -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 index 0000000..d3a6a8f --- /dev/null +++ b/.bash_profile @@ -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 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 index 0000000..518294f --- /dev/null +++ b/.dir_colors @@ -0,0 +1,482 @@ +# LS_COLORS +# Maintainer: Magnus Woldrich +# 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 index 0000000..0e2693d --- /dev/null +++ b/.eclipse-dictionary @@ -0,0 +1 @@ +downside diff --git a/.gdbinit b/.gdbinit new file mode 100644 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 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 index 0000000..182dcb9 --- /dev/null +++ b/.git_template/hooks/pre-commit @@ -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 </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 index 0000000..c4fd69d --- /dev/null +++ b/.gitconfig @@ -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 index 0000000..d52d4c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/t.org +/old-unused +/.emacs.d diff --git a/.gitignore_global b/.gitignore_global new file mode 100644 index 0000000..5bc0376 --- /dev/null +++ b/.gitignore_global @@ -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 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! * diff --git a/.hunspell_en_US b/.hunspell_en_US new file mode 100644 index 0000000..d1aa599 --- /dev/null +++ b/.hunspell_en_US @@ -0,0 +1,2 @@ +timestamp +mediawiki diff --git a/.hushlogin b/.hushlogin new file mode 100644 index 0000000..e69de29 diff --git a/.inputrc b/.inputrc new file mode 100644 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 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 to set a mark and ` 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 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 index 0000000..25ede45 --- /dev/null +++ b/.libao @@ -0,0 +1 @@ +default_driver=pulse diff --git a/.mailcap b/.mailcap new file mode 100644 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 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 index 0000000..cb1bf6b --- /dev/null +++ b/.redshift.conf @@ -0,0 +1,3 @@ +[redshift] +temp-day=5900 +temp-night=5400 diff --git a/.screenrc b/.screenrc new file mode 100644 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 index 0000000..d68b384 --- /dev/null +++ b/.vim/.netrwhist @@ -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 index 0000000..5988183 --- /dev/null +++ b/.vim/after/syntax/c.vim @@ -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 index 0000000..81a8b0b --- /dev/null +++ b/.vim/autoload/SyntaxAttr.vim @@ -0,0 +1,65 @@ +" EXAMPLE SETUP +" +" Show the syntax group name of the item under cursor. +" map -a :call SyntaxAttr() + +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 = "" + endif + echo message + echohl None +endfunction diff --git a/.vim/colors/ir_black.vim b/.vim/colors/ir_black.vim new file mode 100644 index 0000000..6dc402d --- /dev/null +++ b/.vim/colors/ir_black.vim @@ -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 index 0000000..7d47ce1 --- /dev/null +++ b/.vim/colors/leo.vim @@ -0,0 +1,115 @@ +" Vim color file +" Maintainer: Lorenzo Leonini +" 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 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 index 0000000..4d0e247 --- /dev/null +++ b/.vim/colors/nightsky.vim @@ -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 index 0000000..c01b060 --- /dev/null +++ b/.vim/colors/refactor.vim @@ -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 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 index 0000000..1a909a4 --- /dev/null +++ b/.vim/doc/bufexplorer.txt @@ -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 + + \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: + + Opens the buffer that is under the cursor into the current + window. + Toggle help information. + Opens the buffer that is under the cursor into the current + window. + 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 + 14_StartBufExplorer..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’s 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 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 key mapping was causing the buffer + explored to close prematurely when vim was run in an xterm. The + 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 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 be and 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 + +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 index 0000000..6c0188f --- /dev/null +++ b/.vim/doc/tags @@ -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 index 0000000..4f38ab1 --- /dev/null +++ b/.vim/doc/vcscommand.txt @@ -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 +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 || +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 +||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. + +||ca VCSAdd +||cn VCSAnnotate +||cN VCSAnnotate! +||cc VCSCommit +||cD VCSDelete +||cd VCSDiff +||cg VCSGotoOriginal +||cG VCSGotoOriginal! +||ci VCSInfo +||cl VCSLog +||cL VCSLock +||cr VCSReview +||cs VCSStatus +||cu VCSUpdate +||cU VCSUnlock +||cv VCSVimDiff + +Only for CVS buffers: + +||ce CVSEdit +||cE CVSEditors +||ct CVSUnedit +||cwv CVSWatchers +||cwa CVSWatchAdd +||cwn CVSWatchOn +||cwf CVSWatchOff +||cwf CVSWatchRemove + + *vcscommand-mappings-override* + +The default mappings can be overridden by user-provided instead by mapping to +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 VCSAdd + +Second, the default map prefix ('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 ('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 q :bwipeout +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 index 0000000..16ffc97 --- /dev/null +++ b/.vim/ftplugin/c.vim @@ -0,0 +1 @@ +setlocal foldmethod=syntax diff --git a/.vim/ftplugin/imath.vim b/.vim/ftplugin/imath.vim new file mode 100644 index 0000000..7e7f4b2 --- /dev/null +++ b/.vim/ftplugin/imath.vim @@ -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 index 0000000..7883de5 --- /dev/null +++ b/.vim/ftplugin/java.vim @@ -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 index 0000000..9b40fa7 --- /dev/null +++ b/.vim/ftplugin/txt.vim @@ -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 index 0000000..d5d51de --- /dev/null +++ b/.vim/misc/filetype.vim @@ -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 index 0000000..0ff2632 --- /dev/null +++ b/.vim/misc/sasvimrc @@ -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, 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 + 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() + 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 2 + + " clear search highlighting when redrawing the screen + nnoremap :nohl + + " Make Escape toggle normal/insert mode -> I don't like this! + "nnoremap :startinsert + + " 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 Z :set hl& + nnoremap z :set hl+=B:none,P:none,R:none,L:none + + " swap words under cursor with \xp or \gw (where \ = + noremap xp "_yiw:s/\(\%#\w\+\)\(\W\+\)\(\w\+\)/\3\2\1/ + noremap gw xp + + " Use emacs style movement commands in insert mode + " XXX: these redine Vim maps, ctrl-a => useless, ctrl-e => useful + "inoremap I + "inoremap A + + " F1 toggles spell checking for this buffer + if exists("+spell") + inoremap :set spell! set spell?i + nnoremap :set spell! set spell? + endif + + " F2 toggles line numbers + nnoremap :set number! set number? + inoremap :set number! set number?i + + " F3 turns on auto indentations + nnoremap :set smartindent autoindent + inoremap :set smartindent autoindenti + + " XXX make this toggle the nerd tree plugin + " F5 launches file manager in the current directory + nnoremap :OPEN %:p:h + inoremap :OPEN %:p:hi + +" !KEYS }}} + +" !BINDS }}} + +" *PLUGINS* {{{ + +" *Taglist* {{{ + + map tl :TlistToggle + 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 =strftime("%Y-%m-%d %H:%M:%S") + endif + + iabbrev teh the + iabbrev swtich switch + +""""" !play }}} diff --git a/.vim/misc/unix.vim b/.vim/misc/unix.vim new file mode 100644 index 0000000..f766e74 --- /dev/null +++ b/.vim/misc/unix.vim @@ -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 index 0000000..9ed838d --- /dev/null +++ b/.vim/plugin/matrix.vim @@ -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 index 0000000..b8665b8 --- /dev/null +++ b/.vim/plugin/vcsbzr.vim @@ -0,0 +1,262 @@ +" vim600: set foldmethod=marker: +" +" BZR extension for VCSCommand. +" +" Maintainer: Bob Hiestand +" 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(''), s:bzrFunctions, []) + +let &cpo = s:save_cpo diff --git a/.vim/plugin/vcscommand.vim b/.vim/plugin/vcscommand.vim new file mode 100644 index 0000000..98214f2 --- /dev/null +++ b/.vim/plugin/vcscommand.vim @@ -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 +" 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 CommandName, for instance: +" +" nmap ,ca VCSAdd +" +" The default mappings are as follow: +" +" ca VCSAdd +" cn VCSAnnotate +" cN VCSAnnotate! +" cc VCSCommit +" cD VCSDelete +" cd VCSDiff +" cg VCSGotoOriginal +" cG VCSGotoOriginal! +" ci VCSInfo +" cl VCSLog +" cL VCSLock +" cr VCSReview +" cs VCSStatus +" cu VCSUpdate +" cU VCSUnlock +" 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 ('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 q :bwipeout +" 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 '.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 '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', 'c') . a:shortcut + if !hasmapto(a:expansion) + try + execute 'nmap ' 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] . "\" +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\G/" . splitRegex . "/e\d" + call setbufvar('%', '&filetype', getbufvar(originalBuffer, '&filetype')) + set scrollbind + leftabove vert new + normal 0P + execute "normal" . col('$') . "\|" + 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 VCSCommit :call VCSFinishCommitWithBuffer() + + 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 cc (or your own VCSCommit mapping)' + + if VCSCommandGetOption('VCSCommandCommitOnWrite', 1) == 1 + setlocal buftype=acwrite + au VCSCommandCommit BufWriteCmd 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 ExecuteExtensionMapping('" . shortcut . "')" + 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 +" , 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, '') > 0 + let fullCmd = substitute(a:cmd, '', 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', [])) +com! -nargs=* -bang VCSAnnotate call s:VCSAnnotate(, ) +com! -nargs=* -bang VCSBlame call s:VCSAnnotate(, ) +com! -nargs=? -bang VCSCommit call s:VCSCommit(, ) +com! -nargs=* VCSDelete call s:ExecuteVCSCommand('Delete', []) +com! -nargs=* VCSDiff call s:VCSDiff() +com! -nargs=0 -bang VCSGotoOriginal call s:VCSGotoOriginal() +com! -nargs=* VCSInfo call s:ExecuteVCSCommand('Info', []) +com! -nargs=* VCSLock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Lock', [])) +com! -nargs=* VCSLog call s:ExecuteVCSCommand('Log', []) +com! -nargs=* VCSRemove call s:ExecuteVCSCommand('Delete', []) +com! -nargs=0 VCSRevert call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Revert', [])) +com! -nargs=? VCSReview call s:VCSReview() +com! -nargs=* VCSStatus call s:ExecuteVCSCommand('Status', []) +com! -nargs=* VCSUnlock call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Unlock', [])) +com! -nargs=0 VCSUpdate call s:MarkOrigBufferForSetup(s:ExecuteVCSCommand('Update', [])) +com! -nargs=* VCSVimDiff call s:VCSVimDiff() + +" 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 VCSAdd :VCSAdd +nnoremap VCSAnnotate :VCSAnnotate +nnoremap VCSCommit :VCSCommit +nnoremap VCSDelete :VCSDelete +nnoremap VCSDiff :VCSDiff +nnoremap VCSGotoOriginal :VCSGotoOriginal +nnoremap VCSClearAndGotoOriginal :VCSGotoOriginal! +nnoremap VCSInfo :VCSInfo +nnoremap VCSLock :VCSLock +nnoremap VCSLog :VCSLog +nnoremap VCSRevert :VCSRevert +nnoremap VCSReview :VCSReview +nnoremap VCSSplitAnnotate :VCSAnnotate! +nnoremap VCSStatus :VCSStatus +nnoremap VCSUnlock :VCSUnlock +nnoremap VCSUpdate :VCSUpdate +nnoremap VCSVimDiff :VCSVimDiff + +" 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, '' . 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', 'VCSAdd'], + \['A&nnotate', 'VCSAnnotate'], + \['&Commit', 'VCSCommit'], + \['Delete', 'VCSDelete'], + \['&Diff', 'VCSDiff'], + \['&Info', 'VCSInfo'], + \['&Log', 'VCSLog'], + \['Revert', 'VCSRevert'], + \['&Review', 'VCSReview'], + \['&Status', 'VCSStatus'], + \['&Update', 'VCSUpdate'], + \['&VimDiff', '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(''))) +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 index 0000000..73d6a7f --- /dev/null +++ b/.vim/plugin/vcscvs.vim @@ -0,0 +1,449 @@ +" vim600: set foldmethod=marker: +" +" CVS extension for VCSCommand. +" +" Maintainer: Bob Hiestand +" 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 CommandName, for instance: +" +" nnoremap ,ce CVSEdit +" +" The default mappings are as follow: +" +" ce CVSEdit +" cE CVSEditors +" ct CVSUnedit +" cwv CVSWatchers +" cwa CVSWatchAdd +" cwn CVSWatchOn +" cwf CVSWatchOff +" 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() +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 ' . pluginName . ' :' . commandText . '' + if !hasmapto('' . pluginName) + let s:cvsExtensionMappings[shortCut] = commandText + endif +endfor + +" Section: Plugin Registration {{{1 +let s:VCSCommandUtility = VCSCommandRegisterModule('CVS', expand(''), s:cvsFunctions, s:cvsExtensionMappings) + +" Section: Menu items {{{1 +for [s:shortcut, s:command] in [ + \['CVS.&Edit', 'CVSEdit'], + \['CVS.Ed&itors', 'CVSEditors'], + \['CVS.Unedi&t', 'CVSUnedit'], + \['CVS.&Watchers', 'CVSWatchers'], + \['CVS.WatchAdd', 'CVSWatchAdd'], + \['CVS.WatchOn', 'CVSWatchOn'], + \['CVS.WatchOff', 'CVSWatchOff'], + \['CVS.WatchRemove', '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 index 0000000..4e299d9 --- /dev/null +++ b/.vim/plugin/vcsgit.vim @@ -0,0 +1,247 @@ +" vim600: set foldmethod=marker: +" +" git extension for VCSCommand. +" +" Maintainer: Bob Hiestand +" 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 . '"' + 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(''), s:gitFunctions, []) + +let &cpo = s:save_cpo diff --git a/.vim/plugin/vcshg.vim b/.vim/plugin/vcshg.vim new file mode 100644 index 0000000..2c616c0 --- /dev/null +++ b/.vim/plugin/vcshg.vim @@ -0,0 +1,273 @@ +" vim600: set foldmethod=marker: +" +" Mercurial extension for VCSCommand. +" +" Maintainer: Bob Hiestand +" 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(''), s:hgFunctions, []) + +let &cpo = s:save_cpo diff --git a/.vim/plugin/vcssvk.vim b/.vim/plugin/vcssvk.vim new file mode 100644 index 0000000..b7cc6c9 --- /dev/null +++ b/.vim/plugin/vcssvk.vim @@ -0,0 +1,257 @@ +" vim600: set foldmethod=marker: +" +" SVK extension for VCSCommand. +" +" Maintainer: Bob Hiestand +" 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(''), s:svkFunctions, []) + +let &cpo = s:save_cpo diff --git a/.vim/plugin/vcssvn.vim b/.vim/plugin/vcssvn.vim new file mode 100644 index 0000000..758f819 --- /dev/null +++ b/.vim/plugin/vcssvn.vim @@ -0,0 +1,284 @@ +" vim600: set foldmethod=marker: +" +" SVN extension for VCSCommand. +" +" Maintainer: Bob Hiestand +" 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(''), s:svnFunctions, []) + +let &cpo = s:save_cpo diff --git a/.vim/readme-ian.txt b/.vim/readme-ian.txt new file mode 100644 index 0000000..15aa87e --- /dev/null +++ b/.vim/readme-ian.txt @@ -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('') . '.vim' +map :call SyntaxAttr() + +}}} + +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 index 0000000..f35ed96 --- /dev/null +++ b/.vim/syntax/cvsannotate.vim @@ -0,0 +1,45 @@ +" Vim syntax file +" Language: CVS annotate output +" Maintainer: Bob Hiestand +" 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 index 0000000..77ad14e --- /dev/null +++ b/.vim/syntax/gitannotate.vim @@ -0,0 +1,44 @@ +" Vim syntax file +" Language: git annotate output +" Maintainer: Bob Hiestand +" 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 index 0000000..4e1d627 --- /dev/null +++ b/.vim/syntax/hgannotate.vim @@ -0,0 +1,40 @@ +" Vim syntax file +" Language: HG annotate output +" Maintainer: Bob Hiestand +" 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 index 0000000..ea654d9 --- /dev/null +++ b/.vim/syntax/sh.vim @@ -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. +" Previous Maintainer: Lennart Schultz +" 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 "\" + syn region shAlias matchgroup=shStatement start="\\s\+\(\w\+\)\@=" skip="\\$" end="\>\|`" + syn region shAlias matchgroup=shStatement start="\\s\+\(\w\+=\)\@=" skip="\\$" end="=" +endif + +" Error Codes: {{{1 +" ============ +syn match shDoError "\" +syn match shIfError "\" +syn match shInError "\" +syn match shCaseError ";;" +syn match shEsacError "\" +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="\" 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="\" matchgroup=shConditional end="\" contains=@shLoopList + syn region shIf fold transparent matchgroup=shConditional start="\#\=" +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=+\%(\\\)\@" +syn sync match shCaseEsacSync groupthere shCaseEsac "\" +syn sync match shDoSync grouphere shDo "\" +syn sync match shDoSync groupthere shDo "\" +syn sync match shForSync grouphere shFor "\" +syn sync match shForSync groupthere shFor "\" +syn sync match shIfSync grouphere shIf "\" +syn sync match shIfSync groupthere shIf "\" +syn sync match shUntilSync grouphere shRepeat "\" +syn sync match shWhileSync grouphere shRepeat "\" + +" 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 index 0000000..d93e9dd --- /dev/null +++ b/.vim/syntax/svkannotate.vim @@ -0,0 +1,42 @@ +" Vim syntax file +" Language: SVK annotate output +" Maintainer: Bob Hiestand +" 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 index 0000000..87a63ab --- /dev/null +++ b/.vim/syntax/svnannotate.vim @@ -0,0 +1,40 @@ +" Vim syntax file +" Language: SVN annotate output +" Maintainer: Bob Hiestand +" 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 index 0000000..80b4c6e --- /dev/null +++ b/.vim/syntax/vcscommit.vim @@ -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 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 " + +" when pasting, don't yank the pasted over text +vnoremap p "_dP + +" save +inoremap :update + + +"swap " and ' +noremap " ' +noremap ' " + +" Make ctrl-n and ctrl-p cycle through buffers in cmd mode +nnoremap :bn +nnoremap :bp +" quick command line window. +noremap m q:k +" mark +noremap m +noremap , q:i +noremap Q , +noremap q: Q +" yank to end of line +map Y y$ +" Insert a single char +nmap ir + +" Previously unmammed keys. + +" yank document to clipboard +noremap - mzgg"+yG'zzz +" save +noremap :w +" current_file:line_number +nmap \x :let @"=expand("%:t") . ':' . line(".") +" split common files +noremap :tabe ~/.vimrc +noremap :tabe ~/t +" paste clipboard +noremap "+p +noremap "+P +" alt-shift-letter has some terminal incompatibilities, so +" this needs to find a new key: +inoremap + + +" yank clipboard +noremap "+y:let@*=@+ +" easier window, tab and quickfix cycling +noremap w +noremap :cn +noremap :cp +" needs new key + +"nohl +noremap :nohl +inoremap :nohl + +" make x and X not affect registers +noremap x "_x +noremap X "_X +"stop # from unindenting newlines with smartcase +inoremap # X# + +"make * and # in visual mode search for the selection, instead of word under cursor. +vnoremap * :call VSearch()/ +vnoremap # :call VSearch()? +function! s:VSearch() + let old = @" + norm! gvy + let @/ = '\V' . substitute(escape(@", '\'), '\n', '\\n', 'g') + let @" = old +endfunction + +" remove trailing whitespace +nnoremap :%s/\s\+$//:unlet! b:statusline_trailing_space_warning + +" easy vim.. use ctrl-o and ctrl-l to use normal mode. +noremap :call easyVim() +inoremap :call easyVim() +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 :wa:call Make() +"nmap :wa:call Make() + +" standard make +" haven't been using it so its getting kicked out +"inoremap :wa:make +"nnoremap :wa:make +inoremap gT +nnoremap gT +inoremap gt +nnoremap 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 | cw +nnoremap :wa:Make +inoremap :wa:Make + +nnoremap :!./run.sh +nnoremap :silent! !ctags -R .:redr! + +" Execute the current line. +nnoremap "pyy:@p +vnoremap :call ExecRegion('vis') +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/^{{ nyyddnkk::r !date +"\%I:\%M \%p \%A \%D" < :call setline(".", strftime("%I:%M %p %A %d/%m/%y")) +command! T :call setline(".", strftime("%I:%M %p %A %d/%m/%y")) +" need to lookup the cross platform date for this one +command! D :r !date +"\%D" + +" for my super simple version control. +command! -nargs=* I w|!ii % +command! -nargs=* O w|!io % + + + +" 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('') . '.vim' +nnoremap :call SyntaxAttr() + +" 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(, , "") +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 index 0000000..95f2b98 --- /dev/null +++ b/.xbindkeysrc.scm @@ -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 9b8b675..71f4418 100644 --- 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 . diff --git a/brc b/brc new file mode 100644 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=' \ + --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<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" <"$file" </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 <= 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/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/(/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}$(/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 index 0000000..4a5f64d --- /dev/null +++ b/browser-home.html @@ -0,0 +1,9 @@ + + + + + HOME + + + + diff --git a/filesystem/etc/X11/Xsession.d/98iank b/filesystem/etc/X11/Xsession.d/98iank new file mode 100644 index 0000000..fa21023 --- /dev/null +++ b/filesystem/etc/X11/Xsession.d/98iank @@ -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 index 0000000..4b2e39e --- /dev/null +++ b/filesystem/etc/apt/apt.conf.d/02proxy @@ -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 index 0000000..4bb8f3c --- /dev/null +++ b/filesystem/etc/apt/detect-http-proxy @@ -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 index 0000000..073b78a --- /dev/null +++ b/filesystem/etc/apt/preferences.d/radicale @@ -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 index 0000000..d78e09b --- /dev/null +++ b/filesystem/etc/client.conf @@ -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 index 0000000..8327a14 --- /dev/null +++ b/filesystem/etc/cron.d/ian @@ -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 index 0000000..b0f35e1 --- /dev/null +++ b/filesystem/etc/cups/client.conf @@ -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 index 0000000..520b8b2 --- /dev/null +++ b/filesystem/etc/dnsmasq.d/iank.conf @@ -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 index 0000000..f1ad4fe --- /dev/null +++ b/filesystem/etc/logrotate.d/mylogs @@ -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 index 0000000..5318126 --- /dev/null +++ b/filesystem/etc/modprobe.d/blacklist.conf @@ -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 index 0000000..95dce05 --- /dev/null +++ b/filesystem/etc/profile.d/environment.sh @@ -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 index 0000000..8abb8de --- /dev/null +++ b/filesystem/etc/synergy.conf @@ -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 index 0000000..dcfba45 --- /dev/null +++ b/filesystem/etc/systemd/system.conf.d/tz.conf @@ -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 index 0000000..a751467 --- /dev/null +++ b/filesystem/etc/systemd/system/btrfsmaint.service @@ -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 index 0000000..f07fffb --- /dev/null +++ b/filesystem/etc/systemd/system/btrfsmaint.timer @@ -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 index 0000000..e564e1c --- /dev/null +++ b/filesystem/etc/systemd/system/btrfsmaintstop.service @@ -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 index 0000000..eebfc94 --- /dev/null +++ b/filesystem/etc/systemd/system/btrfsmaintstop.timer @@ -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 index 0000000..2edf6c0 --- /dev/null +++ b/filesystem/etc/systemd/system/iptables.service @@ -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 index 0000000..ab5ccba --- /dev/null +++ b/filesystem/etc/systemd/system/openvpn-client-nn@.service @@ -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 index 0000000..c814a3f --- /dev/null +++ b/filesystem/etc/systemd/system/openvpn-nn@.service @@ -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 index 0000000..9a67e74 --- /dev/null +++ b/filesystem/etc/udev/rules.d/99-kaleidoscope.rules @@ -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 index 0000000..176ab1e --- /dev/null +++ b/filesystem/etc/updatedb.findutils.cron.local @@ -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 index 0000000..ea4250f --- /dev/null +++ b/filesystem/usr/share/gitweb/gitweb.cgi @@ -0,0 +1,8418 @@ +#!/usr/bin/perl + +# gitweb - simple web interface to track changes in git repositories +# +# (C) 2005-2006, Kay Sievers +# (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 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() to retrieve the value + # (an array) or gitweb_check_feature() to check if + # 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'} = ['']; + # where 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 = ; + '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 ' 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 '"' 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/ / /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/ / /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 "$chr"; + } +} + +# 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 "$chr"; + } +} + +# 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 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 +# 'foobar' +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 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 .= " " . + $link . ""; + } + } + + if ($markers) { + return ' '. $markers . ''; + } 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} ? " " : ""); + my $post_white = ($opts{-pad_after} ? " " : ""); + $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 . + "" . $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) . + ""; +} + +# 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 "
$line
\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 + 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 + if ($line =~ m/\s(\d{6})$/) { + $line .= ' (' . + file_type_long($1) . + ')'; + } + # match + 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 . "
\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!
$line
\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!
$line
\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!
$line
\n!; + + return $result; +} + +# create note for patch simplified by combined diff +sub format_diff_cc_simplified { + my ($diffinfo, @parents) = @_; + my $result = ''; + + $result .= "
" . + "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 .= "
\n" . # class="diff header" + "
" . + "Simple merge" . + "
\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 = "@@ $from_text $to_text @@" . + "" . esc_html($section, -nbsp=>1) . ""; + 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 = "$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" . + "" . esc_html($section, -nbsp=>1) . ""; + 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 = "
$line
\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 [ ] +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/ 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/ / /g; + $title =~ s/^/ /g; + $title =~ s/$/ /g; + if (defined $matched && $matched eq $ctag) { + $title = qq($title); + } + $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($title); + } + $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 + '
' . + join (', ', map { + $cloud->{$_}->{'ctag'} + } splice(@tags, 0, $count)) . + '
'; + } +} + +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 -- 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 "\n"; + + $href_params{'extra_options'} = '--no-merges'; + $link_attr{'-href'} = href(%href_params); + $link_attr{'-title'} .= ' (no merges)'; + print "\n"; + } + + } else { + printf(''."\n", + esc_attr($site_name), href(project=>undef, action=>"project_index")); + printf(''."\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 ''."\n"; + } else { + foreach my $stylesheet (@stylesheets) { + next unless $stylesheet; + print ''."\n"; + } + } + print_feed_meta() + if ($status eq '200 OK'); + if (defined $favicon) { + print qq(\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) . + "
\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" . + "" . + $cgi->checkbox(-name => 'sr', -value => 1, -label => 're', + -checked => $search_use_regexp) . + "" . + "
" . + $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 < + + + + + + + + +$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 "\n"; + } + print_header_links($status); + + if (defined $site_html_head_string) { + print to_utf8($site_html_head_string); + } + + print "\n" . + "\n"; + + if (defined $site_header && -f $site_header) { + insert_file($site_header); + } + + print "
\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 "
\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 "
\n"; + if (defined $project) { + my $descr = git_get_project_description($project); + if (defined $descr) { + print "\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 "
\n"; # class="page_footer" + + if (defined $t0 && gitweb_check_feature('timed')) { + print "
\n"; + print 'This page took '. + ''. + tv_interval($t0, [ gettimeofday() ]). + ' seconds '. + ' and '. + ''. + $number_of_git_cmds. + ' git commands '. + " to generate.\n"; + print "
\n"; # class="page_footer" + } + + if (defined $site_footer && -f $site_footer) { + insert_file($site_footer); + } + + print qq!\n!; + if (defined $action && + $action eq 'blame_incremental') { + print qq!\n!; + } else { + my ($jstimezone, $tz_cookie, $datetime_class) = + gitweb_get_feature('javascript-timezone'); + + print qq!\n!; + } + + print "\n" . + ""; +} + +# die_error(, [, ]) +# 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 < +

+$status - $error +
+EOF + if (defined $extra) { + print "
\n" . + "$extra\n"; + } + print "\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 "
\n" . + (join " | ", + map { $_ eq $current ? + $_ : $cgi->a({-href => ($arg{$_}{_href} ? $arg{$_}{_href} : href(%{$arg{$_}}))}, "$_") + } @navs); + print "
\n$extra
\n" . + "
\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") . + " ⋅ " . + $cgi->a({-href => href(-replay=>1, page=>$page-1), + -accesskey => "p", -title => "Alt-p"}, "prev"); + } else { + $paging_nav .= "first ⋅ prev"; + } + + if ($has_next_link) { + $paging_nav .= " ⋅ " . + $cgi->a({-href => href(-replay=>1, page=>$page+1), + -accesskey => "n", -title => "Alt-n"}, "next"); + } else { + $paging_nav .= " ⋅ 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 "
\n" . + $cgi->a({-href => href(%args), -class => "title"}, + $title ? $title : $action) . + "\n
\n"; +} + +sub format_repo_url { + my ($name, $url) = @_; + return "$name$url\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!$strtime!; + } + + my $localtime_format = '(%02d:%02d %s)'; + if ($date->{'hour_local'} < 6) { + $localtime_format = '(%02d:%02d %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) . + "\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 "$who" . + format_search_author($co->{"${who}_name"}, $who, + esc_html($co->{"${who}_name"})) . " " . + format_search_author($co->{"${who}_email"}, $who, + esc_html("<" . $co->{"${who}_email"} . ">")) . + "" . + git_get_avatar($co->{"${who}_email"}, -size => 'double') . + "\n" . + "" . + "" . + format_timestamp_html(\%wd) . + "" . + "\n"; + } +} + +sub git_print_page_path { + my $name = shift; + my $type = shift; + my $hb = shift; + + + print "
"; + 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 "
\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 "" . esc_html($line) . "
\n"; + $skip_blank_line = 1; + } + next; + } + + if ($line =~ m,\s*([a-z]*link): (https?://\S+),i) { + if (! $opts{'-remove_signoff'}) { + print "" . esc_html($1) . ": " . + "" . esc_html($2) . "" . + "
\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) . "
\n"; + } + + if ($opts{'-final_empty_line'}) { + # end with single empty line + print "
\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 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 "" . mode_str($t->{'mode'}) . "\n"; + if (exists $t->{'size'}) { + print "$t->{'size'}\n"; + } + if ($t->{'type'} eq "blob") { + print "" . + $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 "\n"; + print ""; + 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 "\n"; + + } elsif ($t->{'type'} eq "tree") { + print ""; + print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'}, + file_name=>"$basedir$t->{'name'}", + %base_key)}, + esc_path($t->{'name'})); + print "\n"; + print ""; + 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 "\n"; + } else { + # unknown object: we can only present history for it + # (this includes 'commit' object, i.e. submodule support) + print "" . + esc_path($t->{'name'}) . + "\n"; + print ""; + if (defined $hash_base) { + print $cgi->a({-href => href(action=>"history", + hash_base=>$hash_base, + file_name=>"$basedir$t->{'name'}")}, + "history"); + } + print "\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 "
\n"; + if ($#{$difftree} > 10) { + print(($#{$difftree} + 1) . " files changed:\n"); + } + print "
\n"; + + print " 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 "\n" . + "\n"; # filename, patchN link + for (my $i = 0; $i < @parents; $i++) { + my $par = $parents[$i]; + print "\n"; + } + print "\n\n"; + } + + my $alternate = 1; + my $patchno = 0; + foreach my $line (@{$difftree}) { + my $diff = parsed_difftree_line($line); + + if ($alternate) { + print "\n"; + } else { + print "\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 "\n"; + } else { + print "\n"; + } + + if ($action eq 'commitdiff') { + # link to patch + $patchno++; + print "\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 "\n"; + } elsif ($status eq 'D') { + print "\n"; + } else { + if ($diff->{'to_id'} eq $from_hash) { + print "\n"; + } + } + + print "\n"; + + print "\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 = "[new $to_file_type"; + $mode_chng .= " with mode: $to_mode_str" if $to_mode_str; + $mode_chng .= "]"; + print "\n"; + print "\n"; + print "\n"; + + } elsif ($diff->{'status'} eq "D") { # deleted + my $mode_chng = "[deleted $from_file_type]"; + print "\n"; + print "\n"; + print "\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 = "[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 .= "]\n"; + } + print "\n"; + print "\n"; + print "\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 "\n" . + "\n" . + "\n"; + + } # we should not encounter Unmerged (U) or Unknown (X) status + print "\n"; + } + print "" if $has_header; + print "
" . + $cgi->a({-href => href(action=>"commitdiff", + hash=>$hash, hash_parent=>$par), + -title => 'commitdiff to parent number ' . + ($i+1) . ': ' . substr($par,0,7)}, + $i+1) . + " 
" . + $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'})) . + "" . + esc_path($diff->{'to_file'}) . + "" . + $cgi->a({-href => href(-anchor=>"patch$patchno")}, + "patch") . + " | " . + " | " . + $cgi->a({-href => href(action=>"blob", + hash_base=>$hash, + hash=>$from_hash, + file_name=>$from_path)}, + "blob" . ($i+1)) . + " | "; + } + 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)) . + " | "; + 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 "
"; + 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 "$mode_chng"; + 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 ""; + 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 "$mode_chng"; + 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 ""; + 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 "$mode_chnge"; + 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 "" . + $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'})) . "[$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]"; + 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 "
\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 '', + '
', + '
', + @$ctx, + '
', + '
', + @$ctx, + '
', + '
'; + } + + if (!@$add) { + # pure removal + print join '', + '
', + '
', + @$rem, + '
', + '
'; + } elsif (!@$rem) { + # pure addition + print join '', + '
', + '
', + @$add, + '
', + '
'; + } else { + print join '', + '
', + '
', + @$rem, + '
', + '
', + @$add, + '
', + '
'; + } +} + +# 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 "
\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 "
\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 "
\n" . + format_diff_cc_simplified($diffinfo, @hash_parents) . + "
\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 "
\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 "
\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 "
\n"; # class="diff extended_header" + + # from-file/to-file diff header + if (! $patch_line) { + print "
\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 "
\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 "
\n" . + format_diff_cc_simplified($diffinfo, @hash_parents) . + "
\n"; # class="patch" + + $patch_number++; + } + + if ($patch_number == 0) { + if (@hash_parents > 1) { + print "
Trivial merge
\n"; + } else { + print "
No differences found
\n"; + } + } + + print "
\n"; # class="patchset" +} + +# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . + +sub git_project_search_form { + my ($searchtext, $search_use_regexp) = @_; + + my $limit = ''; + if ($project_filter) { + $limit = " in '$project_filter/'"; + } + + print "
\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" . + "" . + $cgi->checkbox(-name => 'sr', -value => 1, -label => 're', + -checked => $search_use_regexp) . + "\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")) . "
\n"; + print "
\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' 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 .= "$header\n"; + } else { + $sort_th .= "" . + $cgi->a({-href => href(-replay=>1, order=>$name), + -class => "header"}, $header) . + "\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 "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + + if ($check_forks) { + print ""; + 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 "\n"; + } + print "" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"), + -class => "list"}, + esc_html_match_hl($pr->{'path'}, $search_regexp)) . + "\n" . + "" . $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'})) . + "\n"; + unless ($omit_owner) { + print "" . chop_and_escape_str($pr->{'owner'}, 15) . "\n"; + } + unless ($omit_age_column) { + print "{'age'}) . "\">" . + (defined $pr->{'age_string'} ? $pr->{'age_string'} : "No commits") . "\n"; + } + print"" . + $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") : '') . + "\n" . + "\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 "
\n". + "No such projects found
\n". + "Click ".$cgi->a({-href=>href(project=>undef)},"here")." to view all projects
\n". + "
\n
\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 "\n"; + unless ($no_header) { + print "\n"; + if ($check_forks) { + print "\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 "\n" . # for links + "\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 "\n"; + if ($check_forks) { + print "\n"; + } + print "\n"; + print "\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 "\n"; + if ($check_forks) { + print "\n"; + } + print "\n" . + "\n"; + } + print "
".esc_html($cat)."
$extra
\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', + "$co{'age_string'}" . + esc_html($co{'title'}) . $ref, + $commit); + print "
\n" . + "
\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") . + "
\n" . + "
\n"; + git_print_authorship(\%co, -tag => 'span'); + print "
\n
\n"; + + print "
\n"; + git_print_log($co{'comment'}, -final_empty_line=> 1); + print "
\n"; + } + if ($extra) { + print "
\n"; + print "$extra\n"; + print "
\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 "\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 "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + # git_summary() used print "\n" . + print "\n" . + format_author_html('td', \%co, 10) . "\n" . + "\n" . + "\n"; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$co{'age_string'}$co{'age_string_date'}"; + print format_subject_html($co{'title'}, $co{'title_short'}, + href(action=>"commit", hash=>$commit), $ref); + print "" . + $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 "
$extra
\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 "\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 "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + # shortlog: format_author_html('td', \%co, 10) + format_author_html('td', \%co, 15, 3) . "\n" . + "\n" . + "\n"; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$co{'age_string_date'}"; + # 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 "" . + $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 "
$extra
\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 "\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 "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + if (defined $tag{'age'}) { + print "\n"; + } else { + print "\n"; + } + print "\n" . + "\n" . + "\n" . + "\n" . + ""; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$tag{'age'}" . + $cgi->a({-href => href(action=>$tag{'reftype'}, hash=>$tag{'refid'}), + -class => "list name"}, esc_html($tag{'name'})) . + ""; + if (defined $comment) { + print format_subject_html($comment, $comment_short, + href(action=>"tag", hash=>$tag{'id'})); + } + print ""; + if ($tag{'type'} eq "tag") { + print $cgi->a({-href => href(action=>"tag", hash=>$tag{'id'})}, "tag"); + } else { + print " "; + } + print "" . " | " . + $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 "
$extra
\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 "\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 "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + ($curr ? "\n" . + "\n" . + ""; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$ref{'age'}" : "") . + $cgi->a({-href => href(action=>"shortlog", hash=>$ref{'fullname'}), + -class => "list name"},esc_html($ref{'name'})) . + "" . + $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") . + "
$extra
\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 = "\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 .= "
\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 "\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 "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print ""; + print ""; + + print "\n"; + } + + if ($limited) { + print "\n" . + "\n" . "\n"; + } + + print "
" . + $cgi->a({-href=> href(action=>'remotes', hash=>$remote), + -class=> "list name"},esc_html($remote)) . + "" . + (defined $fetch ? $cgi->a({-href=> $fetch}, "fetch") : "fetch") . + " | " . + (defined $push ? $cgi->a({-href=> $push}, "push") : "push") . + "
" . + $cgi->a({-href => href(action=>"remotes")}, "...") . + "
"; +} + +# 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") . + " ⋅ " . + $cgi->a({-href => href(-replay=>1, page=>$page-1), + -accesskey => "p", -title => "Alt-p"}, "prev"); + } else { + $paging_nav .= "first ⋅ 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 .= " ⋅ $next_link"; + } else { + $paging_nav .= " ⋅ 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 "

No match.

\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 "\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 "\n" . + "\n" . + "\n"; + } + + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + %co = parse_commit($set{'commit'}); + my $author = chop_and_escape_str($co{'author_name'}, 15, 5); + print "\n" . + "\n" . + "\n" . + "\n" . + "\n"; + } + + print "
" . + $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, + "commit") . + " | " . + $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, + hash_base=>$co{'id'})}, + "tree") . + "
$co{'age_string_date'}$author" . + $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), + -class => "list subject"}, + chop_and_escape_str($co{'title'}, 50) . "
"); + } 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"}, + "" . esc_path($set{'file'}) . "") . + "
\n"; + } + } + close $fd; + + # finish last commit (warning: repetition!) + if (%co) { + print "
" . + $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, + "commit") . + " | " . + $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, + hash_base=>$co{'id'})}, + "tree") . + "
\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 "\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 "\n"; + if ($alternate++) { + print "\n"; + } else { + print "\n"; + } + $file_href = href(action=>"blob", hash_base=>$co{'id'}, + file_name=>$file); + print "\n"; + if ($matches > 1000) { + print "
Too many matches, listing trimmed
\n"; + } + } else { + print "
No matches found
\n"; + } + close $fd; + + print "
". + $cgi->a({-href => $file_href, -class => "list"}, esc_path($file)); + print "\n"; + $lastfile = $file; + } + if ($binary) { + print "
Binary file
\n"; + } else { + $ltext = untabify($ltext); + if ($ltext =~ m/^(.*)($search_regexp)(.*)$/i) { + $ltext = esc_html($1, -nbsp=>1); + $ltext .= ''; + $ltext .= esc_html($2, -nbsp=>1); + $ltext .= ''; + $ltext .= esc_html($3, -nbsp=>1); + } else { + $ltext = esc_html($ltext, -nbsp=>1); + } + print "
" . + $cgi->a({-href => $file_href.'#l'.$lno, + -class => "linenr"}, sprintf('%4i', $lno)) . + ' ' . $ltext . "
\n"; + } + } + if ($lastfile) { + print "
\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 "\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 "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + print "\n" . + format_author_html('td', \%co, 15, 5) . + "\n" . + "\n" . + "\n"; + } + if (defined $extra) { + print "\n" . + "\n" . + "\n"; + } + print "
$co{'age_string_date'}" . + $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), + -class => "list subject"}, + chop_and_escape_str($co{'title'}, 50) . "
"); + 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$match$trail
"; + } + } + print "
" . + $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 "
$extra
\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 "
\n"; + insert_file($home_text); + print "
\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 "
 
\n"; + print "\n" . + "\n"; + if ($owner and not $omit_owner) { + print "\n"; + } + if (defined $cd{'rfc2822'}) { + print "" . + "\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 "" . + "" . + "" . + "\n"; + } + } + + print "
description" . esc_html($descr) . "
owner" . esc_html($owner) . "
last change".format_timestamp_html(\%cd)."
content tags".git_show_project_tagcloud($cloud, 48)."
\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 "
readme
\n" . + "
\n"; + insert_file("$projectroot/$project/README.html"); + print "\n
\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 "
\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "\n" . + "\n"; + if (defined($tag{'author'})) { + git_print_authorship_rows(\%tag, 'author'); + } + print "
object" . $cgi->a({-class => "list", -href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, + $tag{'object'}) . "" . $cgi->a({-href => href(action=>$tag{'type'}, hash=>$tag{'object'})}, + $tag{'type'}) . "
\n\n" . + "
\n"; + print "
"; + my $comment = $tag{'comment'}; + foreach my $line (@$comment) { + chomp $line; + print esc_html($line, -nbsp=>1) . "
\n"; + } + print "
\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 "\n"; + + print qq!
\n!; + } + + print qq!
\n!; + print qq!
... / ...
\n! + if ($format eq 'incremental'); + print qq!\n!. + #qq!\n!. + qq!\n!. + qq!\n!. + qq!\n!. + qq!\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!!. + qq!!. + qq!!; + print qq!\n"; + print qq!\n!; + } + + } else { # porcelain, i.e. ordinary blame + my %metainfo = (); # saves information about commits + + # blame data + LINE: + while (my $line = <$fd>) { + chomp $line; + # the header: [] + # no 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 "\n"; + if ($group_size) { + print "\n"; + } + # 'previous' + 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 ""; + print "\n"; + print "\n"; + } # end while + + } + + # footer + print "\n". + "
CommitLineData
!. + qq!$linenr! . esc_html($line) . "
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 "
" . + esc_html(join('', @author_initials)); + # or join('.', ...) + } + } + print "
"; + print $cgi->a({ -href => "$blamed#l$orig_lineno", + -class => "linenr" }, + esc_html($lineno)); + print "" . esc_html($data) . "
\n"; # class="blame" + print "
\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 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 "
\n" . + "

\n" . + "
".esc_html($hash)."
\n"; + } + git_print_page_path($file_name, "blob", $hash_base); + print "
\n"; + if ($mimetype =~ m!^image/!) { + print qq!!.esc_attr($file_name).qq!$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!
%4i %s
\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 "
"; + 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 "
\n"; + print "

\n"; + print "
".esc_html($hash)."
\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 "
\n"; + print "\n"; + my $alternate = 1; + # '..' (top directory) link if possible + if (defined $hash_base && + defined $file_name && $file_name =~ m![^/]+$!) { + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + + my $up = $file_name; + $up =~ s!/?[^/]+$!!; + undef $up unless $up; + # based on git_print_tree_entry + print '\n"; + print ''."\n" if $show_sizes; + print '\n"; + print "\n"; + + print "\n"; + } + foreach my $line (@entries) { + my %t = parse_ls_tree_line($line, -z => 1, -l => $show_sizes); + + if ($alternate) { + print "\n"; + } else { + print "\n"; + } + $alternate ^= 1; + + git_print_tree_entry(\%t, $basedir, $hash_base, $have_blame); + + print "\n"; + } + print "
' . mode_str('040000') . " '; + print $cgi->a({-href => href(action=>"tree", + hash_base=>$hash_base, + file_name=>$up)}, + ".."); + print "
\n" . + "
"; + 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 .= " ⋅ " . + $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 "
\n" . + "\n"; + git_print_authorship_rows(\%co); + print "\n"; + print "" . + "" . + "" . + "" . + "\n"; + + foreach my $par (@$parents) { + print "" . + "" . + "" . + "" . + "\n"; + } + print "
commit$co{'id'}
tree" . + $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$hash), + class => "list"}, $co{'tree'}) . + "" . + $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 "
parent" . + $cgi->a({-href => href(action=>"commit", hash=>$par), + class => "list"}, $par) . + "" . + $cgi->a({-href => href(action=>"commit", hash=>$par)}, "commit") . + " | " . + $cgi->a({-href => href(action=>"commitdiff", hash=>$hash, hash_parent=>$par)}, "diff") . + "
". + "
\n"; + + print "
\n"; + git_print_log($co{'comment'}); + print "
\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 "

$formats_nav
\n"; + print "
".esc_html("$hash vs $hash_parent")."
\n"; + } + if (defined $file_name) { + git_print_page_path($file_name, "blob", $hash_base); + } else { + print "
\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 "
\n"; + + git_patchset_body($fd, $diff_style, + [ \%diffinfo ], $hash_base, $hash_parent_base); + close $fd; + + print "
\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 "
\n" . + "\n"; + git_print_authorship_rows(\%co); + print "
". + "
\n"; + print "
\n"; + if (@{$co{'comment'}} > 1) { + print "
\n"; + git_print_log($co{'comment'}, -final_empty_line=> 1, -remove_title => 1); + print "
\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 "
\n"; + + git_patchset_body($fd, $diff_style, + \@difftree, $hash, + $use_parents ? @{$co{'parents'}} : $hash_parent); + close $fd; + print "
\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 <Pattern 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 re checkbox, +the pattern entered is recognized as the POSIX extended +regular expression (also case +insensitive).

+
+
commit
+
The commit messages and authorship information will be scanned for the given pattern.
+EOT + my $have_grep = gitweb_check_feature('grep'); + if ($have_grep) { + print <grep +
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.
+EOT + } + print <author +
Name and e-mail of the change author and date of birth of the patch will be scanned for the given pattern.
+
committer
+
Name and e-mail of the committer and date of commit will be scanned for the given pattern.
+EOT + my $have_pickaxe = gitweb_check_feature('pickaxe'); + if ($have_pickaxe) { + print <pickaxe +
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.
+EOT + } + print "
\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!\n!; + if ($format eq 'rss') { + print < + +XML + print "$title\n" . + "$alt_url\n" . + "$descr\n" . + "en\n" . + # project owner is responsible for 'editorial' content + "$owner\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 "\n" . + "$img\n" . + "$title\n" . + "$alt_url\n" . + "\n"; + } + if (%latest_date) { + print "$latest_date{'rfc2822'}\n"; + print "$latest_date{'rfc2822'}\n"; + } + print "gitweb v.$version/$git_version\n"; + } elsif ($format eq 'atom') { + print < +XML + print "$title\n" . + "$descr\n" . + '' . "\n" . + '' . "\n" . + "" . href(-full=>1) . "\n" . + # use project owner for feed author + "$owner\n"; + if (defined $favicon) { + print "" . esc_url($favicon) . "\n"; + } + if (defined $logo) { + # not twice as wide as tall: 72 x 27 pixels + print "" . esc_url($logo) . "\n"; + } + if (! %latest_date) { + # dummy date to keep the feed valid until commits trickle in: + print "1970-01-01T00:00:00Z\n"; + } else { + print "$latest_date{'iso-8601'}\n"; + } + print "gitweb\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 "\n" . + "" . esc_html($co{'title'}) . "\n" . + "" . esc_html($co{'author'}) . "\n" . + "$cd{'rfc2822'}\n" . + "$co_url\n" . + "$co_url\n" . + "" . esc_html($co{'title'}) . "\n" . + "" . + "\n" . + "" . esc_html($co{'title'}) . "\n" . + "$cd{'iso-8601'}\n" . + "\n" . + " " . esc_html($co{'author_name'}) . "\n"; + if ($co{'author_email'}) { + print " " . esc_html($co{'author_email'}) . "\n"; + } + print "\n" . + # use committer for contributor + "\n" . + " " . esc_html($co{'committer_name'}) . "\n"; + if ($co{'committer_email'}) { + print " " . esc_html($co{'committer_email'}) . "\n"; + } + print "\n" . + "$cd{'iso-8601'}\n" . + "\n" . + "$co_url\n" . + "\n" . + "
\n"; + } + my $comment = $co{'comment'}; + print "
\n";
+		foreach my $line (@$comment) {
+			$line = esc_html($line);
+			print "$line\n";
+		}
+		print "
    \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 "
  • " . + "[" . + $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
  • \n"; + } + if ($format eq 'rss') { + print "
]]>\n" . + "\n" . + "\n"; + } elsif ($format eq 'atom') { + print "\n
\n" . + "
\n" . + "\n"; + } + } + + # end of feed + if ($format eq 'rss') { + print "
\n\n"; + } elsif ($format eq 'atom') { + print "\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 < + + + $title OPML Export$filter + + + +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 "\n"; + } + print < + + +XML +} diff --git a/flidas-2017-12.xkb b/flidas-2017-12.xkb new file mode 100644 index 0000000..42d0fba --- /dev/null +++ b/flidas-2017-12.xkb @@ -0,0 +1,1820 @@ +xkb_keymap { +xkb_keycodes "evdev+aliases(qwerty)" { + minimum = 8; + maximum = 255; + = 9; + = 10; + = 11; + = 12; + = 13; + = 14; + = 15; + = 16; + = 17; + = 18; + = 19; + = 20; + = 21; + = 22; + = 23; + = 24; + = 25; + = 26; + = 27; + = 28; + = 29; + = 30; + = 31; + = 32; + = 33; + = 34; + = 35; + = 36; + = 37; + = 38; + = 39; + = 40; + = 41; + = 42; + = 43; + = 44; + = 45; + = 46; + = 47; + = 48; + = 49; + = 50; + = 51; + = 52; + = 53; + = 54; + = 55; + = 56; + = 57; + = 58; + = 59; + = 60; + = 61; + = 62; + = 63; + = 64; + = 65; + = 66; + = 67; + = 68; + = 69; + = 70; + = 71; + = 72; + = 73; + = 74; + = 75; + = 76; + = 77; + = 78; + = 79; + = 80; + = 81; + = 82; + = 83; + = 84; + = 85; + = 86; + = 87; + = 88; + = 89; + = 90; + = 91; + = 92; + = 94; + = 95; + = 96; + = 97; + = 98; + = 99; + = 100; + = 101; + = 102; + = 103; + = 104; + = 105; + = 106; + = 107; + = 108; + = 109; + = 110; + = 111; + = 112; + = 113; + = 114; + = 115; + = 116; + = 117; + = 118; + = 119; + = 120; + = 121; + = 122; + = 123; + = 124; + = 125; + = 126; + = 127; + = 128; + = 129; + = 130; + = 131; + = 132; + = 133; + = 134; + = 135; + = 136; + = 137; + = 138; + = 139; + = 140; + = 141; + = 142; + = 143; + = 144; + = 145; + = 146; + = 147; + = 148; + = 149; + = 150; + = 151; + = 152; + = 153; + = 154; + = 155; + = 156; + = 157; + = 158; + = 159; + = 160; + = 161; + = 162; + = 163; + = 164; + = 165; + = 166; + = 167; + = 168; + = 169; + = 170; + = 171; + = 172; + = 173; + = 174; + = 175; + = 176; + = 177; + = 178; + = 179; + = 180; + = 181; + = 182; + = 183; + = 184; + = 185; + = 186; + = 187; + = 188; + = 189; + = 190; + = 191; + = 192; + = 193; + = 194; + = 195; + = 196; + = 197; + = 198; + = 199; + = 200; + = 201; + = 202; + = 203; + = 204; + = 205; + = 206; + = 207; + = 208; + = 209; + = 210; + = 211; + = 212; + = 213; + = 214; + = 215; + = 216; + = 217; + = 218; + = 219; + = 220; + = 221; + = 222; + = 223; + = 224; + = 225; + = 226; + = 227; + = 228; + = 229; + = 230; + = 231; + = 232; + = 233; + = 234; + = 235; + = 236; + = 237; + = 238; + = 239; + = 240; + = 241; + = 242; + = 243; + = 244; + = 245; + = 246; + = 247; + = 248; + = 249; + = 250; + = 251; + = 252; + = 253; + 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 = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; +}; + +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 { [ Escape ] }; + key { [ 1, exclam ] }; + key { [ 2, at ] }; + key { [ 3, numbersign ] }; + key { [ 4, dollar ] }; + key { [ 5, percent ] }; + key { [ 6, asciicircum ] }; + key { [ 7, ampersand ] }; + key { [ 8, asterisk ] }; + key { [ 9, parenleft ] }; + key { [ 0, parenright ] }; + key { [ minus, underscore ] }; + key { [ equal, plus ] }; + key { [ BackSpace, BackSpace ] }; + key { [ Tab, ISO_Left_Tab ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ q, Q ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ w, W ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ e, E ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ r, R ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ t, T ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ y, Y ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ u, U ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ i, I ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ o, O ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ p, P ] + }; + key { [ bracketleft, braceleft ] }; + key { [ bracketright, braceright ] }; + key { [ Return ] }; + key { [ Control_L ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ a, A ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ s, S ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ d, D ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ f, F ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ g, G ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ h, H ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ j, J ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ k, K ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ l, L ] + }; + key { [ semicolon, colon ] }; + key { [ apostrophe, quotedbl ] }; + key { [ grave, asciitilde ] }; + key { [ Shift_L ] }; + key { [ backslash, bar ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ z, Z ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ x, X ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ c, C ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ v, V ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ b, B ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ n, N ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ m, M ] + }; + key { [ comma, less ] }; + key { [ period, greater ] }; + key { [ slash, question ] }; + key { [ Shift_R ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Multiply, KP_Multiply, KP_Multiply, KP_Multiply, XF86ClearGrab ] + }; + key { [ Alt_L, Meta_L ] }; + key { [ space ] }; + key { [ Caps_Lock ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F1, F1, F1, F1, XF86Switch_VT_1 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F2, F2, F2, F2, XF86Switch_VT_2 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F3, F3, F3, F3, XF86Switch_VT_3 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F4, F4, F4, F4, XF86Switch_VT_4 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F5, F5, F5, F5, XF86Switch_VT_5 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F6, F6, F6, F6, XF86Switch_VT_6 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F7, F7, F7, F7, XF86Switch_VT_7 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F8, F8, F8, F8, XF86Switch_VT_8 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F9, F9, F9, F9, XF86Switch_VT_9 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F10, F10, F10, F10, XF86Switch_VT_10 ] + }; + key { [ Num_Lock ] }; + key { [ Scroll_Lock ] }; + key { [ KP_Home, KP_7 ] }; + key { [ KP_Up, KP_8 ] }; + key { [ KP_Prior, KP_9 ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Subtract, KP_Subtract, KP_Subtract, KP_Subtract, XF86Prev_VMode ] + }; + key { [ KP_Left, KP_4 ] }; + key { [ KP_Begin, KP_5 ] }; + key { [ KP_Right, KP_6 ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Add, KP_Add, KP_Add, KP_Add, XF86Next_VMode ] + }; + key { [ KP_End, KP_1 ] }; + key { [ KP_Down, KP_2 ] }; + key { [ KP_Next, KP_3 ] }; + key { [ KP_Insert, KP_0 ] }; + key { [ KP_Delete, KP_Decimal ] }; + key { [ ISO_Level3_Shift ] }; + key { + type= "FOUR_LEVEL", + symbols[Group1]= [ less, greater, bar, brokenbar ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F11, F11, F11, F11, XF86Switch_VT_11 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F12, F12, F12, F12, XF86Switch_VT_12 ] + }; + key { [ Katakana ] }; + key { [ Hiragana ] }; + key { [ Henkan_Mode ] }; + key { [ Hiragana_Katakana ] }; + key { [ Muhenkan ] }; + key { [ KP_Enter ] }; + key { [ Control_R ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Divide, KP_Divide, KP_Divide, KP_Divide, XF86Ungrab ] + }; + key { + type= "PC_ALT_LEVEL2", + symbols[Group1]= [ Print, Sys_Req ] + }; + key { + type= "TWO_LEVEL", + symbols[Group1]= [ Alt_R, Meta_R ] + }; + key { [ Linefeed ] }; + key { [ Home ] }; + key { [ Up ] }; + key { [ Prior ] }; + key { [ Left ] }; + key { [ Right ] }; + key { [ End ] }; + key { [ Down ] }; + key { [ Next ] }; + key { [ Insert ] }; + key { [ Delete ] }; + key { [ XF86AudioMute ] }; + key { [ XF86AudioLowerVolume ] }; + key { [ XF86AudioRaiseVolume ] }; + key { [ XF86PowerOff ] }; + key { [ KP_Equal ] }; + key { [ plusminus ] }; + key { + type= "PC_CONTROL_LEVEL2", + symbols[Group1]= [ Pause, Break ] + }; + key { [ XF86LaunchA ] }; + key { [ KP_Decimal, KP_Decimal ] }; + key { [ Hangul ] }; + key { [ Hangul_Hanja ] }; + key { [ Super_L ] }; + key { [ Super_R ] }; + key { [ Menu ] }; + key { [ Cancel ] }; + key { [ Redo ] }; + key { [ SunProps ] }; + key { [ Undo ] }; + key { [ SunFront ] }; + key { [ XF86Copy ] }; + key { [ XF86Open ] }; + key { [ XF86Paste ] }; + key { [ Find ] }; + key { [ XF86Cut ] }; + key { [ Help ] }; + key { [ XF86MenuKB ] }; + key { [ XF86Calculator ] }; + key { [ XF86Sleep ] }; + key { [ XF86WakeUp ] }; + key { [ XF86Explorer ] }; + key { [ XF86Send ] }; + key { [ XF86Xfer ] }; + key { [ XF86Launch1 ] }; + key { [ XF86Launch2 ] }; + key { [ XF86WWW ] }; + key { [ XF86DOS ] }; + key { [ XF86ScreenSaver ] }; + key { [ XF86RotateWindows ] }; + key { [ XF86TaskPane ] }; + key { [ XF86Mail ] }; + key { [ XF86Favorites ] }; + key { [ XF86MyComputer ] }; + key { [ XF86Back ] }; + key { [ XF86Forward ] }; + key { [ XF86Eject ] }; + key { [ XF86Eject, XF86Eject ] }; + key { [ XF86AudioNext ] }; + key { [ XF86AudioPlay, XF86AudioPause ] }; + key { [ XF86AudioPrev ] }; + key { [ XF86AudioStop, XF86Eject ] }; + key { [ XF86AudioRecord ] }; + key { [ XF86AudioRewind ] }; + key { [ XF86Phone ] }; + key { [ XF86Tools ] }; + key { [ XF86HomePage ] }; + key { [ XF86Reload ] }; + key { [ XF86Close ] }; + key { [ XF86ScrollUp ] }; + key { [ XF86ScrollDown ] }; + key { [ parenleft ] }; + key { [ parenright ] }; + key { [ XF86New ] }; + key { [ Redo ] }; + key { [ XF86Tools ] }; + key { [ XF86Launch5 ] }; + key { [ XF86Launch6 ] }; + key { [ XF86Launch7 ] }; + key { [ XF86Launch8 ] }; + key { [ XF86Launch9 ] }; + key { [ XF86AudioMicMute ] }; + key { [ XF86TouchpadToggle ] }; + key { [ XF86TouchpadOn ] }; + key { [ XF86TouchpadOff ] }; + key { [ Mode_switch ] }; + key { [ NoSymbol, Alt_L ] }; + key { [ NoSymbol, Meta_L ] }; + key { [ NoSymbol, Super_L ] }; + key { [ NoSymbol, Hyper_L ] }; + key { [ XF86AudioPlay ] }; + key { [ XF86AudioPause ] }; + key { [ XF86Launch3 ] }; + key { [ XF86Launch4 ] }; + key { [ XF86LaunchB ] }; + key { [ XF86Suspend ] }; + key { [ XF86Close ] }; + key { [ XF86AudioPlay ] }; + key { [ XF86AudioForward ] }; + key { [ Print ] }; + key { [ XF86WebCam ] }; + key { [ XF86Mail ] }; + key { [ XF86Messenger ] }; + key { [ XF86Search ] }; + key { [ XF86Go ] }; + key { [ XF86Finance ] }; + key { [ XF86Game ] }; + key { [ XF86Shop ] }; + key { [ Cancel ] }; + key { [ XF86MonBrightnessDown ] }; + key { [ XF86MonBrightnessUp ] }; + key { [ XF86AudioMedia ] }; + key { [ XF86Display ] }; + key { [ XF86KbdLightOnOff ] }; + key { [ XF86KbdBrightnessDown ] }; + key { [ XF86KbdBrightnessUp ] }; + key { [ XF86Send ] }; + key { [ XF86Reply ] }; + key { [ XF86MailForward ] }; + key { [ XF86Save ] }; + key { [ XF86Documents ] }; + key { [ XF86Battery ] }; + key { [ XF86Bluetooth ] }; + key { [ XF86WLAN ] }; + modifier_map Control { }; + modifier_map Shift { }; + modifier_map Shift { }; + modifier_map Mod1 { }; + modifier_map Lock { }; + modifier_map Mod2 { }; + modifier_map Mod5 { }; + modifier_map Control { }; + modifier_map Mod1 { }; + modifier_map Mod4 { }; + modifier_map Mod4 { }; + modifier_map Mod5 { }; + modifier_map Mod1 { }; + modifier_map Mod4 { }; + modifier_map Mod4 { }; +}; + +xkb_geometry "pc(pc105)" { + + width= 470; + height= 180; + + alias = ; + alias = ; + + 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 { + { , "NORM", 1 }, + { , "NORM", 20, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 11, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 11, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 8, color="white" }, + { , "NORM", 1, color="white" }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, + { , "BKSP", 1, color="grey20" } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "TABK", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "RTRN", 1, color="grey20" } + }; + }; + row { + top= 39; + left= 1; + keys { + { , "CAPS", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 } + }; + }; + row { + top= 58; + left= 1; + keys { + { , "LFSH", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, + { , "RTSH", 1, color="grey20" } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "MODK", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SPCE", 1 }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 } + }; + }; + row { + top= 58; + left= 20; + keys { + { , "NORM", 1 } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "KPAD", 1 } + }; + }; + row { + top= 39; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" } + }; + }; + row { + top= 58; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "KPAD", 1 } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "KP0", 1, color="white" }, + { , "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 index 0000000..3128123 --- /dev/null +++ b/machine_specific/btrbk.hosts @@ -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 index 0000000..4be721d --- /dev/null +++ b/machine_specific/btrbk/filesystem/etc/systemd/system/btrbk.service @@ -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 index 0000000..27113f6 --- /dev/null +++ b/machine_specific/btrbk/filesystem/etc/systemd/system/btrbk.timer @@ -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 index 0000000..fa2298d --- /dev/null +++ b/machine_specific/li/filesystem/etc/bind/bind-writable/db.b8.nz @@ -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 index 0000000..e3e3229 --- /dev/null +++ b/machine_specific/li/filesystem/etc/bind/named.conf.local @@ -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 index 0000000..485813f --- /dev/null +++ b/machine_specific/treetowl/filesystem/etc/cron.d/treetowl @@ -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 index 0000000..87e45c0 --- /dev/null +++ b/machine_specific/treetowl/filesystem/etc/cron.daily/check-lets-encrypt-ssl-settings @@ -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 < ~/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 index 0000000..4be721d --- /dev/null +++ b/machine_specific/treetowl/filesystem/etc/systemd/system/btrbk.service @@ -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 index 0000000..27113f6 --- /dev/null +++ b/machine_specific/treetowl/filesystem/etc/systemd/system/btrbk.timer @@ -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 index 0000000..0a2d1fb --- /dev/null +++ b/model01.xkb @@ -0,0 +1,1820 @@ +xkb_keymap { +xkb_keycodes "evdev+aliases(qwerty)" { + minimum = 8; + maximum = 255; + = 9; + = 10; + = 11; + = 12; + = 13; + = 14; + = 15; + = 16; + = 17; + = 18; + = 19; + = 20; + = 21; + = 22; + = 23; + = 24; + = 25; + = 26; + = 27; + = 28; + = 29; + = 30; + = 31; + = 32; + = 33; + = 34; + = 35; + = 36; + = 37; + = 38; + = 39; + = 40; + = 41; + = 42; + = 43; + = 44; + = 45; + = 46; + = 47; + = 48; + = 49; + = 50; + = 51; + = 52; + = 53; + = 54; + = 55; + = 56; + = 57; + = 58; + = 59; + = 60; + = 61; + = 62; + = 63; + = 64; + = 65; + = 66; + = 67; + = 68; + = 69; + = 70; + = 71; + = 72; + = 73; + = 74; + = 75; + = 76; + = 77; + = 78; + = 79; + = 80; + = 81; + = 82; + = 83; + = 84; + = 85; + = 86; + = 87; + = 88; + = 89; + = 90; + = 91; + = 92; + = 94; + = 95; + = 96; + = 97; + = 98; + = 99; + = 100; + = 101; + = 102; + = 103; + = 104; + = 105; + = 106; + = 107; + = 108; + = 109; + = 110; + = 111; + = 112; + = 113; + = 114; + = 115; + = 116; + = 117; + = 118; + = 119; + = 120; + = 121; + = 122; + = 123; + = 124; + = 125; + = 126; + = 127; + = 128; + = 129; + = 130; + = 131; + = 132; + = 133; + = 134; + = 135; + = 136; + = 137; + = 138; + = 139; + = 140; + = 141; + = 142; + = 143; + = 144; + = 145; + = 146; + = 147; + = 148; + = 149; + = 150; + = 151; + = 152; + = 153; + = 154; + = 155; + = 156; + = 157; + = 158; + = 159; + = 160; + = 161; + = 162; + = 163; + = 164; + = 165; + = 166; + = 167; + = 168; + = 169; + = 170; + = 171; + = 172; + = 173; + = 174; + = 175; + = 176; + = 177; + = 178; + = 179; + = 180; + = 181; + = 182; + = 183; + = 184; + = 185; + = 186; + = 187; + = 188; + = 189; + = 190; + = 191; + = 192; + = 193; + = 194; + = 195; + = 196; + = 197; + = 198; + = 199; + = 200; + = 201; + = 202; + = 203; + = 204; + = 205; + = 206; + = 207; + = 208; + = 209; + = 210; + = 211; + = 212; + = 213; + = 214; + = 215; + = 216; + = 217; + = 218; + = 219; + = 220; + = 221; + = 222; + = 223; + = 224; + = 225; + = 226; + = 227; + = 228; + = 229; + = 230; + = 231; + = 232; + = 233; + = 234; + = 235; + = 236; + = 237; + = 238; + = 239; + = 240; + = 241; + = 242; + = 243; + = 244; + = 245; + = 246; + = 247; + = 248; + = 249; + = 250; + = 251; + = 252; + = 253; + 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 = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; +}; + +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 { [ Escape ] }; + key { [ 1, exclam ] }; + key { [ 2, at ] }; + key { [ 3, numbersign ] }; + key { [ 4, dollar ] }; + key { [ 5, percent ] }; + key { [ 6, asciicircum ] }; + key { [ 7, ampersand ] }; + key { [ 8, asterisk ] }; + key { [ 9, parenleft ] }; + key { [ 0, parenright ] }; + key { [ minus, underscore ] }; + key { [ equal, plus ] }; + key { [ BackSpace, BackSpace ] }; + key { [ Tab, ISO_Left_Tab ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ q, Q ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ w, W ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ e, E ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ r, R ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ t, T ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ y, Y ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ u, U ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ i, I ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ o, O ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ p, P ] + }; + key { [ bracketleft, braceleft ] }; + key { [ bracketright, braceright ] }; + key { [ Return ] }; + key { [ Control_L ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ a, A ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ s, S ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ d, D ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ f, F ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ g, G ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ h, H ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ j, J ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ k, K ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ l, L ] + }; + key { [ semicolon, colon ] }; + key { [ apostrophe, quotedbl ] }; + key { [ grave, asciitilde ] }; + key { [ Shift_L ] }; + key { [ backslash, bar ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ z, Z ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ x, X ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ c, C ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ v, V ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ b, B ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ n, N ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ m, M ] + }; + key { [ comma, less ] }; + key { [ period, greater ] }; + key { [ slash, question ] }; + key { [ Shift_R ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ underscore, grave, KP_Multiply, KP_Multiply, XF86ClearGrab ] + }; + key { [ Alt_L, Meta_L ] }; + key { [ space ] }; + key { [ Caps_Lock ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F1, F1, F1, F1, XF86Switch_VT_1 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F2, F2, F2, F2, XF86Switch_VT_2 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F3, F3, F3, F3, XF86Switch_VT_3 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F4, F4, F4, F4, XF86Switch_VT_4 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F5, F5, F5, F5, XF86Switch_VT_5 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F6, F6, F6, F6, XF86Switch_VT_6 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F7, F7, F7, F7, XF86Switch_VT_7 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F8, F8, F8, F8, XF86Switch_VT_8 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F9, F9, F9, F9, XF86Switch_VT_9 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F10, F10, F10, F10, XF86Switch_VT_10 ] + }; + key { [ Num_Lock ] }; + key { [ Scroll_Lock ] }; + key { [ KP_Home, KP_7 ] }; + key { [ KP_Up, KP_8 ] }; + key { [ KP_Prior, KP_9 ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Subtract, KP_Subtract, KP_Subtract, KP_Subtract, XF86Prev_VMode ] + }; + key { [ KP_Left, KP_4 ] }; + key { [ KP_Begin, KP_5 ] }; + key { [ KP_Right, KP_6 ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Add, KP_Add, KP_Add, KP_Add, XF86Next_VMode ] + }; + key { [ KP_End, KP_1 ] }; + key { [ KP_Down, KP_2 ] }; + key { [ KP_Next, KP_3 ] }; + key { [ KP_Insert, KP_0 ] }; + key { [ KP_Delete, KP_Decimal ] }; + key { [ ISO_Level3_Shift ] }; + key { + type= "FOUR_LEVEL", + symbols[Group1]= [ less, greater, bar, brokenbar ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F11, F11, F11, F11, XF86Switch_VT_11 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F12, F12, F12, F12, XF86Switch_VT_12 ] + }; + key { [ Katakana ] }; + key { [ Hiragana ] }; + key { [ Henkan_Mode ] }; + key { [ Hiragana_Katakana ] }; + key { [ Muhenkan ] }; + key { [ KP_Enter ] }; + key { [ Control_R ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Divide, KP_Divide, KP_Divide, KP_Divide, XF86Ungrab ] + }; + key { + type= "PC_ALT_LEVEL2", + symbols[Group1]= [ Print, Sys_Req ] + }; + key { + type= "TWO_LEVEL", + symbols[Group1]= [ Alt_R, Meta_R ] + }; + key { [ Linefeed ] }; + key { [ Home ] }; + key { [ Up ] }; + key { [ Prior ] }; + key { [ Left ] }; + key { [ Right ] }; + key { [ End ] }; + key { [ Down ] }; + key { [ Next ] }; + key { [ Insert ] }; + key { [ Delete ] }; + key { [ XF86AudioMute ] }; + key { [ XF86AudioLowerVolume ] }; + key { [ XF86AudioRaiseVolume ] }; + key { [ XF86PowerOff ] }; + key { [ KP_Equal ] }; + key { [ plusminus ] }; + key { + type= "PC_CONTROL_LEVEL2", + symbols[Group1]= [ Pause, Break ] + }; + key { [ XF86LaunchA ] }; + key { [ KP_Decimal, KP_Decimal ] }; + key { [ Hangul ] }; + key { [ Hangul_Hanja ] }; + key { [ Super_L ] }; + key { [ Super_R ] }; + key { [ Menu ] }; + key { [ Cancel ] }; + key { [ Redo ] }; + key { [ SunProps ] }; + key { [ Undo ] }; + key { [ SunFront ] }; + key { [ XF86Copy ] }; + key { [ XF86Open ] }; + key { [ XF86Paste ] }; + key { [ Find ] }; + key { [ XF86Cut ] }; + key { [ Help ] }; + key { [ XF86MenuKB ] }; + key { [ XF86Calculator ] }; + key { [ XF86Sleep ] }; + key { [ XF86WakeUp ] }; + key { [ XF86Explorer ] }; + key { [ XF86Send ] }; + key { [ XF86Xfer ] }; + key { [ XF86Launch1 ] }; + key { [ XF86Launch2 ] }; + key { [ XF86WWW ] }; + key { [ XF86DOS ] }; + key { [ XF86ScreenSaver ] }; + key { [ XF86RotateWindows ] }; + key { [ XF86TaskPane ] }; + key { [ XF86Mail ] }; + key { [ XF86Favorites ] }; + key { [ XF86MyComputer ] }; + key { [ XF86Back ] }; + key { [ XF86Forward ] }; + key { [ XF86Eject ] }; + key { [ XF86Eject, XF86Eject ] }; + key { [ XF86AudioNext ] }; + key { [ XF86AudioPlay, XF86AudioPause ] }; + key { [ XF86AudioPrev ] }; + key { [ XF86AudioStop, XF86Eject ] }; + key { [ XF86AudioRecord ] }; + key { [ XF86AudioRewind ] }; + key { [ XF86Phone ] }; + key { [ XF86Tools ] }; + key { [ XF86HomePage ] }; + key { [ XF86Reload ] }; + key { [ XF86Close ] }; + key { [ XF86ScrollUp ] }; + key { [ XF86ScrollDown ] }; + key { [ parenleft ] }; + key { [ parenright ] }; + key { [ XF86New ] }; + key { [ Redo ] }; + key { [ XF86Tools ] }; + key { [ XF86Launch5 ] }; + key { [ XF86Launch6 ] }; + key { [ XF86Launch7 ] }; + key { [ XF86Launch8 ] }; + key { [ XF86Launch9 ] }; + key { [ XF86AudioMicMute ] }; + key { [ XF86TouchpadToggle ] }; + key { [ XF86TouchpadOn ] }; + key { [ XF86TouchpadOff ] }; + key { [ Mode_switch ] }; + key { [ NoSymbol, Alt_L ] }; + key { [ NoSymbol, Meta_L ] }; + key { [ NoSymbol, Super_L ] }; + key { [ NoSymbol, Hyper_L ] }; + key { [ XF86AudioPlay ] }; + key { [ XF86AudioPause ] }; + key { [ XF86Launch3 ] }; + key { [ XF86Launch4 ] }; + key { [ XF86LaunchB ] }; + key { [ XF86Suspend ] }; + key { [ XF86Close ] }; + key { [ XF86AudioPlay ] }; + key { [ XF86AudioForward ] }; + key { [ Print ] }; + key { [ XF86WebCam ] }; + key { [ XF86Mail ] }; + key { [ XF86Messenger ] }; + key { [ XF86Search ] }; + key { [ XF86Go ] }; + key { [ XF86Finance ] }; + key { [ XF86Game ] }; + key { [ XF86Shop ] }; + key { [ Cancel ] }; + key { [ XF86MonBrightnessDown ] }; + key { [ XF86MonBrightnessUp ] }; + key { [ XF86AudioMedia ] }; + key { [ XF86Display ] }; + key { [ XF86KbdLightOnOff ] }; + key { [ XF86KbdBrightnessDown ] }; + key { [ XF86KbdBrightnessUp ] }; + key { [ XF86Send ] }; + key { [ XF86Reply ] }; + key { [ XF86MailForward ] }; + key { [ XF86Save ] }; + key { [ XF86Documents ] }; + key { [ XF86Battery ] }; + key { [ XF86Bluetooth ] }; + key { [ XF86WLAN ] }; + modifier_map Control { }; + modifier_map Shift { }; + modifier_map Shift { }; + modifier_map Mod1 { }; + modifier_map Lock { }; + modifier_map Mod2 { }; + modifier_map Mod5 { }; + modifier_map Control { }; + modifier_map Mod1 { }; + modifier_map Mod4 { }; + modifier_map Mod4 { }; + modifier_map Mod5 { }; + modifier_map Mod1 { }; + modifier_map Mod4 { }; + modifier_map Mod4 { }; +}; + +xkb_geometry "pc(pc105)" { + + width= 470; + height= 180; + + alias = ; + alias = ; + + 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 { + { , "NORM", 1 }, + { , "NORM", 20, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 11, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 11, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 8, color="white" }, + { , "NORM", 1, color="white" }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, + { , "BKSP", 1, color="grey20" } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "TABK", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "RTRN", 1, color="grey20" } + }; + }; + row { + top= 39; + left= 1; + keys { + { , "CAPS", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 } + }; + }; + row { + top= 58; + left= 1; + keys { + { , "LFSH", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, + { , "RTSH", 1, color="grey20" } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "MODK", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SPCE", 1 }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 } + }; + }; + row { + top= 58; + left= 20; + keys { + { , "NORM", 1 } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "KPAD", 1 } + }; + }; + row { + top= 39; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" } + }; + }; + row { + top= 58; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "KPAD", 1 } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "KP0", 1, color="white" }, + { , "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 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 index 0000000..f03451a --- /dev/null +++ b/path_add-function @@ -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 index 0000000..af815a4 --- /dev/null +++ b/playlists/music/10.xsp @@ -0,0 +1,14 @@ + + + + all + + Avant-Garde + Noise + Skit + Spoken Word + + + 9 + + diff --git a/playlists/music/8.xsp b/playlists/music/8.xsp new file mode 100644 index 0000000..51722aa --- /dev/null +++ b/playlists/music/8.xsp @@ -0,0 +1,14 @@ + + + 8 + all + + 7 + + + Avant-Garde + Noise + Skit + Spoken Word + + diff --git a/playlists/music/sad.xsp b/playlists/music/sad.xsp new file mode 100644 index 0000000..be00384 --- /dev/null +++ b/playlists/music/sad.xsp @@ -0,0 +1,11 @@ + + + sad + all + + sad + + + 7 + + diff --git a/playlists/music/sad5.xsp b/playlists/music/sad5.xsp new file mode 100644 index 0000000..7b86c5e --- /dev/null +++ b/playlists/music/sad5.xsp @@ -0,0 +1,11 @@ + + + sad5 + all + + sad + + + 9 + + diff --git a/playlists/music/sy most.xsp b/playlists/music/sy most.xsp new file mode 100644 index 0000000..a5a971c --- /dev/null +++ b/playlists/music/sy most.xsp @@ -0,0 +1,11 @@ + + + sy most + all + + Sonic Youth + + + 5 + + diff --git a/subdir_files/.bazaar/authentication.conf b/subdir_files/.bazaar/authentication.conf new file mode 100644 index 0000000..3b8d3cb --- /dev/null +++ b/subdir_files/.bazaar/authentication.conf @@ -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 index 0000000..1ca33b4 --- /dev/null +++ b/subdir_files/.bazaar/bazaar.conf @@ -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 index 0000000..3452831 --- /dev/null +++ b/subdir_files/.config/beets/config.yaml @@ -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 index 0000000..ba4dc68 --- /dev/null +++ b/subdir_files/.config/i3/config @@ -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 index 0000000..11836ca --- /dev/null +++ b/subdir_files/.config/menus/mate-applications.menu @@ -0,0 +1,14 @@ + + + + Applications + /etc/xdg/menus/mate-applications.menu + + firefox.desktop + + + chromium.desktop + + diff --git a/subdir_files/.config/mpv/mpv.conf b/subdir_files/.config/mpv/mpv.conf new file mode 100644 index 0000000..923bb30 --- /dev/null +++ b/subdir_files/.config/mpv/mpv.conf @@ -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 index 0000000..266fd06 --- /dev/null +++ b/subdir_files/.config/sakura/sakura.conf @@ -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 index 0000000..f9da1b5 --- /dev/null +++ b/subdir_files/.gnupg/gpg.conf @@ -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 index 0000000..260cb7d --- /dev/null +++ b/subdir_files/.local/share/applications/chromium.desktop @@ -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 index 0000000..3bd7324 --- /dev/null +++ b/subdir_files/.local/share/applications/firefox.desktop @@ -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 index 0000000..dcde179 --- /dev/null +++ b/subdir_files/.local/share/konsole/README.ian @@ -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 index 0000000..0d12e6b --- /dev/null +++ b/subdir_files/.local/share/konsole/default.keytab @@ -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 index 0000000..d000a1e --- /dev/null +++ b/subdir_files/.local/share/konsole/profileian.profile @@ -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 index 0000000..f0271e0 --- /dev/null +++ b/subdir_files/.local/share/kxmlgui5/konsole/README.ian @@ -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 index 0000000..62c9e1a --- /dev/null +++ b/subdir_files/.local/share/kxmlgui5/konsole/sessionui.rc @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/subdir_files/.xmonad/xmonad.hs b/subdir_files/.xmonad/xmonad.hs new file mode 100644 index 0000000..b385ad5 --- /dev/null +++ b/subdir_files/.xmonad/xmonad.hs @@ -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 index 0000000..9c631af --- /dev/null +++ b/subdir_files/sieve/fsf-test.sieve @@ -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" "", + header :is "Return-path" "" + ) { +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 index 0000000..9c631af --- /dev/null +++ b/subdir_files/sieve/fsf.sieve @@ -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" "", + header :is "Return-path" "" + ) { +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 index 0000000..6dbafa2 --- /dev/null +++ b/subdir_files/sieve/lists.sieve @@ -0,0 +1,85 @@ +require [ "regex", "variables", "fileinto", "envelope", "mailbox", "imap4flags", "include" ]; + +if anyof ( + header :contains "list-id" "", + header :contains "list-id" "" + ) { + fileinto :create "sec"; + stop; + } + +if anyof ( + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "<", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "") { + if header :regex "list-id" "<([a-z_0-9-]+)[.@]" { + set :lower "listname" "${1}"; + fileinto :create "l/${listname}"; + } +} +if anyof ( + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "") { + if header :regex "list-id" "<([a-z_0-9-]+)[.@]" { + set :lower "listname" "${1}"; + fileinto :create "l/fedora.${listname}"; + } +} + +if anyof ( + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "", + header :contains "list-id" "" + ) { + 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 index 0000000..1098ea0 --- /dev/null +++ b/subdir_files/sieve/main.sieve @@ -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 index 0000000..431fd67 --- /dev/null +++ b/subdir_files/sieve/maintest.sieve @@ -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 index 0000000..1dd571a --- /dev/null +++ b/x2.xkb @@ -0,0 +1,1907 @@ +xkb_keymap { +xkb_keycodes "evdev+aliases(qwerty)" { + minimum = 8; + maximum = 255; + = 9; + = 10; + = 11; + = 12; + = 13; + = 14; + = 15; + = 16; + = 17; + = 18; + = 19; + = 20; + = 21; + = 22; + = 23; + = 24; + = 25; + = 26; + = 27; + = 28; + = 29; + = 30; + = 31; + = 32; + = 33; + = 34; + = 35; + = 36; + = 64; + = 38; + = 39; + = 40; + = 41; + = 42; + = 43; + = 44; + = 45; + = 46; + = 47; + = 48; + = 49; + = 50; + = 51; + = 52; + = 53; + = 54; + = 55; + = 56; + = 57; + = 58; + = 59; + = 60; + = 61; + = 62; + = 63; + # = 64; + = 65; + # = 66; + = 67; + = 68; + = 69; + = 70; + = 71; + = 72; + = 73; + = 74; + = 75; + = 76; + = 77; + = 78; + = 79; + = 80; + = 81; + = 82; + = 83; + = 84; + = 85; + = 66; + = 87; + = 88; + = 89; + = 90; + = 91; + = 92; + = 94; + = 95; + = 96; + = 97; + = 98; + = 99; + = 100; + = 101; + = 102; + = 103; + = 135; + # = 105; + = 105; + = 107; + = 108; + = 109; + = 110; + = 111; + = 112; + = 113; + = 114; + = 115; + = 116; + = 117; + = 118; + = 119; + = 120; + = 121; + = 122; + = 123; + = 124; + = 37; + = 126; + = 127; + = 128; + = 129; + = 130; + = 131; + = 132; + = 133; + = 134; + # = 135; + = 136; + = 137; + = 138; + = 139; + = 140; + = 141; + = 142; + = 143; + = 144; + = 145; + = 146; + = 147; + = 148; + = 149; + = 150; + = 151; + = 152; + = 153; + = 154; + = 155; + = 156; + = 157; + = 158; + = 159; + = 160; + = 161; + = 162; + = 163; + = 164; + = 165; + = 166; + = 167; + = 168; + = 169; + = 170; + = 171; + = 172; + = 173; + = 174; + = 175; + = 176; + = 177; + = 178; + = 179; + = 180; + = 181; + = 182; + = 183; + = 184; + = 185; + = 186; + = 187; + = 188; + = 189; + = 190; + = 191; + = 192; + = 193; + = 194; + = 195; + = 196; + = 197; + = 198; + = 199; + = 200; + = 201; + = 202; + = 203; + = 204; + = 205; + = 206; + = 207; + = 208; + = 209; + = 210; + = 211; + = 212; + = 213; + = 214; + = 215; + = 216; + = 217; + = 218; + = 219; + = 220; + = 221; + = 222; + = 223; + = 224; + = 225; + = 226; + = 227; + = 228; + = 229; + = 230; + = 231; + = 232; + = 233; + = 234; + = 235; + = 236; + = 237; + = 238; + = 239; + = 240; + = 241; + = 242; + = 243; + = 244; + = 245; + = 246; + = 247; + = 248; + = 249; + = 250; + = 251; + = 252; + = 253; + 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 = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; + alias = ; +}; + +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 { [ Escape ] }; + key { [ 1, exclam ] }; + key { [ 2, at ] }; + key { [ 3, numbersign ] }; + key { [ 4, dollar ] }; + key { [ 5, percent ] }; + key { [ 6, asciicircum ] }; + key { [ 7, ampersand ] }; + key { [ 8, asterisk ] }; + key { [ 9, parenleft ] }; + key { [ 0, parenright ] }; + key { [ minus, underscore ] }; + key { [ equal, plus ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ BackSpace, BackSpace, NoSymbol, NoSymbol, Terminate_Server ] + }; + key { [ Tab, ISO_Left_Tab ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ q, Q ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ w, W ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ e, E ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ r, R ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ t, T ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ y, Y ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ u, U ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ i, I ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ o, O ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ p, P ] + }; + key { [ bracketleft, braceleft ] }; + key { [ bracketright, braceright ] }; + key { [ Return ] }; + key { [ Control_L ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ a, A ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ s, S ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ d, D ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ f, F ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ g, G ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ h, H ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ j, J ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ k, K ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ l, L ] + }; + key { [ semicolon, colon ] }; + key { [ apostrophe, quotedbl ] }; + key { [ grave, asciitilde ] }; + key { [ Shift_L ] }; + key { [ backslash, bar ] }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ z, Z ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ x, X ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ c, C ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ v, V ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ b, B ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ n, N ] + }; + key { + type= "ALPHABETIC", + symbols[Group1]= [ m, M ] + }; + key { [ comma, less ] }; + key { [ period, greater ] }; + key { [ slash, question ] }; + key { [ Shift_R ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Multiply, KP_Multiply, KP_Multiply, KP_Multiply, XF86ClearGrab ] + }; + key { [ Alt_L, Meta_L ] }; + key { [ space ] }; + key { [ Control_L, Control_L ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F1, F1, F1, F1, XF86Switch_VT_1 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F2, F2, F2, F2, XF86Switch_VT_2 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F3, F3, F3, F3, XF86Switch_VT_3 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F4, F4, F4, F4, XF86Switch_VT_4 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F5, F5, F5, F5, XF86Switch_VT_5 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F6, F6, F6, F6, XF86Switch_VT_6 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F7, F7, F7, F7, XF86Switch_VT_7 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F8, F8, F8, F8, XF86Switch_VT_8 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F9, F9, F9, F9, XF86Switch_VT_9 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F10, F10, F10, F10, XF86Switch_VT_10 ] + }; + key { [ Num_Lock ] }; + key { [ Scroll_Lock ] }; + key { [ KP_Home, KP_7 ] }; + key { [ KP_Up, KP_8 ] }; + key { [ KP_Prior, KP_9 ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Subtract, KP_Subtract, KP_Subtract, KP_Subtract, XF86Prev_VMode ] + }; + key { [ KP_Left, KP_4 ] }; + key { [ KP_Begin, KP_5 ] }; + key { [ KP_Right, KP_6 ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Add, KP_Add, KP_Add, KP_Add, XF86Next_VMode ] + }; + key { [ KP_End, KP_1 ] }; + key { [ KP_Down, KP_2 ] }; + key { [ KP_Next, KP_3 ] }; + key { [ KP_Insert, KP_0 ] }; + key { [ KP_Delete, KP_Decimal ] }; + key { [ ISO_Level3_Shift ] }; + key { + type= "FOUR_LEVEL", + symbols[Group1]= [ less, greater, bar, brokenbar ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F11, F11, F11, F11, XF86Switch_VT_11 ] + }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ F12, F12, F12, F12, XF86Switch_VT_12 ] + }; + key { [ Katakana ] }; + key { [ Hiragana ] }; + key { [ Henkan_Mode ] }; + key { [ Hiragana_Katakana ] }; + key { [ Muhenkan ] }; + key { [ KP_Enter ] }; + key { [ Control_R ] }; + key { + type= "CTRL+ALT", + symbols[Group1]= [ KP_Divide, KP_Divide, KP_Divide, KP_Divide, XF86Ungrab ] + }; + key { + type= "PC_ALT_LEVEL2", + symbols[Group1]= [ Print, Sys_Req ] + }; + key { + type= "TWO_LEVEL", + symbols[Group1]= [ Alt_R, Meta_R ] + }; + key { [ Linefeed ] }; + key { [ Home ] }; + key { [ Up ] }; + key { [ Prior ] }; + key { [ Left ] }; + key { [ Right ] }; + key { [ End ] }; + key { [ Down ] }; + key { [ Next ] }; + key { [ Insert ] }; + key { [ Delete ] }; + key { [ XF86AudioMute ] }; + key { [ XF86AudioLowerVolume ] }; + key { [ XF86AudioRaiseVolume ] }; + key { [ XF86PowerOff ] }; + key { [ KP_Equal ] }; + key { [ plusminus ] }; + key { + type= "PC_CONTROL_LEVEL2", + symbols[Group1]= [ Pause, Break ] + }; + key { [ XF86LaunchA ] }; + key { [ KP_Decimal, KP_Decimal ] }; + key { [ Hangul ] }; + key { [ Hangul_Hanja ] }; + key { [ Super_L ] }; + key { [ Super_R ] }; + key { [ Menu ] }; + key { [ Cancel ] }; + key { [ Redo ] }; + key { [ SunProps ] }; + key { [ Undo ] }; + key { [ SunFront ] }; + key { [ XF86Copy ] }; + key { [ XF86Open ] }; + key { [ XF86Paste ] }; + key { [ Find ] }; + key { [ XF86Cut ] }; + key { [ Help ] }; + key { [ XF86MenuKB ] }; + key { [ XF86Calculator ] }; + key { [ XF86Sleep ] }; + key { [ XF86WakeUp ] }; + key { [ XF86Explorer ] }; + key { [ XF86Send ] }; + key { [ XF86Xfer ] }; + key { [ XF86Launch1 ] }; + key { [ XF86Launch2 ] }; + key { [ XF86WWW ] }; + key { [ XF86DOS ] }; + key { [ XF86ScreenSaver ] }; + key { [ XF86RotateWindows ] }; + key { [ XF86TaskPane ] }; + key { [ XF86Mail ] }; + key { [ XF86Favorites ] }; + key { [ XF86MyComputer ] }; + key { [ XF86Back ] }; + key { [ XF86Forward ] }; + key { [ XF86Eject ] }; + key { [ XF86Eject, XF86Eject ] }; + key { [ XF86AudioNext ] }; + key { [ XF86AudioPlay, XF86AudioPause ] }; + key { [ XF86AudioPrev ] }; + key { [ XF86AudioStop, XF86Eject ] }; + key { [ XF86AudioRecord ] }; + key { [ XF86AudioRewind ] }; + key { [ XF86Phone ] }; + key { [ XF86Tools ] }; + key { [ XF86HomePage ] }; + key { [ XF86Reload ] }; + key { [ XF86Close ] }; + key { [ XF86ScrollUp ] }; + key { [ XF86ScrollDown ] }; + key { [ parenleft ] }; + key { [ parenright ] }; + key { [ XF86New ] }; + key { [ Redo ] }; + key { [ XF86Tools ] }; + key { [ XF86Launch5 ] }; + key { [ XF86Launch6 ] }; + key { [ XF86Launch7 ] }; + key { [ XF86Launch8 ] }; + key { [ XF86Launch9 ] }; + key { [ XF86AudioMicMute ] }; + key { [ XF86TouchpadToggle ] }; + key { [ XF86TouchpadOn ] }; + key { [ XF86TouchpadOff ] }; + key { [ Mode_switch ] }; + key { [ NoSymbol, Alt_L ] }; + key { [ NoSymbol, Meta_L ] }; + key { [ NoSymbol, Super_L ] }; + key { [ NoSymbol, Hyper_L ] }; + key { [ XF86AudioPlay ] }; + key { [ XF86AudioPause ] }; + key { [ XF86Launch3 ] }; + key { [ XF86Launch4 ] }; + key { [ XF86LaunchB ] }; + key { [ XF86Suspend ] }; + key { [ XF86Close ] }; + key { [ XF86AudioPlay ] }; + key { [ XF86AudioForward ] }; + key { [ Print ] }; + key { [ XF86WebCam ] }; + key { [ XF86Mail ] }; + key { [ XF86Messenger ] }; + key { [ XF86Search ] }; + key { [ XF86Go ] }; + key { [ XF86Finance ] }; + key { [ XF86Game ] }; + key { [ XF86Shop ] }; + key { [ Cancel ] }; + key { [ XF86MonBrightnessDown ] }; + key { [ XF86MonBrightnessUp ] }; + key { [ XF86AudioMedia ] }; + key { [ XF86Display ] }; + key { [ XF86KbdLightOnOff ] }; + key { [ XF86KbdBrightnessDown ] }; + key { [ XF86KbdBrightnessUp ] }; + key { [ XF86Send ] }; + key { [ XF86Reply ] }; + key { [ XF86MailForward ] }; + key { [ XF86Save ] }; + key { [ XF86Documents ] }; + key { [ XF86Battery ] }; + key { [ XF86Bluetooth ] }; + key { [ XF86WLAN ] }; + modifier_map Control { }; + modifier_map Shift { }; + modifier_map Shift { }; + modifier_map Mod1 { }; + modifier_map Control { }; + modifier_map Mod2 { }; + modifier_map Mod5 { }; + modifier_map Control { }; + modifier_map Mod1 { }; + modifier_map Mod4 { }; + modifier_map Mod4 { }; + modifier_map Mod5 { }; + modifier_map Mod1 { }; + modifier_map Mod4 { }; + modifier_map Mod4 { }; +}; + +xkb_geometry "pc(pc105)" { + + width= 470; + height= 180; + + alias = ; + alias = ; + + 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 { + { , "NORM", 1 }, + { , "NORM", 20, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 11, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 11, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 8, color="white" }, + { , "NORM", 1, color="white" }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, + { , "BKSP", 1, color="grey20" } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "TABK", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "RTRN", 1, color="grey20" } + }; + }; + row { + top= 39; + left= 1; + keys { + { , "CAPS", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 } + }; + }; + row { + top= 58; + left= 1; + keys { + { , "LFSH", 1, color="grey20" }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, + { , "RTSH", 1, color="grey20" } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "MODK", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SPCE", 1 }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "SMOD", 1, color="grey20" }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 } + }; + }; + row { + top= 58; + left= 20; + keys { + { , "NORM", 1 } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "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 { + { , "NORM", 1 }, { , "NORM", 1 }, + { , "NORM", 1 }, { , "NORM", 1 } + }; + }; + row { + top= 20; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "KPAD", 1 } + }; + }; + row { + top= 39; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" } + }; + }; + row { + top= 58; + left= 1; + keys { + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "NORM", 1, color="white" }, + { , "KPAD", 1 } + }; + }; + row { + top= 77; + left= 1; + keys { + { , "KP0", 1, color="white" }, + { , "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"; + }; +}; + +};