Author Archives: misohena

2016-04-21

Windows(MSYS2 MinGW64)でEmacs25+IMEパッチをビルド

(2019-12-08追記: 新しいものを書きました Windows用のEmacs26.3をMSYS2でビルドする方法(IMEパッチ、ImageMagick-6))

新しいPCにしてからEmacsの適当なWindows用バイナリを試してみたのですが、gnutlsが使えなかったりIMEまわりが不安定だったりどれが最新だか分からなかったり色々したので諦めて自分でビルドすることにしました。SSDになったことですし!

ビルドの方法については nt/INSTALLnt/INSTALL.W64 が一次情報となり、詳しいと思います。私はW64の方を参考にしました。

日本語では次のページが参考になりました。

特にIMEパッチを提供されている rzl24ozi’s gists のREADME.txtが最も参考になりました。

以下、私がビルドした時の手順です。

ソースコードは emacs.git - Emacs source repository (GitHubならemacs/nt at master - emacs-mirror/emacs)、リリース版なら http://ftp.gnu.org/gnu/emacs/ からダウンロード可能です。 (Gitで落とすときは改行コードを変換しないように注意(autocrlf=false))

Gitでは全て落とすとリポジトリがかなりの容量になるそうなので、 --depth 1 を指定してみました。それでも時間はちょっとかかりますね。

git clone --depth 1 git://git.savannah.gnu.org/emacs.git --config core.autocrlf=false

emacs-25ブランチなら次のようにします(masterは少し不具合があるのでこちらの方が良いかも)。

git clone --depth=1 -b emacs-25 git://git.savannah.gnu.org/emacs.git --config core.autocrlf=false

ビルドの前にMSYS2のインストールとパッケージの設定が必要です。 MSYS2 に書かれていることに加えて nt/INSTALL.W64 に書かれているパッケージをインストールしました。

  1. MSYS2ダウンロード: http://sourceforge.net/projects/msys2/files/latest/download?source=files
  2. MSYS2インストール: msys2-x86_64-20160205.exeを実行
  3. MSYS2のbashから pacman -Sy
  4. パッケージのインストール

       pacman -S base-devel \
       mingw-w64-x86_64-toolchain \
       mingw-w64-x86_64-xpm-nox \
       mingw-w64-x86_64-libtiff \
       mingw-w64-x86_64-giflib \
       mingw-w64-x86_64-libpng \
       mingw-w64-x86_64-libjpeg-turbo \
       mingw-w64-x86_64-librsvg \
       mingw-w64-x86_64-libxml2 \
       mingw-w64-x86_64-gnutls   
    

    (1回でうまく入らないことあり)

私の場合、ここでautocrlf=trueにしていたことに気がついたので、その解消をしました。 cloneしたディレクトリで git config core.autocrlf false して、.git以外全て消して、 git reset --hard しました。

