koturnの日記

普通の人です.ブログ上のコードはコピペ自由です.

C言語でメモリ上のコードを実行する

(あまり低レイヤに詳しくない人間がこの記事を書いているので,信憑性については注意すること)

C言語といえば,自由度の高いプログラミングである. メモリ上に機械語を書いて,それを実行したいという欲求は多くあるだろう. 例えば,次のHello Worldプログラムなんかがそうだ.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>

static int stack[128 * 1024];
static unsigned char code[] = {
  0x53, 0x55, 0x41, 0x54, 0x48, 0x89, 0xfb, 0x48, 0x89, 0xf5, 0x49, 0x89, 0xd4, 0x41, 0x83, 0x04,
  0x24, 0x09, 0x41, 0x8b, 0x04, 0x24, 0x85, 0xc0, 0x0f, 0x84, 0x25, 0x00, 0x00, 0x00, 0x49, 0x83,
  0xc4, 0x04, 0x41, 0x83, 0x04, 0x24, 0x08, 0x49, 0x83, 0xc4, 0x04, 0x41, 0x83, 0x04, 0x24, 0x0b,
  0x49, 0x83, 0xc4, 0x04, 0x41, 0x83, 0x04, 0x24, 0x05, 0x49, 0x83, 0xec, 0x0c, 0x41, 0xff, 0x0c,
  0x24, 0xeb, 0xcf, 0x49, 0x83, 0xc4, 0x04, 0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3, 0x49, 0x83, 0xc4,
  0x04, 0x41, 0x83, 0x04, 0x24, 0x02, 0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3, 0x41, 0x83, 0x04, 0x24,
  0x07, 0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3, 0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3, 0x41, 0x83, 0x04,
  0x24, 0x03, 0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3, 0x49, 0x83, 0xc4, 0x04, 0x41, 0xff, 0x0c, 0x24,
  0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3, 0x41, 0x83, 0x2c, 0x24, 0x0c, 0x49, 0x8b, 0x3c, 0x24, 0xff,
  0xd3, 0x49, 0x83, 0xec, 0x04, 0x41, 0x83, 0x04, 0x24, 0x08, 0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3,
  0x41, 0x83, 0x2c, 0x24, 0x08, 0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3, 0x41, 0x83, 0x04, 0x24, 0x03,
  0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3, 0x41, 0x83, 0x2c, 0x24, 0x06, 0x49, 0x8b, 0x3c, 0x24, 0xff,
  0xd3, 0x41, 0x83, 0x2c, 0x24, 0x08, 0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3, 0x49, 0x83, 0xc4, 0x04,
  0x41, 0xff, 0x04, 0x24, 0x49, 0x8b, 0x3c, 0x24, 0xff, 0xd3, 0x41, 0x5c, 0x5d, 0x5b, 0xc3,
};

int
main(void)
{
  long page_size = sysconf(_SC_PAGESIZE) - 1;
  mprotect((void *) code, (sizeof(code) + page_size) & ~page_size, PROT_READ | PROT_EXEC);
  ((void (*)(int (*)(int), int (*)(), int *)) (unsigned char *) code)(putchar, getchar, stack);
  return EXIT_SUCCESS;
}

これは64bit上のLinuxならば,おそらく実行可能なコードであろう. コードの元ネタは,herumi/xbyakのサンプルコードのBrainfuck処理系:sample/bf.cppが,Hello Worldを出力するBrainfuckコード

+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.
------------.<++++++++.--------.+++.------.--------.>+.

を元に出力したC言語ソースコードである.

配列 code[]機械語であり,システムコールmprotect() を用いてこの領域に実行可能属性を付加している. 実行可能にしたメモリ領域の先頭を指すポインタを取得し,それを関数ポインタとしてコールすると,その領域の機械語を実行できるというカラクリのようだ.

mprotect()mmap() で確保した領域にのみにしか用いることができないが,staticなグローバル変数ならば問題はないらしい(このあたりについてのことは,僕の力量不足で調査できていない). また,僕の環境では,malloc() で確保した領域に mprotect() を用いたが,うまくいかなかった.

