Windows環境で作られたzip書庫のファイル名化けを解消する方法
Linux で人から渡された zip 書庫を解凍すると、ファイル名が文字化けしている事が良くあります。
zip 書庫は、格納するファイルの文字コードをそのまま保存しており、Windows 環境で作った日本語ファイル名には、文字コード CP932 が使用されるため、UTF8 が使用されている最近の Linux 環境では文字化けするのは当然です。
このようなファイルは、convmv コマンドでファイル名の変換を試してみます。
$ unzip sample1.zip Archive: sample1.zip extracting: ?V?????t?H???_/?V?K?e?L?X?g ?h?L???????g.txt $ convmv -r -f cp932 -t utf8 * Your Perl version has fleas #37757 #49830 Starting a dry run without changes... mv "?V?????t?H???_/?V?K?e?L?X?g ?h?L???????g.txt" "?V?????t?H???_/新規テキスト ドキュメント.txt" mv "./?V?????t?H???_" "./新しいフォルダ" No changes to your files done. Use --notest to finally rename the files.
うまく変換されているようなら、--notest オプションを付けて変換を実行します。
全ての場合でこの方法でうまくいくならそれでいいのですが、一部の zip 書庫については、この方法でもうまくいかないことがあります。
$ unzip sample2.zip Archive: sample2.zip extracting: ?V?????t?H???_/?V?K?e?L?X?g ?h?L???????g.txt honda@galeon:/tmp$ convmv -r -f cp932 -t utf8 * Your Perl version has fleas #37757 #49830 Starting a dry run without changes... mv "?V?????t?H???_/?V?K?e?L?X?g ?h?L???????g.txt" "?V?????t?H???_/ノV?K稙祗禮稟 禀祗籵紆糜稟.txt" mv "./?V?????t?H???_" "./ノV鮹鴣穰礬糀秬" No changes to your files done. Use --notest to finally rename the files.
これは、unzip コマンドが解凍を実施する際に、ある条件*1でファイル名の変換を実施しているが原因のようです。
上のリンク先の方は、ファイル名変換を実行する関数を無効にした unzip コマンドを再コンパイルすることで対処されています。他にも debian だと、unzip-cp932 というパッケージ*2を利用する方法もあります。
自分の場合は、jar コマンドで解凍を行って対処しています。jar 形式は zip 書庫がそのまま使用されていることが多く、jar コマンドは zip 書庫の圧縮/解凍をすることも可能です。ちなみにオプションは、tar コマンドとほとんど同じです。
$ jar xvf sample2.zip created: ?V?????t?H???_/ extracted: ?V?????t?H???_/?V?K?e?L?X?g ?h?L???????g.txt
最近のディストリビューションは java パッケージと共に jar コマンドが公式に提供されていることが多いので、unzip コマンドを再コンパイルしたり、パッケージを独自に用意する必要がないのが、この方法のメリットです。
デメリットは、unzip を利用している file-roller 等では、問題が残ったままになることが挙げられますが、最終的に文字化けを解消するには、convmv コマンドの使用が必要なので、はじめから CUI で解凍を行うのが良さそうな気がします。
*1:使用する圧縮ソフトが影響するようです。
*2:http://debian.fam.cx/index.php?sarge%2FJapanese#content_1_70
Vimからはてなフォトライフに画像を投稿する
Vimからはてなフォトライフに画像を投稿するvimスクリプトを書いてみました。hatena.vimに追加して使用してください。画像を投稿すると、その画像のfotolife記法がVimの本文に追加されます。「Vimからはてなハイクに投稿する」も合わせてどうぞ。
画像の投稿には、はてなフォトライフAtomAPIを使用しました。WSSE認証ヘッダはシェルスクリプトでは難しそうでしたので、こちらの記事を元にrubyで生成しています。AtomAPIを使っているためcookieが使用できず、画像投稿時には毎回パスワードを要求されます。
動作には、rubyとhatena.vimが使用するcurlコマンドの他に、fileコマンドが必要です。
" WSSE認証ヘッダの生成 function s:WSSECreds(user, pass) let creds = system('ruby -r "digest/sha1" -r "time" -e "' . \'nonce = Array.new(10){ rand(0x100000000) }.pack(%!I*!);' . \'nonce_base64 = [nonce].pack(%!m!).chomp;' . \'now = Time.now.utc.iso8601;' . \'digest = [Digest::SHA1.digest(nonce + now + %!' . a:pass . '!)].pack(%!m!).chomp;' . \'print %!UsernameToken !' . \'+ %!Username=\"%s\", ! % %!' . a:user . '!' . \'+ %!PasswordDigest=\"%s\", ! % digest' . \'+ %!Nonce=\"%s\", ! % nonce_base64' . \'+ %!Created=\"%s\"! % now;' . \'"') if v:shell_error return else return creds endif endfunction " 指定したファイルをはてなフォトライフに投稿する " :Usage " :FotolifePost <filename> command! -nargs=1 -complete=file FotolifePost call <SID>FotolifePost('<args>') function s:FotolifePost(filename) let mimetype = system('file -bi "' . a:filename . '"') if v:shell_error echo "指定したファイルがありません" return endif let tmpfile = tempname() let title = input('タイトル: ') " リクエストXMLの作成 let result_encode = system('ruby -r "base64" -e "' . \'request_xml = %!<entry xmlns=\"http://purl.org/atom/ns#\">\n' . \'<title>' . title . '</title>\n' . \'<content mode=\"base64\" type=\"' . mimetype . '\">\n' . \'%s</content>\n' . \'</entry>!;' . \'open(%!' . tmpfile . '!, %!w!){|io|' . \'io << request_xml % Base64.encode64(open(%!' . a:filename .'!).read)' . \'}' . \'"') if v:shell_error echo "Base64 エンコードに失敗しました" return endif let password = inputsecret('Password: ') let header_wsse = 'X-WSSE: ' . s:WSSECreds(g:hatena_user, password) let endpoint = 'http://f.hatena.ne.jp/atom/post' let header_ac = 'Accept: application/x.atom+xml, application/xml, text/xml, */*' let header_cont = 'Content-Type: application/x.atom+xml; charset="utf-8"' let request = s:curl_cmd . ' -f --header "' . header_cont . \'" --header "' . header_ac . '" --header "' . header_wsse . \'" --data "@' . tmpfile . \'" "' . endpoint . '"' echo "画像のアップロード中です..." " 画像の投稿を実行 let ret = system(request) if v:shell_error echo "画像の投稿に失敗しました" return endif let imgsyntax = matchstr(ret, '<hatena:syntax>\zs[^>]*\ze</hatena:syntax>') " 投稿した画像の記法を本文に追加 let ret = append( line('.'), imgsyntax) if strlen(imgsyntax) != 0 echo imgsyntax . " の投稿に成功しました" else echo "画像の投稿に失敗しました" endif return endfunction
実行方法は次のとおり。
:FotolifePost <filename>
写真に位置情報を記録する
半年程前にハンディ GPS の Garmin eTrex Vista HCx (英語版) を購入しました。元々は山登りの為に買ったんですが、最近は写真に位置情報を追加するために使うことが多いです。今回は、Linux で写真に位置情報を記録する方法について説明します。
自分が買った eTrex シリーズは、最近の Linux カーネルにはドライバが収録されていて、USB ケーブルで繋ぐと USB/シリアル変換デバイスとして認識されます。
以下は、GPS を繋いだときの /var/log/messages です。GPS が /dev/ttyUSB0 として認識されたことが分かります。
May 31 18:46:17 galeon kernel: usb 1-3: new full speed USB device using ohci_hcd and address 4 May 31 18:46:17 galeon kernel: usb 1-3: configuration #1 chosen from 1 choice May 31 18:46:18 galeon kernel: usbcore: registered new interface driver usbserial May 31 18:46:18 galeon kernel: drivers/usb/serial/usb-serial.c: USB Serial support registered for generic May 31 18:46:18 galeon kernel: usbcore: registered new interface driver usbserial_generic May 31 18:46:18 galeon kernel: drivers/usb/serial/usb-serial.c: USB Serial Driver core May 31 18:46:18 galeon kernel: drivers/usb/serial/usb-serial.c: USB Serial support registered for Garmin GPS usb/tty May 31 18:46:18 galeon kernel: garmin_gps 1-3:1.0: Garmin GPS usb/tty converter detected May 31 18:46:18 galeon kernel: usb 1-3: Garmin GPS usb/tty converter now attached to ttyUSB0 May 31 18:46:18 galeon kernel: usbcore: registered new interface driver garmin_gps May 31 18:46:18 galeon kernel: drivers/usb/serial/garmin_gps.c: garmin gps driver v0.31
この /dev/ttyUSB0 は、Debian では dialout グループのデバイスとして認識されます。
$ ls -la /dev/ttyUSB0 crw-rw---- 1 root dialout 188, 0 2008-05-31 19:16 /dev/ttyUSB0
そのため、一般ユーザで GPS を使用するには、使用するユーザを dialout グループに加えておきます。
# usermod -a -G dialout h2onda
GPS から移動経路の情報(トラック)を抽出するには、GPSBabel を使用します。
GPSBabel 自体は Windows や Mac にも対応した OSS で、GPS データ形式の変換や GPS からの情報の読み書きを行うソフトです。
トラックの抽出は、以下のコマンドで実行します。
$ gpsbabel -t -i garmin -f /dev/ttyUSB0 -o gpx -F track.gpx
オプション | 説明 |
---|---|
-t | トラックの抽出を行う。他にはルート(-r)、ウェイポイント(-w)などが指定可能。 |
-i | 入力ファイルタイプを指定。今回は Garmin GPS を入力として使用するので garmin を指定。 |
-f | 入力ファイル名。今回は /dev/ttyUSB0 を指定。 |
-o | 出力ファイルタイプを指定。今回は gpx を指定。 |
-F | 出力ファイル名。 |
これで track.gpx として、トラック情報を取り出せます。GPSBabel は Google Earth で使われている KML 形式など、色々などファイルフォーマットを使用できますが、今回、位置情報を写真に記録する際に使用する gpscorrelate が gpx のみに対応しているので、gpx 形式を使用します。
gpscorrelate は、位置情報記録のための Linux 用プログラムです。debian testing だと、aptitude コマンドでインストールできますが、他のディストリビューションだとソースコードからインストールする必要があるでしょう。
gpscorrelate コマンドを実行する際は、先ほど取り出したトラックファイルや、UTC からの時差*1などのオプションを指定して実行します。
$ gpscorrelate -g track.gpx -M -z +9 *.jpg
オプション | 説明 |
---|---|
-g | 使用するトラックファイル(GPX形式)を指定する。 |
-M | 画像の変更日時(mtime)を修正しない。 |
-z | GPSデータの時刻がUTCで記録されている場合に時間を修正する。日本では+9。 |
-t | 位置情報が記録できなかった期間についても補完を行う。 |
-r | 位置情報を削除する。 |
gpscorrelate は gpscorrelate-gui という GUI プログラムも同梱されているので、そちらを使用することも可能です。
さて、これで位置情報が記録されるわけですが、GPS が衛星を捕捉できなかった期間に撮られた写真については、位置情報は記録されません。もし、捕捉できていない期間の写真にも位置情報を追加する場合は、-t オプションを追加します。
$ gpscorrelate -g track.gpx -t -M -z +9 *.jpg
これでGPSが位置を記録していない期間についても位置情報を追加してくれますが、単純に線形補完で位置を決定しているため、かなり誤差の大きい位置情報が記録されることがあるので注意しましょう。
誤差が大きい場合等、写真についた位置情報を削除したい場合は -r オプションで gpscorrelate を使用します。
$ gpscorrelate -M -r *.jpg
こうして位置情報を追加した写真は、Panoramioなどの位置情報に関連したサービスを使う際に便利です。
Linux ソフト RAID1 のハードディスクを取り替える
これまで自宅で使ってた Linux マシンは、ソフト RAID 機能を使って 500GB の HDD 2本で RAID1 アレイを構築していましたが、最近空き容量が少なくなってきたので 1TBの HDD で構築しなおすことにしました。
注意事項
以下のメモは、Debian testing (2008/05/25現在。カーネルは 2.6.24-1-amd64) での作業履歴です。
他のディストリビューションではそのまま使えない可能性があります。参考にする場合は自己責任でお願いします。
前提とする環境
これまでの RAID 構成は以下のとおり。
# cat /proc/mdstat Personalities : [raid1] md0 : active raid1 sda1[0] sdb1[1] 16008640 blocks [2/2] [UU] md1 : active raid1 sda3[0] sdb3[1] 466366848 blocks [2/2] [UU]
パーティション構成は、16GB 領域(/dev/sd[ab]1)をシステム用(/)に確保して /dev/md0 に、スワップパーティション(/dev/sd[ab]2)を適当に確保、残り(/dev/sd[ab]3)をデータ用(/data)に /dev/md1 としています。
また、今回の作業方法としては、ハードディスクを一つずつ取り替える方法を使用してます。
準備
とりあえず、万一に備えて knoppix とかLinuxディストリビューションのインストールCDなど、レスキューCDとして使用できるものを用意しておきます。
また、/proc/mdstat を見て、RAID1 アレイの sync が完了しているかどうかを確認しておきます。
sync が完了しているかどうかは、mdadm コマンドでも確認することが出来ます。recovery や resync が実行されている状態では次のコマンドは復帰せず、ブロックされます。
# mdadm --wait /dev/md0
1. /dev/sdb をアレイから取り除く
2番目のハードディスク /dev/sdb に関連するパーティションをアレイから除去します。まず mdadm コマンドの --fail オプションで sync されないようにして、remove オプションでアレイから取り除きます。
# mdadm /dev/md0 --manage --fail /dev/sdb1 # mdadm /dev/md0 --manage --remove /dev/sdb1
残りのアレイ(/dev/md1)に関しても同様に作業を行って、/dev/sdb に属するパーティションは全てアレイから取り除きます。
# mdadm /dev/md1 --manage --fail /dev/sdb3 # mdadm /dev/md1 --manage --remove /dev/sdb3
2. シャットダウンとハードディスク取り替える
一旦電源を落として、/dev/sdb の HDD を新しいものに取り替えます。
3. 新しい /dev/sdb のパーティションを設定する
マシンの電源を入れて、新しい HDD が認識されていることを確認したら、/dev/sdb のパーティション設定を行います。
/dev/sda のパーティション情報を参考にしつつ、RAID1 用パーティションが古いものよりサイズが大きくなるように、fdisk コマンドで新しい /dev/sdb のパーティション設定を行います。今回は、システム用の /dev/sdb1 とスワップ用の /dev/sdb2 は前回と同じ、残りは全てデータ用の /dev/sdb3 とします。
# fdisk /dev/sdb
なお、ディスクのサイズやジオメトリが全く変わらないものに取り替える場合は、sfdisk コマンドを使って、/dev/sda のパーティション情報をそのまま /dev/sdb に適用させることが出来ます。今回は /dev/sdb1 と /dev/sdb2 は以前と同じ容量を使用するので、sfdisk でパーティションを設定してから、fdisk で修正しました。
# sfdisk -d /dev/sda | sfdisk /dev/sdb
ただ、sfdisk コマンドはパーティション情報を書き換える前に確認がないので、入力間違いが致命的な問題を引き起こす可能性があります。安全を重視するなら、一旦パーティション情報を USB メモリなどにバックアップしておくなり、fdisk コマンドを使うなりしたほうが良いでしょう。
パーティションの編集が終わったら、partprobe コマンド*1を実行して、カーネルに対して /dev/sdb のパーティションを再認識させておきます。
# partprobe /dev/sdb
4. 新しい /dev/sdbをアレイに追加する
# mdadm /dev/md0 --manage --add /dev/sdb1
追加と同時にアレイの recovery が開始されます。recovery 中は常に I/O が行われているため、システムはかなり重くなります。気長に待ちましょう。
ちなみに --wait オプションを利用すると、recovery が終了したことを通知させることが可能です。
# mdadm --wait /dev/md0 && mplayer /usr/share/sounds/KDE_Notify.wav
他のアレイについても、同様に追加します。
# mdadm /dev/md1 --manage --add /dev/sdb3 # mdadm --wait /dev/md1 && mplayer /usr/share/sounds/KDE_Notify.wav
5. /dev/sdb に grub をインストールする
/dev/sdb が新しいディスクに取り替えられたので、次は /dev/sda を取り替えることになりますが、このときに問題になるのが、ブートローダをどうするか。
普通の Linux システムだと、/dev/sda のブートセクタに grub がインストールされているので、/dev/sda を新しい HDD と取り替えると当然マシンが起動しなくなってしまいます。直接 grub を利用できる起動用ディスクがあればそれを使えば良いですが、今回は /dev/sdb に grub をインストールして、/dev/sdb から起動するようにしてみます。
しかし、/dev/sdb のブートセクタへの grub のインストールは、通常使われる grub-install コマンドを使ってもうまく行かない*2ので、grub コマンドを実行して、grub シェルに入ってから、grub 組み込みの install コマンドを使用します。
# grub grub> install /boot/grub/stage1 (hd1) /boot/grub/stage2
この方法でインストールすると、grub メニュー起動時のみ、/dev/sdb が (hd0) と認識されるようで、menu.lst を編集する必要もありませんでした。
6. /dev/sda を取り替える
/dev/sda の取り替えは /dev/sdb の時と流れそのものは変わりませんが、/dev/sdb となっていたところが /dev/sda となるので、入力ミスには気をつけましょう。
まず、failオプションとremove オプションでアレイから除去。
# mdadm /dev/md0 --manage --fail /dev/sda1 # mdadm /dev/md0 --manage --remove /dev/sda1 # mdadm /dev/md1 --manage --fail /dev/sda3 # mdadm /dev/md1 --manage --remove /dev/sda3
一旦システムを停止させて /dev/sda を取り替えて起動します。5.の設定がうまく行っていれば、問題なく起動するはず。うまく行かなかった場合は、準備したレスキューCDなどから立ち上げます。
新しい /dev/sda が認識されていることを確認したら、パーティション設定を行います。この時点で /dev/sda と /dev/sdb が両方新しい HDD に取り替えられ、同じディスクになっているので、sfdisk コマンドでそのままパーティションを設定できます。
# sfdisk -d /dev/sdb | sfdisk /dev/sda # partprobe /dev/sda
パーティションをアレイに追加して、sync が完了するのを待ちましょう。
# mdadm /dev/md0 --manage --add /dev/sda1 # mdadm --wait /dev/md0 && mplayer /usr/share/sounds/KDE_Notify.wav # mdadm /dev/md1 --manage --add /dev/sda3 # mdadm --wait /dev/md1 && mplayer /usr/share/sounds/KDE_Notify.wav
新しい /dev/sda のブートセクタは当然ながら空なので、grub をインストールするのを忘れずに。/dev/sda に対しては、grub-install コマンドで大丈夫です。
# grub-install /dev/sda
7. アレイとファイルシステムを拡大する
ここまでの作業でディスクの入れ替えは完了しました。ただし、パーティションサイズを増やした /dev/md1 は、アレイとファイルシステムは以前と同じ大きさのまま(今回の場合、400GB程度)となっているので、これを拡大させます。
RAID アレイの拡大は、--grow オプションを使用します。
# mdadm --grow /dev/md1 --size=max
ext3 ファイルシステムの拡大は、resize2fs コマンドで行います。
こちらはそれなりに時間がかかります。
# resize2fs -p /dev/md1 resize2fs 1.40.8 (13-Mar-2008) Filesystem at /dev/md1 is mounted on /mnt/md1; on-line resizing required old desc_blocks = 28, new_desc_blocks = 57 Performing an on-line resize of /dev/md1 to 238685712 (4k) blocks. The filesystem on /dev/md1 is now 238685712 blocks long.
なおファイルシステムの拡大を行うと、RAID 1 アレイの resync が自動的に実行されます。これの完了をもって、作業は終了となります。
# cat /proc/mdstat Personalities : [raid1] md0 : active raid1 sda1[0] sdb1[1] 16008640 blocks [2/2] [UU] md1 : active raid1 sda3[0] sdb3[1] 954742848 blocks [2/2] [UU]
補足
作業中のミスによってデータが消えた場合は、RAID1だと取り外したディスクからデータをサルベージすることができます。以下のようにマウントして、データを拾いましょう。
# mount -t ext3 -o ro /dev/sdc3 /mnt/sdc3
/etc/shadow から Basic認証パスワードファイルを作成する
apache の Basic 認証に使用するパスワードファイルは、通常 htpasswd コマンドで作成しますが、Linux などで使用されている /etc/shadow ファイルから作成することも可能です。
例えば、h2onda ユーザのパスワードを Basic 認証で使用するには、以下のコマンドでユーザ名とハッシュ化されたパスワードを抽出し、パスワードファイルに追加します。
# cut -d : -f 1,2 /etc/shadow |grep ^h2onda >> htpasswd_file
この方法が使用出来るのは、Basic 認証の際に読み込んだハッシュ化パスワード文字列が、apache 独自の MD5*1 あるいは SHA*2 ハッシュ化パスワードではないと判別されると、Unix のログインパスワードの暗号化に使用されている crypt() 関数で処理されるためです。
しかし、この方法は Basic 認証とシステムのログインに使用しているパスワードを簡単に統一することが出来て便利ではありますが、そもそも /etc/passwd と /etc/shadow がなぜ分離されるようになったのかということを考えると、Basic 認証パスワードファイルに /etc/shadow と同じ中身が書かれているというのは、セキュリティの面で問題のある方法だということは意識しておくべきでしょう*3。
OpenSSH を rssh で sftp 専用にする
ネットワーク上でファイルの転送を行う用途には、昔から ftp が使用されてきました。しかし ftp にはパスワードやデータのやりとりが暗号化されていないというセキュリティ上の問題があるため、最近では anonymous ftp 以外の用途では使用されなくなってきていて、その代わりとして SSH を使用する scp や sftp が使用されることがあります。
ただ、SSH は元々 Telnet の代わりにリモートシェルを扱うためのプロトコルとして作られたため、ファイル転送機能だけが必要な場合は、本来のリモートシェルの機能がセキュリティの面で問題となります*1。
そこで、シェルを使用させずに scp や sftp のみ許可するために作られたのが rssh (日本語ページ)です。
rssh は制限付きシェルとして実装されており、OpenSSH を直接変更する必要がありません。また chroot も可能なので、安全にファイル転送サービスを提供することができます*2。
以下は、Debian lenny 環境において sftp,scp サービスを chroot で提供する場合の設定例です。
許可するファイル転送サービスの設定
設定ファイル /etc/rssh.conf でコメントアウトされている allow* のうち、許可するサービスを有効にします。
alloscp allosftp #allowcvs #allowdist #allowsync
umask の設定
umask はデフォルトで 022 が設定されています。必要があれば設定ファイルを変更します。
umask 022
chroot の設定
chroot を行うパスを設定ファイルに記入します。
chrootpath = "/srv/rssh_root"
chroot 環境で最小限必要なバイナリやライブラリ等のコピーは、rssh に添付されている mkchroot.sh が行ってくれます。
# mkdir /srv/rssh_root # /usr/share/doc/rssh/examples/mkchroot.sh /srv/rssh_root
chroot 環境のログが取得できるように、syslogd に追加のソケットを指定します。
# syslogd -a /srv/rssh_root/dev/log
ユーザ毎の設定
ユーザ毎に、許可するサービス・umask・chroot パスを設定することも可能です。以下は、h2onda ユーザに scp,sftp を許可し、/srv/rssh_root に chroot する設定です。詳細は man rssh.conf を参照。
user = "h2onda:022:00011:/srv/rssh_root"
ユーザの作成
ユーザを作成します。ホームディレクトリが chroot パスの配下に、シェルを rssh に設定します。以下は Debian でのユーザ追加の例なので、Red Hat 等では適宜変更してください。
# adduser --home /srv/rssh_root/home/h2onda --shell /usr/bin/rssh h2onda
これで、h2onda ユーザは chroot で隔離され、scp と sftp のみが実行可能になります。
ただ、自分が試した Debian lenny の rssh パッケージは、ログイン時に chroot を実行する rssh_chroot_helper コマンドに suid ビットが立っていなかったため、ログイン直後にセッションが切断される問題が発生しました。他の環境で発生するかどうかは分かりませんが、ご注意を。
# chmod u+s /usr/lib/rssh/rssh_chroot_helper
また、本家のページにも書かれていますが、Windows でよく使用される WinSCP は、scp 実行時に内部で別のコマンドも実行しているらしく、rssh を使用した環境では使用できないようです。sftp モードで使用するか、Filezilla を使いましょう。
*1:商用 SSH には、scp や sftp のみを使用するための機能があるようですが。
*2:そういえば、OpenSSH Chroot Patchなんてものもありましたね…。
Vimからはてなハイクに投稿する
vimスクリプトに興味があったので、試しにhatena.vimをはてなハイク対応に改造してみました。
以下のコードを plugin/hatena.vim に追加すると、はてなハイクに投稿可能になります。
" はてなにログインし、ハイクを編集する " Usage: " :HaikuEdit [<keyword>] command! -nargs=? HaikuEdit call <SID>HaikuEdit('<args>') " :HaikuEdit で開いたバッファの内容をはてなに送信し、ハイクを投稿する " Usage: " :HaikuPost "command! -nargs=? HaikuPost call <SID>HaikuPost(<args>) function! s:HaikuEdit(...) " 編集する " ログイン if !exists('b:hatena_login_info') let hatena_login_info = s:HatenaLogin() if !len(hatena_login_info) return endif else let hatena_login_info = b:hatena_login_info endif let [base_url, user, cookie_file] = hatena_login_info let base_url = "http://h.hatena.ne.jp" " キーワードを取得 if a:0 > 0 let word = a:1 else let word = input('Keyword: ') endif if !strlen(word) echoerr 'キーワードを入力してください' return endif " セッション(編集バッファ)を作成 let tmpfile = tempname() execute g:hatena_edit_command tmpfile setlocal noswapfile let &fileencoding = 'utf-8' let content = system(s:curl_cmd . ' "' . base_url . '" -b "' . cookie_file . '"') let b:rkm = matchstr(content, 'name="rkm"\s*value="\zs[^"]*\ze"') if !strlen(b:rkm) echoerr 'ログインできませんでした' if exists('s:user') unlet s:user endif return endif let b:word = word autocmd BufWritePost <buffer> call s:HaikuPost() | autocmd! BufWritePost <buffer> autocmd WinLeave <buffer> let &titlestring = b:prev_titlestring autocmd WinEnter <buffer> let &titlestring = 'Haiku!: ' . b:word . ' [' . b:hatena_login_info[1] . ']' endfunction function! s:HaikuPost(...) " 投稿する if !exists('b:word') || !exists('b:rkm') echoerr ':HaikuEdit してから :HaikuPost して下さい' return endif " ログイン if !exists('b:hatena_login_info') let hatena_login_info = s:HatenaLogin() if !len(hatena_login_info) return endif else let hatena_login_info = b:hatena_login_info endif let [base_url, user, cookie_file] = hatena_login_info let base_url = "http://h.hatena.ne.jp" if &modified write endif let body_file = expand('%') let post_data = ' -F rkm=' . b:rkm . ' -F "word=' . b:word \ . '" -F "location=/' . user . '/" -F "body=<' \ . body_file . '"' " ポスト let result = system(s:curl_cmd . ' ' . base_url . '/entry -b "' . cookie_file . '"' . post_data . ' -D -') if v:shell_error echo '投稿に失敗しました' else echo '投稿しました' endif endfunction
まあ、ほぼ日記投稿部分からのコピぺだったりしますが。
それでも、バッファ変数(b:*)の有効範囲を理解してなかったので、b:wordを定義する位置がおかしくてしばらくハマりました。
あと、
:HaikuEdit キーワード
という使い方が出来るようにしようと思ったんですが、キーワードが日本語だとエラーになるので、引数を無効にしてプロンプトからの入力のみにしてます。一応ダブルクォーテーションで囲っておくとうまく行くのは分かってますが、いちいちそうするのも面倒なので。
追記
Vimからはてなフォトライフに画像を投稿するvimスクリプトを書いているときに、HaikuEditの引数がうまく動作しなかった原因に気付いたので、本文のスクリプトを修正しておきました。command登録時に引数