2014-02-24

Java: Import a certificate with `keytool'

ここ最近、Java のセキュリティ名目での やんちゃ ぶりには、振り回されている人も多いのではないだろうか。私も、Java 7 Update 40 以降でアプレット署名に自己証明書が使えなくなり、その対応に煩わされた。

クライアント PC に証明書をインポートする作業は、keytool で自動化できる。自己証明書の時はそれで問題なかったのだが、正規の証明書に変えた途端、上手くいかなくなった。具体的には、証明書をインポートしてもアプレット実行時に出てくるダイアログを抑止できない。

証明書の作り方に問題があるのかも知れないが、生憎とそこは別の人の仕事。まずは渡された証明書で何とかする方法を探してみる。(恐らくは証明書の Common Name が怪しいと睨んでいるが、未検証)

私を混乱させたのは下記の挙動。

  1. Java コントロールパネルから証明書をインポートしても、アプレット実行時にダイアログが出てきてしまう。
  2. ダイアログで「次回から表示しない」を選択して実行すると、ダイアログは二度と出てこない。
  3. (1)と(2)についてそれぞれ証明書をエクスポートすると、両者は完全に一致する。

つまりエクスポートした証明書は全く同じなのに、ダイアログが出る or 出ない、という違いが出る。インポート方法を色々と試してみたが、結局(2)以外でダイアログを抑止することはできなかった。最後に、ダメ元で気になっていたことを試してみた。実は(1)と(2)では、1 つだけ違いがある。それがキーストア中の「別名」(alias)表示だ。これも keytool で見ることができる。

Java コントロールパネルから証明書をインポートした場合:

別名: deploymentusercertnullnullnulljava.util.random@1a36121

ダイアログで「次回から表示しない」を選択した場合:

別名: deploymentusercert$tsflag$loc=http//example.com:80##docbase:http//example.com:80java.util.random@10acb9b

後者に、怪しげな呪文が見て取れる。ダメ元で後者の「別名」でインポートしてみると・・・、

やったよ、ビンゴ! :-D

もうね、何でこんな仕様なのかと。確かに前述のダイアログには「上記の発行者と場所」とあったが、まさか「場所」をこんな方法で覚えるとか難度が高過ぎだろう。

色々と試した結果、「別名」は次の形式が必要なようだ。(Java 7 Update 51 にて確認)

  • $tsflag$loc=http//example.com:80##docbase:http//example.com:80
  • 「http//example.com:80」は適宜変更。ポートは省略不可。SSL なら「https//example.com:443」とする。
  • パースに問題なさそうな文字列であれば、先頭・末尾に追加可能。途中に入れることも可能だが、やらない方が無難だと思う。

以上を踏まえて、証明書インポート BAT の例。

importcert.bat:

@echo off
setlocal

set SITE_URL=http//example.com:80
set CERT_FILE=%~dp0mycert.cer
set KS_EMPTY_FILE=%~dp0trusted.certs.empty
set KS_ALIAS=deploymentusercert$tsflag$loc=%SITE_URL%##docbase:%SITE_URL%

set JAVA_VERSION=7
set JAVA_HOME=%ProgramFiles(x86)%\Java\jre%JAVA_VERSION%
if not exist "%JAVA_HOME%" (
  set JAVA_HOME=%ProgramFiles%\Java\jre%JAVA_VERSION%
)
set keytool=%JAVA_HOME%\bin\keytool.exe

set LocalLow=%USERPROFILE%\AppData\LocalLow
if not exist "%LocalLow%" (
  set LocalLow=%AppData%
)
set ks_dir=%LocalLow%\Sun\Java\Deployment\security
set ks_file=%ks_dir%\trusted.certs

set result=1

if not exist "%keytool%" (
  echo ERROR: not found: "%keytool%"
  goto exit
)
if not exist "%CERT_FILE%" (
  echo ERROR: not found: "%CERT_FILE%"
  goto exit
)
if not exist "%ks_file%" (
  mkdir "%ks_dir%" 2>NUL
  copy /v "%KS_EMPTY_FILE%" "%ks_file%"
  if not exist "%ks_file%" (
    echo ERROR: not found: "%ks_file%"
    goto exit
  )
)

"%keytool%" -list ^
            -alias "%KS_ALIAS%" ^
            -keystore "%ks_file%" ^
            -storepass "" >NUL
if not errorlevel 1 (
  echo already imported.
  goto success
)

"%keytool%" -importcert -v ^
            -alias "%KS_ALIAS%" ^
            -file "%CERT_FILE%" ^
            -keystore "%ks_file%" ^
            -storepass "" ^
            -noprompt
set result=%ERRORLEVEL%
if %result% neq 0 (
  echo ERROR: keytool.exe: code=%result%
  goto exit
)

:success
set result=0

:exit
if %result% equ 0 (
  echo OK
) else (
  echo Failed
)
pause
exit /b %result%

実行には上記 BAT に加え、下記ファイルが必要。

  • mycert.cer (インポートする証明書)
  • trusted.certs.empty (空のキーストアファイル)

詳細は BAT を解読して貰うとして、ファイル「trusted.certs.empty」については説明が必要だと思う。

keytool は、キーストアファイルが存在しなければ新たにファイルを作成する。しかしこの時、何故かパスワードを強要してくる(-storepass "" は効かない)ため、パスワードなしキーストアを作ることができない。一方 Java コントロールパネルから証明書をインポートすると、パスワードなしキーストアが作成される。つまり、Java コントロールパネルはパスワードなしキーストアを使うくせに、keytool からはそれを作ることができないのだ。もうほんと、この仕様を作った奴はタヒねと言いたい。

trusted.certs.empty は、予め作成した空のパスワードなしキーストアだ。Java コントロールパネルで適当な証明書をインポート → 削除すれば作成できる(ファイル場所は %ks_file% を参照)。私が確認した限り、Windows XP と Windows 8 とでファイルは完全に一致したので、今後の互換性も問題ないだろう。

下記は、インポート済み証明書を一覧表示する BAT の例。「別名」もこれで確認できる。

listcerts.bat:

@echo off
setlocal

set JAVA_VERSION=7
set JAVA_HOME=%ProgramFiles(x86)%\Java\jre%JAVA_VERSION%
if not exist "%JAVA_HOME%" (
  set JAVA_HOME=%ProgramFiles%\Java\jre%JAVA_VERSION%
)
set keytool=%JAVA_HOME%\bin\keytool.exe

set LocalLow=%USERPROFILE%\AppData\LocalLow
if not exist "%LocalLow%" (
  set LocalLow=%AppData%
)
set ks_dir=%LocalLow%\Sun\Java\Deployment\security
set ks_file=%ks_dir%\trusted.certs

set result=1

if not exist "%keytool%" (
  echo ERROR: not found: "%keytool%"
  goto exit
)
if not exist "%ks_file%" (
  echo ERROR: not found: "%ks_file%"
  goto exit
)

"%keytool%" -list -v ^
            -keystore "%ks_file%" ^
            -storepass ""
set result=%ERRORLEVEL%
if %result% neq 0 (
  echo ERROR: keytool.exe: code=%result%
)

:exit
pause
exit /b %result%

今回はこれで解決だが、問題は、今後の互換性は保証されない ということ。

事実、Java 7 Update 45 → 51 で「別名」の形式が変わった。具体的には、u51 で「##docbase:...」が増えた。このせいで、u45 で証明書をインポートしていても、u51 にアップデートすると再びダイアログが出てきてしまう。ここまで来ると、分かってて嫌がらせをしているとしか思えない。(u51 アップデート時に出てくるダイアログで「セキュリティ・プロンプトの復元」に初期でチェックが入っているのも、これを隠すための陰謀だと思っている)

そろそろ、世界中の IT エンジニアは Java の横暴に NO! って言っても良い頃だと思う。

参考:


2014-04-28 追記

Java 7 Update 55 にて、更にマジックワード「##from」が増えた模様。

  • $tsflag$loc=http//example.com:80##docbase:http//example.com:80##from:http//example.com:80

2014-02-01

Animes in the 4th quarter of 2013

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

前期からの視聴。光る素材は有ったのに、前期同様それを活かせずに終わってしまった。話が完全にキャラ負けした感じ。キャラデザは良かっただけに、話さえ良ければ化けたと思う。

前期からの視聴だが、相変わらずキャラは秀逸。ただ話は区切りの悪い終わり方と、(原作は知らないが)重要な章が端折られているように見えた。尤も前期の教訓で話の方は期待してはいなかったが、何故 12 話で作ったのか疑問。ただ、OP/ED は良い。

絵は綺麗だが、その他は平凡。エロゲ原作はこのパターンが多い気がする。まあエロゲだし、エロを取ったら(略)。結局、ジョルトのルールは最後まで良く分からなかった。

シリーズ通しての視聴。今回はごった煮らしいが、各話の繋がり(時系列)が良く分からなかった。個人的には臥煙伊豆湖がもっと出てきても良かった。あの手の頼れる存在は安心感を生む。金髪幼女は忍野メメの代わりにはならなかったと思う。あと、そろそろ超速字幕は何とかして欲しい。いちいち巻き戻して読むのが面倒。

リタイアせずに最後までは見られたが、何がやりたかったのか良く分からなかった。メカ? 家族愛? 時を越えた愛? いずれにせよ、特に感動もなく終わった。

  • 俺の脳内選択肢が、学園ラブコメを全力で邪魔している http://noucome.jp/

タイトルから食傷気味で、絵も全く好みでなく、脳内選択肢のバカ設定を知り、もう嫌な予感しかしなかったが意外に期待を裏切ってくれた。設定だけでなく主人公の周りも全てバカなのが良かった。これなら安心して続編を見られる(続くかは知らない)。

2013-12-30

Struts2: ScopeInterceptor: Deadlock in session lock

もう数年前の話になる。Web アプリケーションの負荷テストをすると稀にエラーになることがあった。リクエストから 30 秒後にエラーになり、ログには「Deadlock in session lock」と出る。この文字列から ScopeInterceptor に辿り着き、コードを読んで不具合と判断、特に使っていなかった ScopeInterceptor を外すことで対処した。

当時は同様の事例が見つからなかったが、最近になって検索すると次が出てきた。

コードを読んだだけで動作は未確認だが、最新版でも直っていないようだ。

確か ScopeInterceptor は初期設定で組み込まれているはずで、負荷テストをやれば普通に当たりそうに思うのだが、何故これまで問題になっていないのか謎。

2013-11-24

Animes in the 3rd quarter of 2013

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

キャラデザや OP からして某作の二番煎じっぽいが、初話でいい意味で裏切られた。ロボット物と三枚目キャラの組み合わせは新鮮。しかし結局、その後の話が初話を超えることは無かった。キャラの魅力は十分で、特に女子キャラ 2 名は良い味を出しているので、初話の感じで通していたら大化けしたと思う。

女子向けということで期待はしておらず、そしてほぼ予想通り。イケメンキャラが程よく絡むという女子向けの定番で、男子が見てもつまらないない作品。少女マンガ原作は当たりが多いが、この系統の女子向けは基本スルーで良いような気がしてきた。

前期に引き続き視聴。浜地だけが目当てだったのに、数える程しか出て来なかった。しかもチョイ役で。まあ女子向けだから需要が無のは分かってはいたが、浜地が出ないなら私にとって見る価値が無かった。

ヒロインが十数人の義理兄弟から惚れられる、という男女逆なら昔見たパターン。ヒロインが兄弟を拒まない所に違和感を感じまくるが、よく考えたらこれが男女逆だったらそれが普通だし、異性から見るとこんなにもバカバカしく見えるということが良く分かった作品だった。

原作は大よそ既読。アニメで見返しても、女神篇からの話は違和感がある。特定のヒロインの魅力は増したが、それ以外が総潰れに。流石に新キャラを増やすのが面倒になって整理をしたのかと勘ぐってしまう。OP は好きなのだが、それ故に是非ネイティブに歌って欲しかった。ただ早見沙織の頑張りは認める。

一見して「まどマギ」にインスパイアされた作品なんだろうな、と。タロットとか対消滅とかネタ的には良いと思うが、そこから先が膨らませきれずに終わった印象。物理をネタにしている割に、実は消えてませんでしたとか、まとめ方も理不尽。作り方次第で化けた可能性を感じたので、勿体ない気がする

それなりに期待していたが、このシリーズの特徴「主人公にイイ格好させる程につまらなくなる」の典型になった。格好つけは本編に任せて、黒子たちにバカをやらせてるだけで良かったのに、どうしてこうなった。ブレンダなどは残念キャラの良い資質を持っていたのに。無論、フェブリたんが可愛いかったのは認める。

問答無用の話題作。結果、時系列で評価が↑↓↑と変化。巨人が自然法則を無視しているのが分かった時点で一旦評価が下がったが、それを受け入れれば話として非常に面白い。やはり「先が読めない」というのは重要な要素だと再認識。原作を読まなずにいて良かった。だがそれ故、じっくり一年かけてやって欲しかったとも思う。

京アニなので視聴。絵は京アニらしさが感じられ、標準以上の出来になっているとは思う。のだが、それ以上の感想が出て来ない。もっと何か、京アニにしか出来ない何かを見てみたかった。まあ私の期待が大き過ぎるのは認める所なので、もっと長い目で見守ろうとは思う。

本が主題な割に、深い話は特に出ずに薄っぺらく終わった印象。もっとビブリオマニア的なディープな話を期待していたのだが、本関連の肩書きを持つだけの単なる変態たちだった。ただ何故か秋月マキシを気に入ったので、良しとする。何かシャイニングな感じと声色の組み合わせが何処かのツボに入った模様。

最大の問題は、声優がひどい。まるで素人が当てているようにしか聞こえない。だがそれさえ乗り越えれば、結構面白い。ネタが絶妙というか、誰もが学生時に経験したであろうネタが多く、ツッコミのノリも含めて慣れると癖になる。ただ、殆んどの人はそこまで到達できなかったと思われるのが残念。

今期の伏兵。正直、完全にナメてた。最早これは「ガッチャマン」という名の完全新作と思った方が良い。 世界観に感心したのは久しぶり。ヒロインの性格さえ受け入れられれば、問題なく楽しめると思う。うつつたんも可愛かったし、OP・ED も良い。最近の内田真礼はなかなか良い仕事をする。

初見で全く期待していなかったが、良い意味で裏切ってくれた。まさか恋愛ではなくコメディ作品だったとは。ボケ役とツッコミ役が上手くハマっていて、各キャラの声も違和感が無い。非常に良く纏まっている作品だと思う。今期はコメディ作品の当たりが多くて私得なクールだった。

シリーズの勉強と思っての視聴だったが、これ見てもたぶん本編のことは勉強できないよね? しかしこれはこれで需要が有りそうな感じはするので次回も見るとは思うが、正直私には面白さが分からない。これは本編をやり込んだ人向けのファンディスク的な作品のような気がする。つまり私は予選落ちしてるっぽい。

絵的に好みでないため期待はしていなかった。各キャラが弱めなので、もう少しギャグ方向に振るか萌え要素が欲しい。ヒロインは最後まで薄いし、唯一キャラの強い実質ヒロインに萌えるのは無理だったし。寧ろ小動物の演技の方が記憶に残った。やっぱり中の人(斉藤千和)の力は侮れない。

前期からの視聴。相変わらず暖かい作品だが、やや無理やりいい話にしている感もなくはない。新キャラはキャラが弱すぎて従来四人に全く馴染めてないが、のりえが強化されていて全体としてはプラマイゼロ。ていうか最早、私はのりえを見るためにこの作品を見ていると言っても過言ではない。

2013-10-31

StandardError hides KeyboardInterrupt in Python 2.4

私は、Python で次のようなコードを書くことがある。

import datetime

def validate_date_string(s):
    try:
        assert len(s) == 8
        datetime.date(int(s[:4]), int(s[4:6]), int(s[6:]))
        return True
    except StandardError:
        return False

def test():
    assert validate_date_string('20120229') # leap year
    assert not validate_date_string('20130229') # not leap year

StandardError を捕捉するのは、Google 先生の教えによる。

  • Never use catch-all except: statements, or catch Exception or StandardError, unless you are re-raising the exception or in the outermost block in your thread (and printing an error message). Python is very tolerant in this regard and except: will really catch everything including misspelled names, sys.exit() calls, Ctrl+C interrupts, unittest failures and all kinds of other exceptions that you simply don't want to catch.

しかしここには、Python のバージョンによって例外のクラス階層が異なる、という重要な情報が抜けている。

実際、Python 2.4 以前は KeyboardInterruput が StarndardError のサブクラスになっているため、上記のコードは try ブロック中で Ctrl-C を受けると意図通りに動かない。具体的には、Ctrl-C で終了できず、無条件に False が返ってしまう。

Exception
 +-- SystemExit
 +-- StopIteration
 +-- StandardError
 |    +-- KeyboardInterrupt
<...snip...>
BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- Exception
      +-- GeneratorExit
      +-- StopIteration
      +-- StandardError
<...snip...>

Python 2.4 以前でも動くようにするには、StandardError を処理する に、KeyboardInterruput を明示的に処理する必要がある。

def validate_date_string(s):
    try:
        assert len(s) == 8
        datetime.date(int(s[:4]), int(s[4:6]), int(s[6:]))
        return True
    except KeyboardInterrupt:
        raise
    except StandardError:
        return False

Python は保守的な言語だと思っていたので、まさかこんな所に罠があるとは思わなかった。これでまた 1 つ、Python が嫌いになった。:-p

2013-09-30

xyzzy: zgrep.l

エディターに Emacs を常用するようになって久しいが、その前は xyzzy を使っていた。更にその前は WZ Editor で、社会人プログラマーになって最初のエディターが WZ だった。WZ から xyzzy へ移行した動機は省くが、移行で最後までネックになったのが Grep。

使った人にしか分からないが、WZ Grep には該当行の前後を確認できるプレビュー画面があって、大量の Grep 結果を見る際に非常に便利だった。偶に秀丸などを使う機会があると、プレビューなしでどうやって仕事ができるのか不思議に思ったくらいだ。

結局、xyzzy を使いながら WZ Grep からも離れらない状況が続き、いい加減 WZ と決別するために拡張 Lisp を自作したのだった。当時、既に WZ Grep を模倣する拡張 Lisp は存在したが、使ってみて「これじゃない」感が強くて常用には至らなかった。

以上は 10 年も前のことなので今更ではあるが、その拡張 Lisp をここに公開しておく。ファイルの最終更新日は 2004-09-07。もうコードの内容も覚えてないし、読む気も更新するつもりもないが、WZ Grep から離れられない人の気持ちは良く分かるので。

今でも、Windows で大量のファイルを Grep する必要に迫られた時には、これを使うことがある。Emacs でも作れば良いのだが、今の仕事だと無くてもそれ程には困らないので、作るまでのモチベーションがない・・・。

$XYZZY/site-lisp/zgrep.l:

;;; -*- Mode: Lisp -*-
;;;
;;; This file is NOT part of xyzzy.
;;;
;;
;; 概要
;; ======
;;
;; VZ Grep / WZ Grep のように、Grep バッファにプレビューウィンドウを付ける
;; メジャーモード。
;;
;; 設定
;; ======
;;
;; .xyzzy 又は siteinit.l に下記を記述。
;;
;;   (require "zgrep")
;;   (zgrep-default-setting)
;;
;; `zgrep-default-setting' には、次の設定が含まれている。
;;
;;   ;; SPC でプレビューウィンドウをトグル
;;   (define-key *zgrep-mode-map* #\SPC 'zgrep-toggle-preview)
;;
;;   ;; RET で該当ファイルを開く
;;   (define-key *zgrep-mode-map* #\RET 'zgrep-open)
;;
;;   ;; カーソル移動で次々にプレビューする
;;   (define-key *zgrep-mode-map* #\Up 'zgrep-previous-line)
;;   (define-key *zgrep-mode-map* #\Down 'zgrep-next-line)
;;
;; その他の設定例。
;;
;;   ;; Grep 後、自動で zgrep-mode にする
;;   (defun my-grep-hook ()
;;     (delete-other-windows)
;;     (zgrep-mode (car *minibuffer-search-string-history*)))
;;   (add-hook 'ed::*grepd-hook* 'my-grep-hook)
;;   (add-hook 'ed::*grep-hook* 'my-grep-hook)
;;
;;   ;; Grep ウィンドウ(上段)のサイズ
;;   (setq *zgrep-window-size* 10)
;;
;;   ;; リードオンリーでファイルを開く
;;   (setq *zgrep-open-read-only* t)
;;
;;   ;; Grep 文字色
;;   (setq *zgrep-foreground-color* 1)
;;   (setq *zgrep-background-color* 14)
;;   (setq *zgrep-bold* t)
;;
;;   ;; 拡張子 .grep のファイルを zgrep-mode で開く
;;   (push '("\\.grep$" . zgrep-mode) *auto-mode-alist*)
;;
;; その他
;; ======
;;
;; * ファイル全体を読み込むため、巨大なファイルのプレビューには難有り。
;; * プレビューウィンドウ中での該当行の反転は、色設定によっては見難い。
;; * Grep を C-g で中断したとき zgrep-mode にする上手い方法。
;;

(provide "zgrep")

(defvar *zgrep-mode-hook* nil)

(defvar *zgrep-mode-map* nil)
(unless *zgrep-mode-map*
  (setq *zgrep-mode-map* (make-sparse-keymap)))

(defvar *zgrep-window-size* *error-window-size*)
(defvar *zgrep-open-read-only* nil)

(defvar-local *zgrep-grep-string* nil)
(defvar *zgrep-foreground-color* 1)
(defvar *zgrep-background-color* nil)
(defvar *zgrep-bold* nil)

(defvar-local *zgrep-mode* nil)
(defconstant zgrep-mode-name "zgrep:~A")
(defconstant zgrep-preview-buffer "*grep preview*")
(defvar *zgrep-preview-filename* nil)

(defun zgrep-set-string-color (str &optional from to)
  (if (and str
	   (/= (length str) 0)
	   (or *zgrep-foreground-color*
	       *zgrep-background-color*
	       *zgrep-bold*))
      (save-excursion
	(save-restriction
	  (narrow-to-region (or from (point-min)) (or to (point-max)))
	  (goto-char (point-min))
	  (while (scan-buffer (concat "\\(" str "\\)")
			      :regexp t :no-dup t :case-fold t)
	    (set-text-attribute (match-beginning 1) (match-end 1)
				'zgrep-string
				:foreground *zgrep-foreground-color*
				:background *zgrep-background-color*
				:bold *zgrep-bold*))))))

(defun zgrep-delete-preview-buffer ()
  (let ((buf (find-buffer zgrep-preview-buffer)))
    (if buf (delete-buffer buf)))
  (setq *zgrep-preview-filename* nil))

(defun zgrep-make-preview-buffer (filename)
  (let ((old-buf (selected-buffer))
	(preview-buf (find-buffer zgrep-preview-buffer))
	(grep *zgrep-grep-string*))
    (unless (and preview-buf
		 (string= filename *zgrep-preview-filename*))
      (zgrep-delete-preview-buffer)
      (setq preview-buf (get-buffer-create zgrep-preview-buffer))
      (set-buffer preview-buf)
      (insert-file-contents filename)
      (dolist (x *auto-mode-alist*)
	(when (string-matchp (car x) filename)
	  (funcall (cdr x))))
      (zgrep-set-string-color grep)
      (set-buffer-modified-p nil)
      (setq buffer-read-only t))
    (set-buffer old-buf)
    (setq *zgrep-preview-filename* filename)
    preview-buf))

;; returns (file line body)
(defun zgrep-parse-line ()
  (save-excursion
    (let ((str (buffer-substring (progn (goto-eol) (point))
				 (progn (goto-bol) (point)))))
      (if (string-match "^\\([^:]+\\):\\([0-9]+\\):\\(.*\\)$" str)
	  (list (substring str (match-beginning 1) (match-end 1))
		(parse-integer (substring str
					  (match-beginning 2)
					  (match-end 2)))
		(substring str (match-beginning 3) (match-end 3)))))))

;; Emacs like `get-buffer-window'
(defun zgrep-get-buffer-window (buffer-or-name)
  (ignore-errors
    (get-buffer-window buffer-or-name (selected-window))))

(defun zgrep-preview-p ()
  (zgrep-get-buffer-window zgrep-preview-buffer))

(defun zgrep-preview ()
  (interactive)
  (let ((parsed (zgrep-parse-line)))
    (when (and parsed
	       (file-exist-p (nth 0 parsed)))
      (let ((preview-buf (zgrep-make-preview-buffer (nth 0 parsed))))
	(delete-other-windows)
	(split-window *zgrep-window-size*)
	(other-window)
	(set-buffer preview-buf)
	(goto-line (nth 1 parsed))
	(reverse-region (progn (goto-eol) (point))
			(progn (goto-bol) (point)))
	(refresh-screen)
	(recenter)
	(other-window)
	(recenter)))))

(defun zgrep-toggle-preview ()
  (interactive)
  (if (zgrep-preview-p)
      (progn
	(delete-other-windows)
	;;(zgrep-delete-preview-buffer)
	)
    (zgrep-preview)))

(defun zgrep-find-file (filename)
  (let ((old-buffer (get-file-buffer filename)))
    (if old-buffer
	(progn
	  (ed::find-file-verify old-buffer filename nil nil nil nil)
	  (set-buffer old-buffer))
      (if *zgrep-open-read-only*
	  (find-file-read-only filename)
	(find-file filename)))))

(defun zgrep-open ()
  (interactive)
  (let ((parsed (zgrep-parse-line)))
    (when (and parsed
	       (file-exist-p (nth 0 parsed)))
      ;;(zgrep-delete-preview-buffer)
      (zgrep-find-file (nth 0 parsed))
      (delete-other-windows)
      (goto-line (nth 1 parsed))
      (refresh-screen)
      (recenter))))

(defun zgrep-set-grep-string (grep)
  (interactive "sGrep string (Regexp): ")
  (setq *zgrep-grep-string* grep)
  (zgrep-set-string-color grep))

(defun zgrep-next-line-internal (n)
  (ed::next-line-1 n #'forward-virtual-line #'goto-virtual-column))

(defun zgrep-next-line (&optional (n 1))
  (interactive)
  (let ((old-line (current-line-number)))
    (zgrep-next-line-internal n)
    (when (and (/= old-line (current-line-number))
	       (zgrep-preview-p))
      (zgrep-preview))))

(defun zgrep-previous-line (&optional (n 1))
  (interactive)
  (zgrep-next-line (- n)))

(defun zgrep-mode-toggle ()
  (interactive)
  (if *zgrep-mode*
      (progn
	(setq mode-name (format nil zgrep-mode-name "off"))
	(use-keymap (make-sparse-keymap))
	(local-set-key '(#\C-c #\C-c) 'zgrep-mode-toggle))
    (progn
      (setq mode-name (format nil zgrep-mode-name "on"))
      (use-keymap *zgrep-mode-map*)))
  (setq *zgrep-mode* (null *zgrep-mode*)))

(defun zgrep-mode (&optional grep)
  (interactive)
  (kill-all-local-variables)
  (setq buffer-mode 'zgrep-mode)
  (zgrep-mode-toggle)
  (setq *zgrep-grep-string* grep)
  (run-hooks '*zgrep-mode-hook*))

(defun zgrep-default-setting ()
  (define-key *zgrep-mode-map* #\SPC 'zgrep-toggle-preview)
  (define-key *zgrep-mode-map* #\RET 'zgrep-open)
  (define-key *zgrep-mode-map* #\Up 'zgrep-previous-line)
  (define-key *zgrep-mode-map* #\Down 'zgrep-next-line)
  (define-key *zgrep-mode-map* #\S 'zgrep-set-grep-string)
  (define-key *zgrep-mode-map* #\R 'rename-buffer)
  (define-key *zgrep-mode-map* '(#\C-c #\C-c) 'zgrep-mode-toggle))

2013-08-01

Animes in the 2nd quarter of 2013

アニメは IT エンジニアの必須科目です。 ということで簡単なレビューを。

前回に続き、今回も素晴らしい出来。新キャラ達は個性的なだけでなく、どれも魅力的。今回は「潘めぐみ」を覚えた。途中、ピンチを演出する意図的な展開はあるものの、作品の評価を下げるほどではない。話は今回で一区切りついているので、次回の展開が楽しみでもあり不安でもある。まあ期待し過ぎず、次回を待とうと思う。

名前も中の人も同じキャラが居るせいで、「ゆるゆり」の二番煎じに見えた。ただ、本作の方がマニアックな感じ。あの年代の、箸が転んでも可笑しいというか、周りには何が楽しいのかサッパリというか、そういう空気がある。これを楽しむには、自分の精神年齢を下げるスキルが必要かも知れない。まあ私には問題なかった。

今期のロボット物で本命になるはずだったのだろうが、他のロボット作品に埋もれてしまった。絵は綺麗でキャラデザも目力があって好きだが、話は強引でちぐはぐ。黒い方のヒロインが本性を出す辺りから面白くなり始めるが、それまでを挽回する程では無かった。第二期も期待はしていないが、黒い方目当てで見ると思う。

前作は確かに話題作ではあったが、個人的な評価はイマイチだった。なので今期も特に期待はしていなかったが、結果、寧ろ劣化した印象。だがしかし、クー子が攻略対象になったことだけは評価に値する。というか、製作側はいい加減にクー子以外に萌え要素が無いことに気付くべき。特に黄色、お前は要らない(断言)。

このシリーズは原作派だが、今回はアニメも面白かった。理由を考えるに、ヒロインの出番が少なかったせいかも知れない。実のところ、ヒロインは話のテンポを損なう重荷なのでは。あと最後のキス話は覚えてないと思ったら、やはりオリジナルだった。この作品に限らないが、オリジナル話がつまらないのは何故だろう。

イケメンキャラが一杯出てきて、声優も有名どころが揃っている。スペックは豪華だが、話的には至って普通で特に見るべきものは無かった。女子向けの作品らしいので、男子が見ればこんなものだろうか。ただ、悔しいがツクモたんの可愛さは認めざるを得ない。次回があるなら、彼女を目当てに絶対に見ると思う。

前作は好きだったが、続編が来るとは予想してなかった。ただ、妹が何を考えてるか分からない感じが良かったのに、両思いが判明してラブラブ、いつの間にかあやせルートどころかハーレムルートまで立っていたとか、こんなに視聴者に媚びた作品だっけ?と。まあ面白かったので問題ないが、距離感は前作の方が好きだった。

いきなり忍者とか陰陽師とか出てきて、さぞ安っぽい作品だろうと思ったら、意外にしっかりとした作りだった。もし中二時分に見ていたらヤバかったかも知れない、と思うくらいに、それっぽい空気を持っている。ただ、万人受けは難しいとは思う。個人的な話だが、間が悪くて数話ほど見逃してしまったのが残念。

原作は流し読みする程度。前期が未見なのでどんな感じか興味があったが、なかなか楽しめた。元がかなり下品な作品ではあるが、深夜アニメを良いことに殆どそのまま移植している気がする。その変態キャラを有名声優が演じるのが興味深い。ただ、何故 15 分作品なのかは謎。普通に 30 分で良かったように思う。

少年マガジンに連載してることすら知らなかった程、絵が全く好みでない。純粋にアザゼルさんの「ついで」に視聴。全く期待してなかったせいか、意外に楽しめた。何というか、ヒロインの勢いに押されてしまう。慣れてくるとウザさが勝ってくるが、ハーピーが可愛いので許す。このハーピー、原作より可愛いくね?

西尾維新という作家を見極めるため、興味深く視聴。結果、やはり只者では無いと結論。この人のキャラは「喋る」ではなく「語る」が相応しい。余程キャラを作り込んでいるのか、天然の才能か、各キャラの台詞回しに引き込まれてしまう。最初こそ絵が不安だったが、全くの杞憂だった。彼の作品に絵は重要では無いと悟った。

「世界を殺す少女を止めるため、デートして、デレさせろ!?」とか、日本人のこういうバカな所は世界に誇って良いと思う。と期待して見たのだが、中身は平凡だった。よくある既存パーツを組み合わせた感じ。既存でなかったのはキャッチコピーだけだった。とは言え、ヤンとツン担当の両キャラは結構好きだったりする。

それなりに楽しくは見られたのだが、何かもう一押し足りない。キャラがウリの作品なのは分かるが、そこからギャグとシナリオの両方に振ろうとして、どちらも中途半端に終わった感じ。キャラの魅力は十分だと思うので、素直にギャグ方面に振り切った方が良かったのでは。あと悔しいが、OP/ED は結構好き。

今期ロボット物の中では最も斬新。初話を見て、これはとんでもない作品かも知れない、と期待値は高かったものの、結局は既存の範疇に収束した感じ。こっち(地球)側ではなく、あっち側での話の展開を見てみたかった。とはいえ、本作はネタとして見ておくことを推奨。今後のロボット物の可能性を広げた作品だと思う。

女子に大人気、ということで視聴。初話の OP (通常話の ED)を見て、全てを理解した。普段アニメを見ている人なら、三次元の女子が二次元に勝てないのは常識だが、その逆も然り。女子の理想そのものがそこに描かれているので、ジャニーズなんて敵にならない。話は男子が見てもつまらないが、色々と勉強にはなった。

初見の感じで全く期待してなかったが、かなり楽しめた。主人公の自虐っぷりが半端なくて、同じボッチ属性の私のツボに嵌った。ヒロインとの掛け合いも良い。ただボッチ設定の割には、主人公にリア充要素が多過ぎた感はある。次回に続きそうで楽しみだが、原作の方がストック切れという噂。あと、OP は良かった。