ところで,システムコールmprotect() は当然ながらWindows環境下で用いることはできない. Windowsで,ある領域に実行可能属性を付加するにはどうすればいいのだろうか? 調査したところ,Windows APIVirtualProtectmprotect() に相当するらしい. 前述のC言語ソースコードWindows(64bit)用に書き直すと,以下のようになる. もちろん,機械語部分はWindows用に書き直してある.

#include <stdio.h>
#include <stdlib.h>
#ifndef WIN32_LEAN_AND_MEAN
#  define WIN32_LEAN_AND_MEAN
#  define WIN32_LEAN_AND_MEAN_IS_NOT_DEFINED
#endif
#include <windows.h>
#ifdef WIN32_LEAN_AND_MEAN_IS_NOT_DEFINED
#  undef WIN32_LEAN_AND_MEAN_IS_NOT_DEFINED
#  undef WIN32_LEAN_AND_MEAN
#endif

static int stack[128 * 1024];
static unsigned char code[] = {
  0x56, 0x57, 0x55, 0x48, 0x89, 0xce, 0x48, 0x89, 0xd7, 0x4c, 0x89, 0xc5, 0x83, 0x45, 0x00, 0x09,
  0x8b, 0x45, 0x00, 0x85, 0xc0, 0x0f, 0x84, 0x21, 0x00, 0x00, 0x00, 0x48, 0x83, 0xc5, 0x04, 0x83,
  0x45, 0x00, 0x08, 0x48, 0x83, 0xc5, 0x04, 0x83, 0x45, 0x00, 0x0b, 0x48, 0x83, 0xc5, 0x04, 0x83,
  0x45, 0x00, 0x05, 0x48, 0x83, 0xed, 0x0c, 0xff, 0x4d, 0x00, 0xeb, 0xd4, 0x48, 0x83, 0xc5, 0x04,
  0x48, 0x8b, 0x4d, 0x00, 0x48, 0x83, 0xec, 0x20, 0xff, 0xd6, 0x48, 0x83, 0xc4, 0x20, 0x48, 0x83,
  0xc5, 0x04, 0x83, 0x45, 0x00, 0x02, 0x48, 0x8b, 0x4d, 0x00, 0x48, 0x83, 0xec, 0x20, 0xff, 0xd6,
  0x48, 0x83, 0xc4, 0x20, 0x83, 0x45, 0x00, 0x07, 0x48, 0x8b, 0x4d, 0x00, 0x48, 0x83, 0xec, 0x20,
  0xff, 0xd6, 0x48, 0x83, 0xc4, 0x20, 0x48, 0x8b, 0x4d, 0x00, 0x48, 0x83, 0xec, 0x20, 0xff, 0xd6,
  0x48, 0x83, 0xc4, 0x20, 0x83, 0x45, 0x00, 0x03, 0x48, 0x8b, 0x4d, 0x00, 0x48, 0x83, 0xec, 0x20,
  0xff, 0xd6, 0x48, 0x83, 0xc4, 0x20, 0x48, 0x83, 0xc5, 0x04, 0xff, 0x4d, 0x00, 0x48, 0x8b, 0x4d,
  0x00, 0x48, 0x83, 0xec, 0x20, 0xff, 0xd6, 0x48, 0x83, 0xc4, 0x20, 0x83, 0x6d, 0x00, 0x0c, 0x48,
  0x8b, 0x4d, 0x00, 0x48, 0x83, 0xec, 0x20, 0xff, 0xd6, 0x48, 0x83, 0xc4, 0x20, 0x48, 0x83, 0xed,
  0x04, 0x83, 0x45, 0x00, 0x08, 0x48, 0x8b, 0x4d, 0x00, 0x48, 0x83, 0xec, 0x20, 0xff, 0xd6, 0x48,
  0x83, 0xc4, 0x20, 0x83, 0x6d, 0x00, 0x08, 0x48, 0x8b, 0x4d, 0x00, 0x48, 0x83, 0xec, 0x20, 0xff,
  0xd6, 0x48, 0x83, 0xc4, 0x20, 0x83, 0x45, 0x00, 0x03, 0x48, 0x8b, 0x4d, 0x00, 0x48, 0x83, 0xec,
  0x20, 0xff, 0xd6, 0x48, 0x83, 0xc4, 0x20, 0x83, 0x6d, 0x00, 0x06, 0x48, 0x8b, 0x4d, 0x00, 0x48,
  0x83, 0xec, 0x20, 0xff, 0xd6, 0x48, 0x83, 0xc4, 0x20, 0x83, 0x6d, 0x00, 0x08, 0x48, 0x8b, 0x4d,
  0x00, 0x48, 0x83, 0xec, 0x20, 0xff, 0xd6, 0x48, 0x83, 0xc4, 0x20, 0x48, 0x83, 0xc5, 0x04, 0xff,
  0x45, 0x00, 0x48, 0x8b, 0x4d, 0x00, 0x48, 0x83, 0xec, 0x20, 0xff, 0xd6, 0x48, 0x83, 0xc4, 0x20,
  0x5d, 0x5f, 0x5e, 0xc3,
};