そしてビルド。

  1. (重要) mingw64_shell.bat (スタートメニューからMinGW-w64 Win64 Shell)を実行してMinGW64環境へ切り替える(これをしないとmake段階でエラーになります)
  2. 後はだいたい以下のような感じ

    ./autogen.sh
    ./autogen.sh git #こうしろと言われたので
    ./autogen.sh  #こうしろと言われたので
    
    PKG_CONFIG_PATH=/mingw64/lib/pkgconfig ./configure --prefix=/c/<checkout-dir> --without-imagemagick
    
    make -j4 #4コアで
    
    make install prefix=/c/<install-dir>
    
    <install-dir>/bin/runemacs #起動
    

    最適化するなら chuntaro/NTEmacs64: Windows 版 Emacs (通称 NTEmacs) の 64bit 版 にあるようにCFLAGSを指定して以下のようにすれば良い。

       PKG_CONFIG_PATH=/mingw64/lib/pkgconfig CFLAGS='-Ofast -march=x86-64 -mtune=corei7' ./configure --prefix=/c/<checkout-dir>/ --without-imagemagick
    
  3. (おっと、パッチを当ててなかった)rzl24ozi’s gists から emacs-25.0.92-x64.diff をダウンロード
  4. ソースコードのあるディレクトリトップで patch -b -p0 < emacs-25.0.92-x64.diff (patchはCygwinでテキストマウントを使っていると色々ハマるので注意)
  5. いくつかのHunkが失敗したり曖昧だったりするので一つ一つ確認して修正

    現時点でリジェクトされたのは次の2箇所のみでした。前後の内容が変わってしまって失敗しただけなので、手作業で修正すれば難しくありません。 (ちなみにemacs-25ブランチなら特にリジェクト無くパッチが当たる模様。masterは不具合もあるのでそっちの方が良いかも)

    --- ./src/Makefile.in.orig      2016-03-02 19:21:43.000000000 +0900
    +++ ./src/Makefile.in   2016-03-26 11:17:17.908757300 +0900
    @@ -396,7 +396,7 @@
            region-cache.o sound.o atimer.o \
            doprnt.o intervals.o textprop.o composite.o xml.o $(NOTIFY_OBJ) \
            $(XWIDGETS_OBJ) \
    -       profiler.o decompress.o \
    +       profiler.o decompress.o cmigemo.o \
            $(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
            $(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ)
     obj = $(base_obj) $(NS_OBJC_OBJ)
    
    --- ./src/w32fns.c.orig 2016-03-02 19:21:43.000000000 +0900
    +++ ./src/w32fns.c      2016-03-26 11:17:17.988761000 +0900
    @@ -52,6 +52,11 @@
     #include "w32.h"
     #endif
     
    +#ifdef USE_W32_IME
    +#include "fontset.h"
    +#include "w32font.h"
    +#endif
    +
     #include <commctrl.h>
     #include <commdlg.h>
     #include <shellapi.h>
    
  6. 再度makeしてmake installでok

    作成するexeのサイズを小さくするなら make install-strip とする。

       make install-strip prefix=/c/<install-dir>/
    

うん、ちゃんとIME使える!

2016-04-18

冬もすっかり終わって日中部屋にいると暑さを感じるくらいになりましたね。ついこの間年が明けたと思ったのに。

今年はもっと色々出来るかと思ったのですが、ちょっと色々あって思うように捗りません。

着実に一つ一つやっていくしか無いと思われ。

2016-04-18

2016春の新番組

さて、今期はどうでしょう。作品数も多いですし、良さそうなのも多いです。

04/01(金) 22:30~ □ TOKYO MX うしおととら 第3クール  
04/01(金) 23:00~ □ TOKYO MX 影鰐(KAGEWANI)-承- ※ウルトラスーパーアニメタイム
04/01(金) 23:00~ □ TOKYO MX 宇宙パトロールルル子 ※ウルトラスーパーアニメタイム
04/01(金) 24:30~ □ TOKYO MX ジョジョの奇妙な冒険 第4部 -ダイヤモンドは砕けない-  
04/01(金) 25:05~ □ TOKYO MX テラフォーマーズリベンジ  
04/01(金) ~ ニコニコCh 猫も、オンダケ  
04/02(土) 04:52~ □ フジテレビ ぼのぼの 新作  
04/02(土) 08:00~ □ テレビ東京 フューチャーカード バディファイトDDD (第3期)  
04/02(土) 09:30~ □ テレビ東京 レゴ ニンジャゴー 新シリーズ  
04/02(土) 12:00~ □ TBS アグレッシブ烈子 ※「王様のブランチ」内
04/02(土) 17:30~ □ 日本テレビ 逆転裁判  
04/02(土) 24:30~ □ TOKYO MX 学戦都市アスタリスク 2nd Season  
04/02(土) 24:56~ □ TOKYO MX ラグナストライクエンジェルズ ※CM枠の30秒アニメ
04/02(土) 25:00~ □ TOKYO MX ニンジャスレイヤー フロムアニメイシヨン TV版 スペシャルエディション  
× 04/02(土) 26:30~ □ 日本テレビ エンドライド~X fragments  
04/02(土) 深夜~ □ MBS 「かっちけねぇ!」「風の又三郎」(あにめたまご2016)  
04/03(日) 07:00~ □ テレビ朝日 機動戦士ガンダムUC RE:0096  
04/03(日) 08:30~ □ テレビ東京 デュエル・マスターズ VSRF  
× 04/03(日) 17:00~ □ TBS 僕のヒーローアカデミア  
△- 04/03(日) 22:30~ □ TOKYO MX マクロスΔ(デルタ)  
04/03(日) 23:00~ □ TOKYO MX コンクリート・レボルティオ~超人幻想~ THE LAST SONG (第2期)  
04/03(日) 24:30~ □ TOKYO MX くまみこ  
04/03(日) 25:35~ □ テレビ東京 Re:ゼロから始める異世界生活  
04/04(月) 06:45頃~ □ 日本テレビ 朝だよ!貝社員 ※「ZIP!」内
04/04(月) 17:55~ □ テレビ東京 ベイブレードバースト  
04/04(月) 18:00~ □ NHK Eテレ わしも-wasimo- 第4期?  
04/04(月) 18:10~ □ NHK Eテレ 忍たま乱太郎 第24シリーズ  
04/04(月) 19:00~ □ TOKYO MX 怪盗ジョーカー シーズン3  
04/04(月) 19:30~ □ TOKYO MX 12歳。~ちっちゃなムネのトキメキ~  
04/04(月) 23:00~ □ TOKYO MX 美少女戦士セーラームーンCrystal デス・バスターズ編 (第3期)  
04/04(月) 24:00~ □ TOKYO MX ばくおん!!  
04/04(月) 25:00~ □ TOKYO MX パンでPeace!  
△- 04/04(月) 25:35~ □ テレビ東京 聖戦ケルベロス 竜刻のファタリテ  
△- 04/04(月) 26:05~ □ テレビ東京 ハンドレッド  
04/05(火) 17:55~ □ テレビ東京 プリパラ 3rdシーズン  
× 04/05(火) 18:25~ □ テレビ東京 アルティメット・スパイダーマン VS シニスター・シックス  
04/05(火) 18:30~ □ BSプレミアム ぼくらベアベアーズ  
04/05(火) 18:45~ □ NHK Eテレ 少年アシベ GO!GO!ゴマちゃん  
△+ 04/05(火) 21:55~ □ TOKYO MX 薄桜鬼 ~御伽草子~  
04/05(火) 24:30~ □ TOKYO MX ジョーカーゲーム  
04/06(水) 17:55~ □ テレビ東京 バトルスピリッツ ダブルドライブ Official  
04/06(水) 18:00~ □ NHK Eテレ おじゃる丸 第19シリーズ  
04/06(水) 18:25~ □ テレビ東京 双星の陰陽師  
04/06(水) 18:45~ □ NHK Eテレ ねこねこ日本史  
04/06(水) 22:00~ □ TOKYO MX 美少女遊戯ユニットクレーンゲール  
× 04/06(水) 25:00~ □ TOKYO MX 鬼斬  
△- 04/06(水) 25:05~ □ TOKYO MX 文豪ストレイドッグス  
△+ 04/06(水) 25:35~ □ TOKYO MX SUPER LOVERS -スーパーラヴァーズ-  
04/07(木) 18:25~ □ テレビ東京 アイカツ スターズ!  
04/07(木) 21:00~ LINE LIVE 秘密結社 鷹の爪 GT  
04/07(木) 22:00~ □ TOKYO MX クロムクロ  
04/07(木) 22:30~ □ TOKYO MX あんハピ♪  
04/07(木) 23:30~ □ TOKYO MX ネトゲの嫁は女の子じゃないと思った?  
04/07(木) 24:25~ □ フジテレビ 甲鉄城のカバネリ  
△+ 04/07(木) 25:58~ □ TBS 少年メイド  
04/07(木) 26:28~ □ TBS 坂本ですが?  
04/08(金) 18:20~ □ NHK Eテレ あはれ!名作くん ※ビットワールド内
04/08(金) 26:10~ □ TBS 迷家-マヨイガ-  
04/09(土) 10:00~ □ CartoonNetwork パワーパフ ガールズ 新作  
04/09(土) 17:30~ □ NHK Eテレ 境界のRINNE 第2シリーズ  
04/09(土) 22:00~ □ TOKYO MX 田中くんはいつもけだるげ  
04/09(土) 23:30~ □ TOKYO MX キズナイーバー  
04/09(土) 24:00~ □ TOKYO MX はいふり  
04/09(土) 26:25~ □ 日本テレビ ふらいんぐうぃっち -flying witch-  
04/09(土) ~ □ TOKYO MX CoCO & NiCO  
04/10(日) 17:00~ □ NHK Eテレ なめこ~せかいのともだち~ ※「ニャンちゅうワールド放送局」内
04/10(日) 22:15~ □ TOKYO MX とんかつDJアゲ太郎  
04/10(日) 24:00~ □ TOKYO MX 三者三葉  
04/11(月) 25:05~ □ TOKYO MX うさかめ  
× 04/11(月) 25:10~ □ TOKYO MX ワガママハイスペック  
04/15(金) 25:40~ □ TOKYO MX ビッグオーダー  
04/15(金) 26:25~ □ TBS マギ シンドバッドの冒険  
04/16(土) 07:00~ □ TBS カミワザ・ワンダ  
04/17(日) 10:00~ □ テレビ東京 カードファイト!! ヴァンガードG -ストライドゲート編-(第7期)  
12歳。
このご時世にこんなコテコテの少女漫画をゴールデンでやるとは思いませんでしたよ。
Re:ゼロから始める異世界生活
これまでの所意外と面白い。
くまみこ
良い滑り出しですが今後次第でしょうか。
マクロスΔ
私はマクロスがあまり好きではないのかなぁと良く思います(←マクロスは2が良いと思っている人間)。
ネトゲの嫁は女の子じゃないと思った?
はがないみたいな雰囲気。
坂本ですか?
坂本カッコいい可笑しい。
夏目友人帳シリーズセレクション
第5期を前に復習ですね。夏目は良い話もそれほどでもない話も沢山あるので、つまみ食いでまた見られると良い感じ。
ニンジャスレイヤー
お話しの芯は意外としっかりしているかもしれない。
2016-04-07

自宅デスクトップPC部品注文

前回がこのあたりだから、ちょうど5年過ぎたところですね。なんかSATAまわりが調子悪いんですよ……。まぁ、そろそろWindows10にもしたかったので。

それにしてもPC高いですね。円相場の影響もあるのか、いや、CPU良いのを選んでいるからなのか(Core i7-6700)、さりげなくメモリ多め(DDR4 16GB)とかケースもそれなりとか(Fractal Design Define R5)、誘惑に負けてSSD買ってしまったり(1TB!)とか……。まぁ、でもBlu-rayドライブは使い回すし、GPUもとりあえず外付けしない方向で。M/BはASUS H170M-PLUS。

15インチのセカンドディスプレイ(RDT156M)も調子悪くて電源がなかなか入らないし、セカンドディスプレイがないと別なことをしながら録画を消化できないので番組改編期は辛いし、これも新調すべきなんだろうけど、今更15インチ買っても仕方ないしかといって23インチ並べるのも場所取るしどうしたら良いんだろう。

10万円手に入れるのにどれだけ苦労するのか考えると散在する気も失せるけど、仕事で使うものでもあるし仕方ないよね。

ソフトウェアのインストール・セットアップがまた面倒くさいし、データのコピーもしなければならず。MSDNはProにダウンしているのでOfficeどうしようとかも。なんだかいろいろ気が重い。

新しいPC買ったのにネガティブ全開なんて、なんだか歳なんですかね。

まぁ、でも、SSDになるので快適になると良いなぁ。ノートはSSDですけど、やっぱり自宅だとノートよりもデスクトップを使ってしまうので。

2016-03-01

Diredで一部の詳細(属性、ユーザ名、グループ名)を隠す(ファイルサイズと日時は残す)

Emacs24.4 から dired-hide-details-mode が使えるようになりましたが、ファイル名以外全部隠すんですね。極端だってば。いや、それが良いときもありますけど。

普段欲しい情報はファイルサイズと日付くらいでしょうか。属性(パーミッション等)、ユーザ名、グループ名は日頃の作業(一人用PCの非root作業)では意識する必要がないでしょう。しかし、これだけを隠すのは一筋縄ではいかないようです。

diredはいろんな動作がバッファ上のテキストに依存しています。例えばディレクトリかどうかはバッファにdという文字があるかどうかで判断していますし、マーク自体もバッファに*が付いているかで記録しているくらいです。なので、不用意に文字を消すと動作に支障を来してしまいます。

lsコマンドの引数を指定する dired-listing-switches という変数がありますが -l オプションを省くことは出来ません。gを指定すればグループ名くらいは消せます(全てのアクションが正しく動作するかは不明)。

それでは dired-hide-details-mode はどうやって情報を消しているかというと、文字列を存在させたまま非表示にすることで実現しています。実装を見ると dired-insert-set-properties関数 でバッファ上のテキストに invisibleプロパティ を設定しています。そしてそのdired-insert-set-properties関数は、diredバッファ上のテキストに次の三種類のシンボルをinvisibleプロパティとして設定します。

  • dired-hide-details-information (バッファ先頭の残り容量などの行)
  • dired-hide-details-detail (ファイルの詳細情報の部分)
  • dired-hide-details-link (シンボリック情報の部分)

それらのシンボルに対して dired-hide-details-update-invisibility-spec関数add-to-invisibility-specやremove-from-invisibility-spec を使うことでinvisible状態を変化させています。(GNU Emacs Lisp Reference Manual: Invisible Text)

さて、どうしましょうか。

理想的には dired-hide-details-detail を細分化すべきでしょう。

  • dired-hide-details-detail-perms
  • dired-hide-details-detail-links
  • dired-hide-details-detail-user
  • dired-hide-details-detail-group
  • dired-hide-details-detail-size
  • dired-hide-details-detail-time

といった具合に。

(setq dired-hide-details-parts '(perms links user group size time))

というリストを作っておけば、次のコードでこれらのシンボルが得られます。

(mapcar
 #'(lambda (part) (intern (concat "dired-hide-details-detail-" (symbol-name part))))
 dired-hide-details-parts)

表示する部分を指定するリストを次のように定義します。

(setq dired-hide-details-visible-parts '(size time)) ;;sizeとtimeのみ表示

実際にこれらを使ってinvisibleプロパティを操作するには次のようにします。

(mapc
 #'(lambda (part)
     (funcall
      (if (memq part dired-hide-details-visible-parts) 'remove-from-invisibility-spec 'add-to-invisibility-spec)
      (intern (concat "dired-details-s-" (symbol-name part)))))
  dired-hide-details-parts)

と、このような理屈で詳細をさらに詳細に非表示に出来るようにする dired-details-s.el を作成しました。

2016-03-01-dired-details-s-1.png

うまくパーミッション等だけを消せたのですが、うーん、どうにも見た目がダサいような。バッファの左端にいきなり右寄せの数字が現れるせいでしょうか。日時、サイズの順に並べればもう少し良くなると思います。順番を入れ替えるにはinvisibleプロパティだけでは実現できないので大変なのですが、それが出来たとしても根本的な解決にはならない無い気もします。ファイル名は左端、詳細情報はその右に表示するのが普通ではないでしょうか。エクスプローラにせよ、昔ながらのファイラーにせよ、少なくともデフォルトはそうなっているような気がします。

というわけで、頑張って詳細情報をファイル名の右に表示させてみたのが dired-details-r.el です。

2016-03-01-dired-details-r-1.png

うん、こんなもんでしょう。

もはやinvisibleプロパティだけでは対処できないので、ファイル名にオーバーレイを適用してafter-stringプロパティで文字列を表示しています。

その弊害がいくつか。

  • Font Lockで色づけできない(代わりの手段を提供)
  • カーソルの移動に不自然な所あり(左右移動でオーバーレイの右に出ると左に戻れないことがある。Dired側でもカーソルの動きを多少制限しているので、その兼ね合いか?)
  • オーバーレイの削除のタイミングが難しい(沢山のファイルがあると効率的に心配)
  • たまにレイアウトが崩れるかも?(元々?)

文字列は自分で組み立てているので自由な順番にカスタマイズできます。 わざわざ最大文字幅を計算してレイアウトし直しているので。 あらかじめ設定した組み合わせを'('キーでローテート出来ます。

あまりに長いファイル名が数個あるために全体が空白だらけになってしまうのを防ぐため、レイアウト上の最大幅を設定できるようにしました。レイアウトは崩れますが、見やすさを確保することが出来ます。

参考:

2016-01-27

今年の抱負

早いもので今年ももう1ヶ月経とうとしています。

去年は遊びすぎたので、今年はもう少し生産的なことに時間を使いたいなと思っております。いや、色々遊びたいネタも沢山あって、それはそれでやりたいのですが、もう少しメリハリをつけたいものです。選択と集中ってやつです。というわけで、お仕事お待ちしております(笑)

2016-01-26

トーストの進化

トーストを焼くようになってから、少しずつバリエーションが広がっています。

ピザ用チーズを常備しておけばトーストなら何にでも合います。これまでは自宅でほとんどパンを食べなかったのでチーズを買う気がしなかったのですが、食べるようになったら一袋買っても3~4週間くらいで使い切れました。

キューピーのあえるだけミートソースは一人分を手軽に使えるので常備しているのですが、それを使ってピザトーストにしてみたり。一人分100円弱くらいするので少し勿体ない気もしますが。

冷凍庫に冷凍ほうれん草が眠っていたので、それをチーズと一緒にしてみたり、卵と混ぜてみたり。

今日は卵とほうれん草に加えてこれまで使った具材を混ぜ合わせて焼いてみたら、ほとんどキッシュのようなトーストができあがりました。

  1. 皿の上でソーセージ1本を包丁で薄く輪切りにする
  2. 小さな耐熱容器に冷凍ほうれん草を入れて電子レンジで1分ほど加熱する
  3. 2に塩、胡椒、卵1個、マヨネーズ、ピザ用チーズ、1のソーセージを加えて混ぜ合わせる
  4. 皿の上にパンを乗せて、中心を広めに強く押して凹ませ、凹んでいないところにマーガリンを塗る
  5. パンの上に3を乗せて具材が偏らないように広げる
  6. しっかり焼く
  7. (゚д゚)ウマー

食べた瞬間、これキッシュじゃん! って思いました。ほうれん草のキッシュは大好きでパン屋で見かけるとつい買ってしまうのですが、何かもうこれでいいや~と思うほどの出来です。

検索すると同じようなものが結構ヒットしますね。なるほど牛乳を入れるのですか。でも牛乳を入れなくてもかなりキッシュっぽいですがチーズのおかげ?

それにしても、料理が苦手な私でも続いているのはひとえに手軽さ故ですね。

洗い物を極力出さないようにしています。なので使う食器は、小さな耐熱容器とパンが乗る皿、それと包丁、後は箸のみ。

調理方法も電子レンジとトースターのみ。

卵はトースターだけだとなかなか中まで固まらないので、先に電子レンジにかけて目玉焼きにしてから乗せるなどしています(穴開けとラップ推奨)。卵1個をパン2枚で使うなら、薄く伸ばせるのでトースターだけでも何とかなるかもしれません。

結局オーブンレンジのトースト機能を使用しています。温まりにくいためか少し時間はかかりますが、一応使えてます。

食材も今のところ日持ちがして一人ですぐに使い切れるもの、食べちゃわなきゃーとプレッシャーにならないものを使っています。いつ飽きるか分からないので。

食材は消費期限・賞味期限に十分注意して購入していますが、パンだけは仕方が無いですね。スーパーの中でできるだけ消費期限が先のものを買うようにしています。冷凍するのは面倒なので。冷凍するならレンジで半解凍して後はトースターで何とかする感じなのでしょうか。レンジしすぎてふにゃふにゃベトベトになったパンが大嫌いなんですよね。トースターと併用できるようになった今なら、正確な時間を掴めば何とかなるのかな。

2016-01-21

Emacsフォントサイズ調整

Emacs(MS-Windows)のフォントサイズを調整しようとしたらとんでもなく手こずった。

フォントまわりは以前からよく分かっていないところだったのだけど、色々いじった結果なんとかそれらしくなったけど、結局よく分からないまま。

最終的に以下のようにした。

(set-face-attribute 'default nil :family "Inconsolata" :height 110)
(set-fontset-font nil 'cp932 (font-spec :family "MS Gothic"))
(setq-default line-spacing 0.2)

文字集合とフォントのマッピングは次のようにした。

  • ASCII部分はプログラム用のフォントとして有名なInconsolataを使用
  • CP932(半角カナ、○×÷等の記号を含む)の範囲はMSゴシック
  • それ以外の所はどんなフォントになっても気にしない

メイリオは行間が空きすぎるのと、等幅ではないのでやめた(特に全角記号類が等幅ではないのが致命的)。等幅に改造したメイリオもあるみたいだけど、面倒なのでやめておいた。

set-fontset-fontで"fontset-standard"に対して設定を行っていく方法も試したのだけど、全角記号類のフォントがどうしてもうまく設定できなかったので諦めた。

set-face-attributeでMS Gothicを指定して、set-fontset-fontでasciiに対して"Inconsolata"を指定するのではうまくいかなかった。起動時にフォント高さが反映されなかったり、×や÷などの記号がInconsolataになってしまったりした。unicodeに対してMS Gothicを指定して、その後にasciiに対してInconsolataを指定してみたりもしたけれど、やはり問題があった。

2016-01-18

BSDiffバイナリ差分アルゴリズムの解説

BSDiff はバイナリ差分を扱うプログラムです。bsdiffとbspatchの二つのコマンドから成ります。bsdiffは旧ファイルと新ファイルを比較してパッチファイルを出力します。bspatchは旧ファイルにパッチファイルを適用して新ファイルを出力します。コマンドライン引数はbsdiff、bspatch共に旧ファイル、新ファイル、パッチファイルの三つをその順番で指定します。

  • Usage: bsdiff oldfile newfile patchfile
  • Usage: bspatch oldfile newfile patchfile

BSDiffは実行ファイルの修正差分を取ることを念頭に置いて設計されています。

一般にソースコードのある行に修正を加えた場合、実行ファイルの変化はその修正した行に直接対応する部分だけに留まりません。その行をコンパイルして出来たコード(マシン語列)の長さが変化するからです。その結果、その後に続くコードやデータの位置が前後にずれ、それを参照する命令のアドレス部が変化します。相対アドレスであっても修正した部分をまたぐ参照があった場合は変化します。また、変数や関数の使用状況の変化によって何らかの割り当て値がずれることも考えられます。そのような理由によって、ソースコードのごく一部に加えた修正は実行ファイル全体の広範囲にまばらに変化を引き起こします。

相対アドレスのずれはほとんどの場合一定の量になります。例えば「reljump, 6, dec, a, reljump, 2, inc, a, inc, b」という命令列があった場合、[inc, a]の前に[inc, c]を追加すると「reljump, 8, dec, a, reljump, 4, inc, c, inc, a, inc, b」のように実行ファイルは変化します。このとき、reljump命令のオペランドは6→8、2→4と同じ数(この場合2)だけ変化します。ジャンプ先の前に「inc, c」の2バイトが追加され、以降のコードが2バイト後ろにずれたからです。

このような性質に対応するため、BSDiffでは差分をADDとINSERTという二つの操作としてパッチファイルへ記録します。

ADDは旧ファイルの内容とパッチファイルの内容を足して新ファイルの内容とする操作です。例えば、旧ファイルに「1 5 3 8」という列があって、パッチファイルに「2 2 100 100」という列があった場合、「1+2 5+2 3+100 8+100」という具合に二つを加算して「3 7 103 108」が新ファイルに書き込まれることになります。単純に旧ファイルの一部を新ファイルへコピーしたい場合は「0 0 0 0」とすれば良いわけです。変化の無い部分は0の羅列がパッチファイルに記録されることになりますが、これはlibbz2で大変良く圧縮できます。また、上述した変化量が一定となる変化も、同じ数の羅列になるため良く圧縮できます。

INSERTはパッチファイルの内容をそのまま新ファイルへ挿入する操作です。これは純粋な新規のコード追加に対応します。

上のreljumpの例をパッチに直してみると、次のようになります。

  • ADD [0, 2, 0, 0, 0, 2]
  • INSERT [inc, c]
  • ADD [0, 0]

一般にはADDとINSERTは交互に繰り返されるため、実際には次のようなパッチになります。

  • ctrlブロック(ADD長, INSERT長, 旧ファイル内での参照位置の移動量)
    • 6, 2, 0
    • 2, 0, 0
  • diffブロック
    • 0, 2, 0, 0, 0, 2,
    • 0, 0
  • extraブロック
    • inc, c

パッチを適用するときはctrlブロックを先頭から読んでいき、次のように新ファイルを生成します。

  1. ADD操作:diffブロックから6バイト読み取って旧ファイルの先頭6バイトと加算して新ファイルへ出力(旧ファイル内での参照位置を+6進める)
  2. INSERT操作:extraブロックから2バイト読み取ってそのまま新ファイルへ出力
  3. 旧ファイル内での参照位置を+0修正(この例では移動の必要が無い)
  4. ADD操作:diffブロックから2バイト読み取って旧ファイルの6バイト目から2バイトと加算して新ファイルへ出力(旧ファイル内での参照位置を+2進める)
  5. INSERT操作:長さ0なので何もしない
  6. 旧ファイル内での参照位置を+0修正(この例では移動の必要が無い)
  7. 終了

パッチファイルフォーマット

bsdiffが出力するパッチファイルの形式は次のようになっています。

  • ファイルヘッダー
    • char[ 8 ] "BSDIFF40"
    • int64 ctrlブロックの圧縮後サイズ
    • int64 diffブロックの圧縮後サイズ
    • int64 新しいファイルのサイズ(パッチを当てた後のサイズ)
  • 圧縮されたctrlブロック (int64,int64,int64)*(ctrlブロックの展開後サイズ/(8*3))
  • 圧縮されたdiffブロック
  • 圧縮されたextraブロック

圧縮にはbzip2(libbz2)を使います。全体を一括では無く、ファイルヘッダーを除く三つのブロックをそれぞれ個別に圧縮します。

ctrlブロックは次の三つの値が一つのエントリーになっています。

  • ADD操作の長さ
  • INSERT操作の長さ
  • 旧ファイル内での現在位置の移動量

パッチの実行はctrlブロックからエントリーを読み出していって、一つのエントリー毎に次の処理を行います。

  1. ADD操作 : diffブロックから「ADD操作の長さ」分だけバイト列を取り出し、それを旧ファイル内での現在位置から続くバイト列とバイト毎に足し合わせて新ファイルとして出力
  2. INSERT操作 : extraブロックから「INSERT操作の長さ」分だけバイト列を取り出し、それをそのまま新ファイルとして出力
  3. 旧ファイル内での現在位置を「旧ファイル内での現在位置の移動量」だけ加算

これを実行するのがbspatchです。

bspatch.c

以下のコードは http://opensource.apple.com/source/X11server/X11server-106.3/Sparkle/sparkle.git/bspatch.c のbspatch関数から引用したものです。本家よりファイル読み込み部分が抽象化されていて分かりやすいので。日本語のコメントは私が書き加えました。

// oldposやnewpos、ctrl[n]など、ほとんどの変数はoff_t型(64-bit符号付き整数)
// ssize_t newsize = 新ファイルのサイズ
// u_char *new = 新ファイルの書き込み先バッファ先頭
// ssize_t oldsize = 旧ファイルのサイズ
// u_char *old = 旧ファイル全体のバイト列
oldpos=0;newpos=0;
while(newpos<newsize) {
    /* Read control data */
    for(i=0;i<=2;i++) {
        lenread = io->read(cstream, buf, 8);
        if (lenread < 8)
            errx(1, "Corrupt patch\n");
        ctrl[i]=offtin(buf); // offtinはchar[8]からoff_tへ変換する関数
    };

    /* Sanity-check */
    if(newpos+ctrl[0]>newsize) //ctrl[0]が負の時に漏れてしまうのでは?
        errx(1,"Corrupt patch\n");

    /* Read diff string */
    lenread = io->read(dstream, new + newpos, ctrl[0]);
    if (lenread < ctrl[0])
        errx(1, "Corrupt patch\n");

    /* Add old data to diff string */
    for(i=0;i<ctrl[0];i++)
        if((oldpos+i>=0) && (oldpos+i<oldsize))
            new[newpos+i]+=old[oldpos+i];

    /* Adjust pointers */
    newpos+=ctrl[0];
    oldpos+=ctrl[0];

    /* Sanity-check */
    if(newpos+ctrl[1]>newsize) //ctrl[1]が負の時に漏れてしまうのでは?
        errx(1,"Corrupt patch\n");

    /* Read extra string */
    lenread = io->read(estream, new + newpos, ctrl[1]);
    if (lenread < ctrl[1])
        errx(1, "Corrupt patch\n");

    /* Adjust pointers */
    newpos+=ctrl[1];
    oldpos+=ctrl[2];
};

ループの中はおおむね次のようになっています。

  1. コントロールデータ読み込み ctrl [ 0 ] ~ ctrl [ 2 ]
  2. ADD操作
    1. diffsize = ctrl[ 0 ]
    2. diffblockからdiffsizeだけ読み込む(new+newposへ)。
    3. バイト毎の加算 new[newpos..<newpos+diffsize] += old[oldpos..<oldpos+diffsize] (oldposの範囲が有効な場合に限り)
    4. newpos += diffsize
    5. oldpos += diffsize
  3. INSERT操作
    1. extrasize = ctrl[ 1 ]
    2. extrablockからextrasizeだけ読み込む
    3. newpos += extrasize
  4. oldpos += ctrl [ 2 ]

bsdiff.c

それで肝心のパッチファイルを作る、つまり、バイナリ差分を作成するアルゴリズムはどのようになっているのでしょうか。

大きく分けて、二つの重要な技術的なポイントがあります。

  • 新ファイルのある部分にマッチする旧ファイルの部分を高速に探す方法
  • パッチファイルが小さくなるようなマッチング範囲の決定方法

一つ目は一種の全文検索の問題です。つまり、新ファイルのある場所から始まる文字列をキーに旧ファイル内を検索するわけです。それには接尾辞配列という索引を使い、その作成に接尾辞ソートを用います。接尾辞配列については Wikipedia に解説があります。ソートにはLarsson Sadakane法が使われているらしいです。

二つ目。新旧の対応関係が探せればあとは「旧データのどこから何バイトコピー、新データから何バイトコピー」といったデータを羅列していけば良いだけに思うかもしれませんが、話はそう簡単ではありません。私たちが欲しいのは最小のパッチファイルです。ある新旧のファイルがあったとき、旧から新を生成するパッチは一通りとは限りません。上述したように実行ファイルの変化は全体にまばらに広がっているので、下手にやると一致箇所と不一致箇所が細かく繰り返され、大変非効率なパッチになりかねません。上述したコントロール(ADD/INSERT操作)の回数が少ないほど余分なオーバーヘッドが小さくなりパッチファイルのサイズは小さくなるわけですが、そのような組み合わせをどのように求めたら良いでしょうか。全ての可能性を試すには膨大な時間が必要で現実的ではなさそうです。

BSDiffでは新旧が一致している部分(変更が無い部分)は旧から新へのベタCOPYではなくADD操作によって表現します。従って、1回のADD操作の中で多少の不一致部分が含まれていてもそれほど問題になりません。むしろ、一回のADDにまとめてしまった方が効率的な場合もあります。つまり、厳密な一致箇所・不一致箇所を求めるのでは無く、ある程度おおまかなマッチングを行います。これをBDiffでは近似一致(approximate matches)と呼んでいます。 つまり、新ファイルの先頭から末尾までスキャンしていきながら、旧ファイルとの間で近似的な一致範囲、ならびに同じく近似的な不一致範囲を求めていきます。そして近似一致をADD操作として、近似不一致をINSERT操作として出力していくことになります。

それでは bsdiff.c を見ていきましょう。(オンラインで本家のソースを見られるところがあまり無いので、Debianのbsdiffパッケージのソースへリンクしています)

main関数の流れは次のようになっています。

  1. 旧ファイルの読み込み(old, oldsize)
  2. 索引の作成(I) qsufsort(I,V,old,oldsize)呼び出し(Vはテンポラリ)
  3. 新ファイルの読み込み(new, newsize)
  4. ファイルヘッダーを仮出力(ブロックサイズはまだ分からないので0で仮出力)
  5. 差分を求める(db,eb,dblen,eblen)と同時にctrlブロックを書き出す
  6. 5で求めたdiffブロック(db)を書き出す
  7. 5で求めたextraブロック(eb)を書き出す
  8. ファイルヘッダーの書き直し(diffブロックとextraブロックの圧縮後サイズを書き直す)
  9. 終了

複雑なのは5の差分を求める部分です。

2と5を合わせてC++風にして余分なものを分離して分かりやすく変形してみました。

static void bsdiff(
    u_char * const olddata, const off_t oldsize,
    u_char * const newdata, const off_t newsize,
    std::function<void(off_t, off_t, off_t)> writeCtrl,
    std::vector<u_char> &db,
    std::vector<u_char> &eb)
{
    // 検索に必要な索引を作る
    std::unique_ptr<off_t[]> I(new off_t[oldsize+1]);
    {
        std::unique_ptr<off_t[]> V(new off_t[oldsize+1]);
        qsufsort(I.get(),V.get(),olddata,oldsize);
    }

    // 出力バッファを準備
    db.clear(); db.reserve(newsize+1);
    eb.clear(); eb.reserve(newsize+1);

    // 新ファイルを先頭から末尾へスキャンしていきながら、新ファイルの領域が旧ファイルのどの領域と対応づけられるか決めていく。
    // 領域の対応関係が確定したら、その領域のパッチをwriteCtrl,db,ebへ出力。
    off_t scan=0; //調べているnew上の位置
    off_t lastscan=0; //前回一致のnew上の先頭。出力し終わったnew上の末端
    off_t lastpos=0; //前回一致のold上の先頭
    off_t lastoffset=0; //new上の位置からold上の位置を求めるための差(lastpos-lastscan)
    while(lastscan < newsize){
        // 切れ目を見つける。
        off_t len=0;
        off_t pos;
        for(off_t scsc=scan, oldscore=0; scan<newsize;) {
            // old 内から new[scan...newsize) との最長一致を探す。
            // posはその位置、lenはその長さ。
            // old[pos...pos+len) == new[scan...scan+len) となる。
            len=search(I.get(),olddata,oldsize,newdata+scan,newsize-scan,
                    0,oldsize,&pos);

            // oldscoreを差分的に更新する。
            // oldscoreは今回一致範囲[scan...scan+len)と前回一致の続きとの一致バイト数。
            // scscをscan+lenまで進めることによる増加分を数える。
            for(;scsc<scan+len;scsc++)
                if((scsc+lastoffset<oldsize) &&
                   (olddata[scsc+lastoffset] == newdata[scsc]))
                    oldscore++;

            // 前回一致の前方延長が新旧完全に一致しているので、その前方延長を継続。
            if(len == oldscore && len != 0){
                scan += len;
                scsc = scan;
                oldscore = 0;
                continue;
            }

            // 前回一致の前方延長に9バイト以上の不一致があり(len-oldscore > 8)、
            // 他の場所に9バイト以上の完全一致が見つかった(len > 8)
            // ので、参照する旧領域を切り替える。
            if(len>oldscore+8) break;

            // oldscoreを差分的に更新する。
            // scanを1進めることによる減少分。
            if((scan+lastoffset<oldsize) &&
                (olddata[scan+lastoffset] == newdata[scan]))
                oldscore--;
            ++scan;
        }

        // 近似的な一致・不一致の境界線を定める。

        // 前回一致の先頭(lastpos)から前方へ、あまり変化していない長さを求める。
        // 一致を50%以上含んでいて、一致がある程度多くなっていればどんどん延ばす。
        off_t lenf=0;
        for(off_t i=0,s=0,Sf=0;(lastscan+i<scan)&&(lastpos+i<oldsize);) {
            if(olddata[lastpos+i]==newdata[lastscan+i]) s++;
            i++;
            if(s*2-i>Sf*2-lenf) { Sf=s; lenf=i; };
        };

        // 今回一致の先頭(pos)から後方へ、あまり変化していない長さを求める。
        // 一致を50%以上含んでいて、一致がある程度多くなっていればどんどん延ばす。
        off_t lenb=0;
        if(scan<newsize) {
            for(off_t i=1,s=0,Sb=0;(scan>=lastscan+i)&&(pos>=i);i++) {
                if(olddata[pos-i]==newdata[scan-i]) s++;
                if(s*2-i>Sb*2-lenb) { Sb=s; lenb=i; };
            };
        };

        // 前回一致の前方延長と今回一致の後方延長が重なっているなら、
        // 前回一致の前方延長を優先しつつ、境目を確定させる。
        if(lastscan+lenf>scan-lenb) {
            off_t overlap=(lastscan+lenf)-(scan-lenb);
            off_t Ss=0,lens=0;
            for(off_t i=0,s=0;i<overlap;i++) {
                if(newdata[lastscan+lenf-overlap+i]==
                   olddata[lastpos+lenf-overlap+i]) s++;
                if(newdata[scan-lenb+i]==
                   olddata[pos-lenb+i]) s--;
                if(s>Ss) { Ss=s; lens=i+1; };
            };

            lenf+=lens-overlap;
            lenb-=lens;
        };

        // 差分を出力する。(deltaブロック、extraブロック、ctrlブロックへ)

        // ADD操作: new[lastscan...lastscan+lenf) - old[lastpos...lastpos+lenf) をdiffブロックへ出力
        for(off_t i=0;i<lenf;i++)
            db.push_back(newdata[lastscan+i]-olddata[lastpos+i]);
        // INSERT操作: new[lastscan+lenf...scan-lenb) をextraブロックへ出力
        for(off_t i=0;i<(scan-lenb)-(lastscan+lenf);i++)
            eb.push_back(newdata[lastscan+lenf+i]);
        writeCtrl(
            lenf, //ADD長
            (scan-lenb)-(lastscan+lenf), //INSERT長
            (pos-lenb)-(lastpos+lenf)); //旧ファイルの現在位置を pos-lenb へ移動

        lastscan=scan-lenb; //ここまで新ファイルを出力
        lastpos=pos-lenb; //lastscanに対応するold上の位置
        lastoffset=pos-scan; //新ファイル上の位置から旧ファイル上のマッチする位置へ変換するための差(scan+i + lastoffset は pos+iに対応する)

        scan += len;
    }
}

案外複雑ですね。ループを何回か追ってみないと何をしているのかよく分からないかもしれません。

新ファイルの内容が全て出力されるようなコントロール(ADD/INSERT操作列)を出力し終わったら一番外側のループは終了です。

一番外側のループ内では次のことをします。

  1. 大まかに一致・不一致範囲(領域の境目)を特定します。

    まずnew[scan]から続くバイト列と一致するoldの位置と長さを求めます。

    そしてoldの前回一致した領域の末尾(無ければ先頭)からその長さ分だけnewのscan位置と比較し、9バイト以上不一致がある場合、そこを大まかな境界線と考えて2へ。

    9バイト以上不一致が無い、つまり、oldの前回一致した領域から続く列とnewのスキャン位置がそれほど違っていない場合、まだ前回からの一致が続いているものと考えて、scanを1進めて探索を繰り返します。

  2. 一致・不一致範囲を確定させます。

    50%以上の一致率を目安に、 前回 一致した領域を前方に延ばした場合の長さを求めます(lenf)。

    同じく50%以上の一致率を目安に、 今回 一致した領域を後方(backward:先頭へ戻る方向)に延ばした場合の長さを求めます(lenb)。

    前回 一致した領域からlenfだけ前方へ延ばしたところが 今回 出力する近似一致領域の末尾です。

    今回 一致した領域からlenbだけ後方へ延ばしたところが 次回 出力する近似一致領域の先頭です。

    その間の一致率の低い領域が新領域だけに存在すると考える、今回出力する近似不一致領域です。

  3. コントロールを出力します。

    一致領域をADD操作としてdiffブロックへ、不一致領域をINSERT操作としてextraブロックへ、その長さと次の一致箇所への相対位置をctrlブロックへ出力します

かいつまんで言うと、新旧で完全に一致する領域を見つけていき、その前後にあるあまり変化していない(まばらに違いがある)領域をその一致領域に含めていく感じでしょうか。そして、その一致領域の間にある領域を不一致領域とします。

各変数の位置関係はおおむね次図のようになります。

2016-01-18-bsdiff-scan.png

bspatchの脆弱性

bspatch.c を読んでいて気がついたのですが、bspatch.cのSanity-checkと書いてある部分は読み込んだctrlの値(off_t 64-bit符号付き整数)が負の時にチェックをすり抜けてしまいます。

    /* Sanity-check */
    if(newpos+ctrl[0]>newsize)
        errx(1,"Corrupt patch\n");

その値(ctrl[ 0 ])は続く BZ2_bzRead にを読み込みバイト数(int)として引き渡しています。 BZ2_bzReadでは負のバイト数はエラーにしていますが、その前に呼び出し側でoff_tからintへの変換が生じます。 intが32-bitのときは上位の桁が失われますので、ctrl[ 0 ]が0x8000000100000001のような値の時は、BZ2_bzReadは1バイト読み込みで通ってしまうのではないでしょうか? その後newpos += ctrl [ 0 ]ですから、ポインターが64-bitのときはnew+newposはバッファの外を指してしまいそうです。 そしてその後、バッファーオーバーランを引き起こす気がします(LP64,LLP64時)。

AndroidやChromeなどいくつかのプロジェクトでは修正を行っているようですが、修正されていないものも多く見られます(上のリンク先のもそうですね)。

元々実行ファイルへのパッチを信頼できないところから取得するというのは考えづらいとは思いますが、ゲームのデータに対する野良パッチを拾って適用しようとしたらそのデータ以外もおかしな事になった、というような事は考えられるかもしれません。十分注意したいものです。

Chromeにおける改良

Chromeでは修正差分の配布にこのbsdiffを改良したものを使っているらしいです。

そういえば何年か前にニュースになっていたような気もします。

Courgetteでは実行ファイルを逆アセンブルして一段階ソースコードに近い状態に戻すことで、アドレスにまつわる問題を低減しているんだそうです。 master - chromium/src/courgette - Git at Google を見ても、CPUアーキテクチャに依存していそうなファイル名が並んでますね。

参考URL

2016-01-16

2016冬の新番組

ようやく寒くなって冬らしくなってきましたね。番組改編期です。おおむね第一話見終わりました。

01/05(火) 24:30~ TOKYO MX プリンス・オブ・ストライド オルタナティブ  
01/06(水) 24:00~ TOKYO MX 無彩限のファントム・ワールド  
01/06(水) 25:00~ TOKYO MX SUSHI POLICE(スシポリス)  
01/06(水) 25:35~ TOKYO MX ハルチカ~ハルタとチカは青春する~  
01/07(木) 22:00~ TOKYO MX アクティヴレイド 機動強襲室第八係 第一期  
01/07(木) 22:30~ TOKYO MX 少女たちは荒野を目指す  
× 01/07(木) 23:30~ TOKYO MX NORN9 ノルン+ノネット  
01/07(木) 25:46~ TBS ファンタシースターオンライン2 ジ アニメーション  
01/07(木) 25:50~ フジテレビ 僕だけがいない街  
01/07(木) 26:16~ TBS だがしかし  
01/07(木) 26:20~ フジテレビ 暗殺教室 第2期  
01/08(金) 21:00~ 日本テレビ系 ルパン三世 -イタリアン・ゲーム- ※テレビスペシャル
01/08(金) 22:30~ TOKYO MX ディバインゲート  
× 01/08(金) 23:00~ TOKYO MX おしえて!ギャル子ちゃん ※ウルトラスーパーアニメタイム
01/08(金) 23:00~ TOKYO MX 石膏ボーイズ ※ウルトラスーパーアニメタイム
01/08(金) 23:00~ TOKYO MX 旅街レイトショー ※ウルトラスーパーアニメタイム
× 01/08(金) 25:05~ TOKYO MX GATE(ゲート) -自衛隊 彼の地にて、斯く戦えり- 第2クール  
01/08(金) 25:40~ TOKYO MX 紅殻のパンドラ  
○+ 01/08(金) 26:35~ TBS 昭和元禄落語心中  
01/–(土) 10:30~ テレビ東京系 FAIRY TAIL ZERO  
01/09(土) 22:00~ TOKYO MX ブブキ・ブランキ  
01/09(土) 22:30~ TOKYO MX ラクエンロジック  
01/09(土) 23:30~ TOKYO MX デュラララ!!×2 結  
01/09(土) 25:30~ TOKYO MX 霊剣山 星屑たちの宴  
01/09(土) 26:25~ 日本テレビ ナースウィッチ小麦ちゃんR  
01/09(土) ??:??~ 物語シリーズ公式アプリ 暦物語 ※スマホアプリで配信
01/10(日) 22:00~ TOKYO MX 虹色デイズ  
01/10(日) 22:27~ TOKYO MX 大家さんは思春期!  
01/10(日) 22:30~ TOKYO MX Dimension W (ディメンション ダブリュー)  
01/10(日) 24:30~ TOKYO MX 灰と幻想のグリムガル  
× 01/10(日) 25:05~ テレビ東京 シュヴァルツェス マーケン  
01/10(日) 26:35~ テレビ東京 闇芝居 第3期  
01/11(月) 24:00~ TOKYO MX 赤髪の白雪姫 第2クール  
01/11(月) 24:30~ TOKYO MX 最弱無敗の神装機竜(バハムート)  
01/11(月) 25:05~ TOKYO MX てーきゅう 第7期  
01/11(月) 25:08~ TOKYO MX 血液型くん!4  
01/11(月) 25:11~ TOKYO MX 魔法少女なんてもういいですから。  
01/11(月) 26:05~ テレビ東京 蒼の彼方のフォーリズム  
01/12(火) 21:00~ ニコニコCh おじさんとマシュマロ  
01/13(水) 25:05~ TOKYO MX この素晴らしい世界に祝福を!  
01/15(金) 25:55~ TBS 亜人  

第一話の段階で最も目を引いたのは昭和元禄落語心中でしょうか。

続いて僕だけがいない街ハルチカ~ハルタとチカは青春する~あたりが気になりました。

例によってあくまで第一話の第一印象と言うことで。最初だけ良くて後はサッパリ、または、その逆ということも良くありますので。