vimをバイナリエディタ的に使う。

vimがいい、vimがいいを言い続けて4年目。
元はといえば学校のMacemacsがやたら異常終了して、嫌気が差してvimを使い始めたのがハジマリ。
それまではWinユーザでメモ帳++なんぞを使っていたのですが...。
もしあのとき、emacsが異常終了しなければ、今頃自分は...。


そんなこんなで月日は流れ今となってはvim!vim!vim!な毎日。
暇さえあれば他人に布教し始める程のvim信者。
すべてのエディタはvim互換モードを持つべきだと言い張る今日この頃。


そういやバイナリエディタvim互換がいいな。
いや、いっそvimがいい!
素敵そうではないか、vimバイナリエディタ
矩形選択がおもしろい程に活躍しそうだ。
正規表現による:sやら、マクロにC-A/C-X。


そんなこんなのvim de バイナリエディタ


えっと、最初に一言だけ。
このエントリのまとめ: "vimで:help xxd"
備忘録ここまで。以下メモ程度に。


vimがインストールされている環境には必ず入っている(はず)のxxdというコマンド。
manを見てみると"make a hexdump or do the reverse"と書かれている。
何処を取ってxxdなのかは知らないが、eX-なんとか heX Dumpかなんかだろう。
16進数にdumpしたりその逆をするコマンドらしい。
vimと一緒にインストールされるので、多分大体のOSで何もせずに使えるはず。


で、vimのhelpにはこのxxdをフィルタとして使う方法が書いてある。
先に書いた":help xxd"で読めるのだが、

" vim -b : edit binary using xxd-format!
augroup Binary
  au!
  au BufReadPre  *.bin let &bin=1
  au BufReadPost *.bin if &bin | %!xxd
  au BufReadPost *.bin set ft=xxd | endif
  au BufWritePre *.bin if &bin | %!xxd -r
  au BufWritePre *.bin endif
  au BufWritePost *.bin if &bin | %!xxd
  au BufWritePost *.bin set nomod | endif
augroup END

を.vimrcに加えると幸せになれるぜ、f*cking my bro.! HAHAHA!!! と書いてある(書いてない)。
*.binのところをcomma-separatedで*.exeだか*.dllと書くとなお幸せ。


これはこれで、そのままでも十分使えるのだけどちょいちょいと手を加えていきます。

vim -bでバイナリエディタ

上のやつそのままだと、.vimrcに加えた拡張子しかバイナリエディタ的に開いてくれない。
まぁ、毎回.binにrenameすればいい、という話ではあるのだけど、vim -bで勝手に切り替わってくれるとなおベターかもしれない。
ということで、&binを&binaryに、BufReadPre以外の*.binを*にすればok。
これでvim -bでファイルを開くとバイナリエディタ的に働きます。
注意点としては普通に開いてから:set binaryなどしないように。最悪ファイルがトびます。

octetを1byteごとに区切る。

xxdの標準だと

echo vim | xxd
0000000: 7669 6d0a                                vim.

の様に2byteで1octet(?)になっています。
個人的にバイナリエディタといえば1byte 1octet(?)なのでxxdの引数に -g1を追加。

echo vim | xxd -g1
0000000: 76 69 6d 0a                                      vim.

うん。しっくり来る。

functionへの切り分け、カーソル位置の記憶

これはただ単に気になっただけ。
endifの位置とかかなり気持ち悪い。個人的に。
ということで、それぞれ関数に分けます。

augroup Binary
	autocmd!
	autocmd BufReadPre  *.bin let &binary = 1
	autocmd BufReadPost * call BinReadPost()
	autocmd BufWritePre * call BinWritePre()
	autocmd BufWritePost * call BinWritePost()
	function! BinReadPost()
		if &binary
			silent %!xxd -g1
			set ft=xxd
		endif
	endfunction
	function! BinWritePre()
		if &binary
			let s:saved_pos = getpos( '.' )
			silent %!xxd -r
		endif
	endfunction
	function! BinWritePost()
		if &binary
			silent %!xxd -g1
			call setpos( '.', s:saved_pos )
			set nomod
		endif
	endfunction
augroup END

ついでにwrite前後でカーソル位置が移動しないように記憶しておきます。
getpos/setposが無いと%!でカーソル位置が左上に持って行かれます。
これが地味にむかついたので修正


ところで、vimスクリプト分からないのだけど、スコープはs:でいいのかなぁ?
まぁ、動いているのでよしとします。

CursorHoldで再フォーマット

実際作業していると削除/挿入、変更などでxxdがread/write時の出力がずれてくることがあります。
欲を言えばリアルタイムに右側のascii出力が変わると便利なのですが、ここはCursorHoldで我慢。

autocmd CursorHold * call BinReHex()
function! BinReHex()
	if &binary
		let s:saved_pos = getpos( '.' )
		let s:modified = &modified
		silent %!xxd -r
		silent %!xxd -g1
		call setpos( '.', s:saved_pos )
		let &modified = s:modified
	endif
endfunction

一旦xxd -rでバイナリに戻して、再びhexに戻しています。


で、今いじりつつ気がついたのですが、xxdって結構ファイル壊しますね...orz
というより、1byte挿入するとその行の一番後ろのoctetが闇に葬り去られる...orz
逆に削除するとフォーマットエラーになるのかその前後、ないしそれ以降が闇に...orz
そりゃスネ夫もorzになりますよ...prz
CursorHoldはやめておいた方がいいかもしれない。
もしくはxxd以外を使うべきかも。


かつてbin2hexとhex2binというソフトを自作したことがあるのですが、今見たら何故かHaskell
しかも使ってみたらパースエラー...。
Cで書いた記憶があるのだがなぁ...。


そいつを使えば先に挙げたデータが闇に葬られる、ということは無くなりそうなのでそのうちbin2hexとhex2binについてエントリ書くやも。
まぁ、つまりは16進数とバイナリの相互変換できる物があれば、Read/WritePre/Postでちょめちょめするとバイナリエディタになりますよ、ということ。
そういえば.gzとかは標準でvimが展開/再圧縮するようなのでその辺のvimrcを眺めるとまた発見があるかも。
ということで、今回はここまで。
そのうち追記するかもです。