int
main(void)
{
  DWORD old_protect;
  VirtualProtect((LPVOID) code, sizeof(code), PAGE_EXECUTE_READWRITE, &old_protect);
  ((void (*)(int (*)(int), int (*)(), int *)) (unsigned char *) code)(putchar, getchar, stack);
  return EXIT_SUCCESS;
}

ちなみに,Cygwinだと mprotect() を利用可能であるが,使用しても意味がなかった. Cygwinでも VirtualProtect() を用いなければならない.

まとめ

mprotect() または VirtualProtect() を用いることによって,メモリのパーミッションを変更できる. 実行可能属性を付加することにより,動的に生成した機械語を実行することも可能である. これがJITコンパイルの基礎となる仕組みなのだろう.

NeoBundleの使い方

他人の.vimrcを見ていると,NeoBundleを用いてプラグインの管理をしている人が多い. かくいう僕もその一人である. しかし,NeoBundleの事情に詳しくない人は,どうも古い情報を参照しているため,以下のような古い仕様のNeoBundleに沿った記述や,そもそもイケてない記述をしていることがある.

if has('vim_starting')
  " ~/.vimrcの読み込みならば,自動的にnocompatibleになっているはず
  set nocompatible
  " 最近のNeoBundleでは必要ない
  filetype off
  set runtimepath+=~/.vim/bundle/neobundle.vim
endif
" neobundle#rc() より,neobundle#begin() を用いるべき
call neobundle#rc(expand('~/.vim/bundle/'))
" neobunde.vim自体は読み込みを行わないNeoBundleFetchで管理すべき
NeoBundle 'Shougo/neobundle.vim'

NeoBundle 'foo/bar'
NeoBundleLazy 'hoge/piyo', {
      \ 'autoload': {
      \   'commands': ['Piyo'],
      \ }
      \}

" ...

filetype plugin indent on

set nocompatibleは副作用の多いオプション設定で,他のオプション設定も変更することになる. 例えば,オプションhistoryの値がデフォルトの50に書き換えられたりする. デフォルトでvimが使用する.vimrc,例えば,~/.vimrcであれば,自動的にset nocompatibleになっているので,わざわざ.vimrc中で設定することは無い. また,

$ vim -u ~/other.vimrc

等としてvimを起動する場合であっても,オプション-Nを付加して起動すればよい. どうしても,set nocompatibleと書いて安心したいのであれば,

if !&compatible
  set nocompatible
endif

のように,互換モードか否かを判断してからにするべきだろう.

filetype offは,最近のNeoBundleでは内部で行っているため,わざわざ.vimrcに記述する必要が無いそうだ.

neobundle#rc()を用いていた場合,警告が出るので,言わずともneobundle#begin()neobundle#end()を用いる設定に書き換えることだろう.

以上のことを踏まえると,最近のNeoBundleでは以下のように記述するのが良い.

if has('vim_starting')
  set rtp+=~/.vim/bundle/neobundle.vim
endif
call neobundle#begin()
NeoBundleFetch 'Shougo/neobundle.vim'

NeoBundle 'foo/bar'
NeoBundleLazy 'hoge/piyo', {
      \ 'autoload': {
      \   'commands': ['Piyo'],
      \ }
      \}
call neobundle#end()
filetype plugin indent on

if !has('vim_starting')
  call neobundle#call_hook('on_source')
endif

また,NeoBundleのキャッシュ機能を利用するならば,以下のように記述する. (キャッシュ機能は実装当初と現在を比較すると,大きく仕様変更されているが,古い方の仕様は紹介しない)

if has('vim_starting')
  set rtp+=~/.vim/bundle/neobundle.vim
endif
call neobundle#begin()

" neobundle#load_cache() はキャッシュが最新ならば,0を返却する関数
if neobundle#load_cache()
  NeoBundleFetch 'Shougo/neobundle.vim'
  NeoBundle 'foo/bar'
  " Lazyの設定等はキャッシュされる
  NeoBundleLazy 'hoge/piyo', {
        \ 'autoload': {
        \   'commands': ['Piyo'],
        \ }
        \}
  NeoBundleSaveCache
endif
call neobundle#end()
filetype plugin indent on

" キャッシュされないプラグインのグローバル変数等の設定をここに
" neobundle#tap() は引数のプラグインがロードされているならば,1を返却する
if neobundle#tap('bar')
  let g:bar#var01 = 10
  let g:bar#var02 = ['a', 'b', 'c']
  call neobundle#untap()
endif

if neobundle#tap('piyo')
  " hooks.on_source() でプラグイン読み込み時の処理を指定できる
  " この程度(グローバル変数を設定するだけ)の処理ならば,無理に利用する必要はない
  " 時間のかかる処理などを記述すると吉
  function! neobundle#tapped.hooks.on_source(bundle) abort
    let g:piyo#var01 = 1
    let g:piyo#var02 = 'abc'
  endfunction
  call neobundle#untap()
endif

if !has('vim_starting')
  call neobundle#call_hook('on_source')
endif

memolist.vimの遅延読み込み設定

glidenote/memolist.vimというメモを取るためのVimプラグインがある, Shougo/neobundle.vimでこのプラグインを管理し,遅延読み込み設定を行った.

NeoBundleLazy 'glidenote/memolist.vim', {
      \ 'autoload': {'commands': ['MemoGrep', 'MemoList', 'MemoNew']}
      \}

しかし遅延読み込み設定をした場合,デフォルト設定のままだと :MemoList を実行しても,1回目はメモファイル一覧が表示できなかった. すなわち,Netrwでメモを保存したディレクトリを開くことができなかった. (僕の環境でのみ起こる問題かもしれない)

そこで,以下のようにコマンドを再定義し,処理を2回呼び出すようにして,この問題を回避した. (functionsfunction_prefix を指定していないが,うまくいった)

NeoBundleLazy 'glidenote/memolist.vim', {
      \ 'autoload': {'commands': ['MemoGrep', 'MemoNew']}
      \}
command! -nargs=0 MemoList  silent call memolist#list() | call memolist#list()

:MemoListShougo/unite.vimShougo/vimfiler.vimを用いるようにした場合,このような問題は発生しなかった.

Vimで<C-c>を含むマッピングをするときに注意すること

前書き

まずは,簡単な説明をする.

$ vim -u NONE

としてvimを立ち上げて, :nnoremap :<C-u>a<C-c>echo 'Hello World!'<CR> のようにマッピングしてみよう. そして,ノーマルモードにて, a<C-c> と入力すると, Hello World! とエコーされず,

Type  :quit<Enter>  to exit Vim

と表示されると思う. (このメッセージはロケールに依存する.上記は language messages C のときの例)

本題

<C-c> を含むマッピングを行うときには注意しなければならない. vim-jpのissueにあるように,

nnoremap z<C-c> :<C-u>echo 'key mapping works'<CR>

マッピングをして,ノーマルモードで z<C-c> と押すと <C-c> のデフォルトの動作をしてしまう. だが,

nnoremap <C-c> <NOP>

のように, <C-c> に対して何か適当なマッピングを行い,再度 z<C-c> と押すと,

key mapping works

と表示される. 古いVimであれば,

nunmap <C-c>

として, z<C-c> と入力しても,

key mapping works

と表示されていたが,いつからか(Vim 8.4.xxx 以降あたり)から再び

Type  :quit<Enter>  to exit Vim

と表示されるようになり,一貫性のある動作をするようになった.

このように <C-c> 単体に何らかのマッピングをしていない場合, <C-c> を含むマッピングができないので,

nnoremap <C-c> <NOP>

もしくは,

nnoremap <C-c> <C-c>

マッピングしておくことをオススメする. 後者であれば,デフォルトの <C-c> 単体の動作を消すことなく, <C-c> を含むマッピングが可能になる.

この記事中ではノーマルモードでのマッピングのみ取り上げたが,他のモードでのマッピングについても同様である.

参考

VimでUndo履歴を消去する

:help clear-undo を見ると,

let old_undolevels = &undolevels
set undolevels=-1
exe "normal a \<BS>\<Esc>"
let &undolevels = old_undolevels
unlet old_undolevels

とすれば,undo履歴が消去できるとのこと. しかし,コマンド normal は,マッピングを展開するので, normal! を用いた方がよい. また,オプション undolevels の値を一時的とはいえ,グローバルに変更する必要は無いので,以下のようにした方がより望ましいだろう.

let old_undolevels = &l:undolevels
setlocal undolevels=-1
execute "normal! a \<BS>\<Esc>"
let &l:undolevels = old_undolevels
unlet old_undolevels

これをコマンド化し,

function! s:clear_undo() abort
  let old_undolevels = &undolevels
  setlocal undolevels=-1
  execute "normal! a \<BS>\<Esc>"
  let &l:undolevels = old_undolevels
endfunction
command! -bar ClearUndo  call s:clear_undo()

とするのも良いだろう. (Undo履歴を消したいというのはかなりレアなケースだと思うが)

Vimでルートユーザとして保存する

rootユーザでVimを起動したり, sudoeditVimを起動しなかった場合,権限の無いファイルを編集後,保存しようとしても保存できない. 割とあるあるなことだと思う. (特にVimに閉じこもる人にとっては)

そんなとき,

:w !sudo tee > /dev/null %

と実行することで,ルートユーザとして保存できる. これはよく紹介されている手法で,書籍「実践Vim 思考のスピードで編集しよう! 」でも紹介されている. 簡単にググっただけでも,

のように数多くの記事で紹介されている. 上で紹介した記事や,人の.vimrcでよく見掛けるのが,

cnoremap w!! w !sudo tee > /dev/null %

cabbrev w!! w !sudo tee > /dev/null %

という設定だが,個人的にはコマンドにまとめておく方が好きである.

if executable('sudo')
  function! s:save_as_root(bang, filename) abort
    execute 'write' . a:bang '!sudo tee > /dev/null' (a:filename ==# '' ? '%' : a:filename)
  endfunction
else
  function! s:save_as_root(bang, filename) abort
    echoerr 'sudo is not supported in this environment'
  endfunction
endif
command! -bar -bang -nargs=? -complete=file SudoWrite  call s:save_as_root('<bang>', <q-args>)

これを, w[rite] コマンドと同じように, :SudoWrite と実行するとよい. なお,コマンドが呼び出すのはたった1行の関数なので,わざわざ関数を作る必要はないかもしれない.

if executable('sudo')
  command! -bar -bang -nargs=? -complete=file SudoWrite
        \ execute 'write' . '<bang>' '!sudo tee > /dev/null' (<q-args> ==# '' ? '%' : <q-args>)
else
  command! -bar -bang -nargs=? -complete=file SudoWrite
        \ echoerr 'sudo is not supported in this environment'
endif

正直, bang に関してはあまり意味は無いが,元々の :writebang を取ることができるので,ついでに指定しておいただけだ.

参考

NeoBundleLazyのautoloadのcommands指定で頑張った話

neobundle.vimには,遅延読み込み機能があり,適切に設定してやることで,プラグインの使用感はそのままで,Vimの起動時間を短縮することができる. autoloadの設定には,

  • ファイルタイプ検出時 filetypes
  • ファイル名検出時 filename_patterns
  • コマンド実行時 commands
  • 関数実行時 functions, function_prefix
  • マップ実行時 mappings
  • Uniteのソース読み込み時 unite_sources
  • インサートモードに入った時 insert

など,様々なタイミングで,プラグインのパスを runtimepath に追加し,読み込みを行うことができる. コマンドの実行時の検出は,仮のコマンドを定義しておいて,それが実行されたとき,プラグイン本体を読み込む仕組みとなっていると思われる. そして,仮のコマンドには引数の補完関数を指定することもできる. basyura/TweetVimのNeoBundleLazyの設定を例にとると,以下のような形になるだろう.

NeoBundleLazy 'basyura/TweetVim'
if neobundle#tap('TweetVim')
  call neobundle#config({
        \ 'depends': ['tyru/open-browser.vim', 'basyura/twibill.vim'],
        \ 'autoload': {
        \   'commands': [
        \     'TweetVimAccessToken',
        \     'TweetVimAddAccount',
        \     'TweetVimClearIcon',
        \     'TweetVimCommandSay',
        \     'TweetVimCurrentLineSay',
        \     'TweetVimHomeTimeline',
        \     {'name': 'TweetVimListStatuses', 'complete': 'custom,tweetvim#complete#list'},
        \     'TweetVimMentions',
        \     {'name': 'TweetVimSay', 'complete': 'custom,tweetvim#complete#account'},
        \     {'name': 'TweetVimSearch', 'complete': 'custom,tweetvim#complete#search'},
        \     {'name': 'TweetVimSwitchAccount', 'complete': 'custom,tweetvim#complete#account'},
        \     'TweetVimUserStream',
        \     {'name': 'TweetVimUserTimeline', 'complete': 'custom,tweetvim#complete#screen_name'},
        \     'TweetVimVersion'
        \   ],
        \   'unite_sources': 'tweetvim'
        \ }
        \})
  call neobundle#untap()
endif

この例のautoloadのcommands設定にあるように,コマンドの補完関数には, customcustomlist に,プラグインautoload/hoge.vim で定義されている関数を指定してもよい. その場合,コマンド引数のTab補完を行った瞬間に,プラグインのロードが行われる.

しかし,プラグインによっては, customcustomlist に, plugin/hoge.vimスクリプトローカル関数を指定している場合もある. スクリプトローカル関数はneobundle側からは見えないので,指定することはできない. プラグインのロードが行われるまで我慢してもよいが,少し気分が悪い.

そこで,.vimrcに plugin/hoge.vim で定義されている関数をコピペし,neobundleから見えるようにスクリプト番号と共に渡してやるとよい. その補完関数はプラグインのロード後には不必要になるので,プラグイン読み込み時に呼び出されるフック関数で消去する,あるいは消去するオートコマンドを定義する.

そういったプラグインの例として,mattn/gist-vimosyo-manga/vim-reanimateなどがあるので,その2つのプラグインを例にとって,設定例を紹介する.

if has('vim_starting')
  set rtp+=~/.vim/bundle/neobundle.vim
endif
call neobundle#begin()
NeoBundleFetch 'Shougo/neobundle.vim'

let g:neobundle#default_options = {'_': {'verbose': 1}}
augroup CompleteDummy
  autocmd!
augroup END

function! s:SID() abort
  return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
endfun
let s:sid = s:SID()
delfunction s:SID

function! s:to_global_name(scriptlocal_funcname) abort
  return '<SNR>' . s:sid . '_' . a:scriptlocal_funcname
endfunction

" 適当なイベントが発生したら,補完関数を消去
function! s:delete_function_lazy(funcname) abort
  execute 'autocmd CompleteDummy CursorHold,CursorHoldI,CursorMoved,CursorMovedI,InsertEnter *'
        \ 'delfunction' a:funcname
        \ '| autocmd! CompleteDummy CursorHold,CursorHoldI,CursorMoved,CursorMovedI,InsertEnter *'
endfunction

if neobundle#load_cache()
  NeoBundleLazy 'mattn/gist-vim'
  NeoBundleLazy 'osyo-manga/vim-reanimate'

  " キャッシュされる設定はこっちに書く
  if neobundle#tap('gist-vim')
    call neobundle#config({
          \ 'autoload': {
          \   'commands': {'name': 'Gist', 'complete': 'customlist,' . s:to_global_name('gist_CompleteArgs')}
          \ }
          \})
    call neobundle#untap()
  endif

  if neobundle#tap('vim-reanimate')
    let s:_ = 'customlist,' . s:to_global_name('reanimate_save_point_completelist')
    call neobundle#config({
          \ 'autoload': {
          \   'commands': [
          \     {'name': 'ReanimateSave', 'complete': s:_},
          \     'ReanimateSaveCursorHold',
          \     'ReanimateSaveInput',
          \     {'name': 'ReanimateLoad', 'complete': s:_},
          \     'ReanimateLoadInput',
          \     'ReanimateLoadLatest',
          \     {'name': 'ReanimateSwitch', 'complete': s:_},
          \     {'name': 'ReanimateEditVimrcLocal', 'complete': s:_},
          \     'ReanimateUnLoad'
          \   ]
          \ }
          \})
    unlet s:_
    call neobundle#untap()
  endif
  NeoBundleSaveCache
endif
call neobundle#end()

" キャッシュされないグローバル変数の設定等はこっち
if neobundle#tap('gist-vim')
  " plugin/gist.vim を見てコピペ
  function! s:gist_CompleteArgs(arg_lead,cmdline,cursor_pos) abort
    return filter(["-p", "-P", "-a", "-m", "-e", "-s", "-d", "+1", "-1", "-f", "-c", "-l", "-la", "-ls", "-b",
          \ "--listall", "--liststar", "--list", "--multibuffer", "--private", "--public", "--anonymous", "--description", "--clipboard",
          \ "--rawurl", "--delete", "--edit", "--star", "--unstar", "--fork", "--browser"
          \ ], '!stridx(v:val, a:arg_lead)')
  endfunction
  function! neobundle#tapped.hooks.on_post_source(bundle) abort
    delfunction s:gist_CompleteArgs
  endfunction
  call neobundle#untap()
endif

if neobundle#tap('vim-reanimate')
  " plugin/reanimate.vim を見てコピペ
  function! s:reanimate_save_point_completelist(arglead, ...) abort
    return filter(reanimate#save_points(), "v:val =~? '" . a:arglead . "'")
  endfunction
  " neobundle#tapped.hooks.on_source() でもいい
  function! neobundle#tapped.hooks.on_post_source(bundle) abort
    " 一時的な補完関数の中でautoload関数をコールをしているので,
    " このタイミングで補完関数は消去できない
    " なので,適当なタイミングで一時的な補完関数を消去する
    call s:delete_function_lazy('s:reanimate_save_point_completelist')
  endfunction
  let g:reanimate_save_dir = '~/.vim/save'
  let g:reanimate_default_save_name = 'reanimate'
  let g:reanimate_sessionoptions = 'curdir,folds,help,localoptions,slash,tabpages,winsize'
  call neobundle#untap()
endif

if !has('vim_starting')
  call neobundle#call_hook('on_source')
endif
filetype plugin indent on