似てる、似てない?(4)

fingerprint 指紋 これ、以前にsha256をやった時に出てきた。そんな繋がりが有ったか 無かったか知らないけれど 『指紋を発見した男』(主婦の友社)なんていう本を読んでみた。

その男とは、ヘンリー・フォールズその人である。イギリスから日本に派遣された宣教師兼医師。 東京築地にある聖路加国際病院を起した人でもある。病院の敷地内には、「指紋研究発祥之地」と 言う記念碑があるそうなので、東京見物の折にでも立ち寄ってみたいぞ。

宣教師である彼は布教の任が有り、明治時代の人々を一生懸命に改教しようと講演に務めるわけだが、 人気がなかなか出ない。ライバルが居たのである。そのライバルとは、大森貝塚を発見したモース。 彼の科学講演の方が、明治の頃の人には人気だったと言う訳。

ライバルであるこの二人も、仕事を離れれば佳き友達。貝塚の発掘にヘンリーが参加したりして いたそうな。そんなある日、発掘された土器に微小な模様があるのを発見する。手の模様(指紋) が付いたんだろうと言う仮定の上で、いろいろな瀬戸物を調べてみると、やはりいろいろな模様が 付いている。詳細に調べてみると、皆違う模様。一方、彼は、日本で行われていた、拇印にも 興味を持っていた。この二つの興味が、やがて彼を指紋研究へ誘う事となる。

何年にも渡る研究の末、2つと同じ指紋は無い事、指紋は再生する事から、同一人の判定に使える と確信した彼は、ネイチャーにその事を発表する。

しかし、彼には傲慢?なライバルが出現した。指紋をどうやって犯罪捜査に応用するかで スコットランド・ヤードとの駆け引きが始まる。120年ぐらい前の戦いが、犯罪捜査をからめて 書かれており、興味は尽きない。

いまだと、 指紋鑑定は言うに及ばず、DNA鑑定やら声紋鑑定やらいろいろ有るけど それぞれに、先人達のドラマがあるんだろうな。

あるある亡き後、皆様のNHKが提供する、試してガッテンがぶっちぎりの強さを見せている ように思うけど、どうだろう?

老人・女性が喜びそうな話題満載。ねちっこく話題を引っ張って話し3分で終わる所を40分に 引き伸ばす水増し番組。手作りの模型が毎回出てきて笑える。

そして、何と言っても権威付け。どこかの大学の先生が毎回登場。先生が言ってるんだから 信用してねってのは、この世界のカリスマであるみのさんに通じるものがあるなあ。

食べ物とかが取り上げられると、翌日はスーパーで売り切れているもんなあ。好いと言われる 食べ物を必ず食べていたら早死にするぞ。

ああ、年寄り増えすぎちゃったんで、みんな早く無くなってねっていう密かな陰謀が隠れて いるんだな。その方が世のため人のためになるか。まあ、どこかの先生が言ってるのw紹介 しただけだから、責任は取らないよってテロップも時々流れているな。

参照するぞ

前回に引き続いて、今回は参照系をつくります。名前は、acc.rbです。ちと長いけどドーンと 載せておきます。

#!/usr/local/bin/ruby
require 'pstore'
require '/home/sakae/z/c_keywords.rb'   # C keywords from cflow
DBFILE='/home/sakae/z/FreeBSD.dat'

def hdlopt()
  opt  = ''
  if ARGV[0] == nil
    puts "Usage: acc.rb [-asACGP] sorce ..."
    exit
  end
  opt = ARGV.shift if ARGV[0][0] == '-'
  list = ARGV
  return list,opt
end

def show(list, ef, fn, opt)
  noshow = true
  list.each {|sn| noshow = false if ef.key?(sn)}
  return if noshow
  return if opt =~ /A/ and ANSI_KEYWORDS.include?(fn)
  return if opt =~ /P/ and POSIX_KEYWORDS.include?(fn)
  return if opt =~ /C/ and C99_KEYWORDS.include?(fn)
  return if opt =~ /G/ and GCC_KEYWORDS.include?(fn)
  printf("%16s", fn)
  list.each do |sn|
    if ef.key?(sn)
      printf("\t%3d", ef[sn])
    else
      printf("\t%s", '---')
    end
  end
  puts ''
end

def ttl(list)
  printf("%16s", "Function")
  list.each {|sn| printf("\t%s", sn)}
  puts ''
end

def dump_all(pb)
  pb.transaction do
    db = pb["roots"]
    db.keys.sort.each do |k|
      fn,sn = k.split(':')
      printf("%16s %8s %3d\n", fn, sn, db[k])
    end
  end
end

def src_all(pb)
  src = {}
  pb.transaction do
    pb['roots'].keys.each do |k|
      fn,sn = k.split(':')
      src[sn] = true
    end
  end
  src.keys.each{|n| puts n}
end

def comp(pb, list, opt)
  pb.transaction do
    ofn = ""
    ef = {}
    ttl(list)
    db = pb["roots"]
    db.keys.sort.each do |k|
      fn,sn = k.split(':')
      if fn != ofn
        show(list, ef, ofn, opt) if ofn != ''
        ofn = fn
        ef.clear
      end
      ef[sn] = db[k]
    end
    show(list, ef, ofn, opt)
    ttl(list)
  end
end

### main HERE ###################
list,opt = hdlopt()

pb = PStore.new(DBFILE)

if opt == '-a'
  dump_all(pb)
elsif opt == '-s'
  src_all(pb)
else
  comp(pb,list,opt)
end

使い方

[sakae@secd ~/z]$ ./acc.rb
Usage: acc.rb [-asACGP] sorce ...
[sakae@secd ~/z]$
[sakae@secd ~/z]$ ./acc.rb -a
      ABORT_READ camcontrol   5
             ABS      lex   6
             ACC kbdcontrol   4
             :
           zread     gzip   1
     zuncompress     gzip   1
          zwrite compress   1

引数無しで、使い方が出てくるのは、お約束。 -a は、いわゆる全データのダンプ。左側から 関数名、ソース(と言うかコマンド名)、出現回数って言う並びだ。コマンド名は8文字で収まると 思っていたら、unixらしからぬ長い名前が有るのね。

ダンプなんてそんなに使い道無いじゃんと思うけど、grepとかとの合わせ技で真価が発揮されます。

[sakae@secd ~]$ ./acc.rb -a | grep arc4random
      arc4random dhclient   4
      arc4random       ed   1
      arc4random      jot   1
      arc4random  keyserv   2
      arc4random   netcat   1
      arc4random    newfs   1
      arc4random   newkey   1
      arc4random    ping6   1
  arc4random_buf    hastd   2
 arc4random_stir    newfs   1
arc4random_uniform   rtadvd   2
arc4random_uniform   rtsold   1

もう、こういうのを見せられると、あれもそれも摘まみ見したくなっちゃうぞ。

[sakae@secd ~/z]$ ./acc.rb -s
cat
chflags
chio
chmod
  :

登録されてるソース数を数えてみたら、535個になってた。

[sakae@secd ~/z]$ ./acc.rb rm rmdir mv
        Function        rm      rmdir   mv
           ISDOT          1     ---     ---
             :
            warn          4       2      26
           warnx          4     ---       8
           write        ---     ---       1
        Function        rm      rmdir   mv

rm,rmdir,mvが、どれぐらい似てるかと思ってみたりして。-ACGPの引数は、cflowから引き継いで います。

[sakae@secd ~/z]$ ./acc.rb  echo
        Function        echo
         errexit          2
            exit          1
            main          1
          malloc          1
          strcmp          1
        strerror          1
          strlen          4
           write          3
          writev          1
        Function        echo
[sakae@secd ~/z]$ ./acc.rb -ACGP echo
        Function        echo
         errexit          2
            main          1
          writev          1
        Function        echo

へぇー、echoでも、malloc なんてのを使ってるよ。使う必然性は有るの? ソース嫁。

パクリ

上記の、-ACGPは、規格に登録されてる関数類は表示しないって機能だ。これをを実現するには、 登録してある関数名を知ってる必要が有ります。

cflowでは、作者さんが一生懸命に調べて、それをC言語用のヘッダーファイルに書いて おいてくれてます。

例えば、ansi_keywords.h の中は下記のようになってました。

static const char* ansi_keywords[] =
{
    "assert", "acos", "asin", "atan", "atan2", "atof", "atoi", "atol",
         :
    "wctomb", "wcstombs",

    NULL
};

ruby用にパクらせて貰いました。

[sakae@secd ~]$ cd cflow-0.0.6/common/
[sakae@secd ~/cflow-0.0.6/common]$ ls
ansi_keywords.h         graph.c                 printgraph.c
c99_keywords.h          graph.h                 wincompat.h
gcc_keywords.h          posix_keywords.h
[sakae@secd ~/cflow-0.0.6/common]$ cat *keywords.h > c_keywords.rb

等としてc_keywords.rb を作り、それを編集すればいいでしょう。配列の最後に入っている NULLは、 C言語では番兵として使われているんで、Rubyでは不用です。それぞれの配列名は、 何処からでもアクセス出来るように、大文字にしています。 配布する場合は、*.h の冒頭にあるコメントに従ってください。

cgraph

折角なので、cflowがどんな技を使ってるか見てみる。自分で自分自身を表示させると言う常套手段だな。

[sakae@secd ~/cflow-0.0.6]$ cflow -ACGP cgraph/*c common/*c
 1 main: int(), <cgraph/cgraph.c 65>
 2                       usage: void(), <cgraph/cgraph.c 50>
 3             create_excludes: node_t*(), <common/graph.c 520>
 4                               add_excludes: node_t*(), <common/graph.c 493>
 5                                                add_node: node_t*(), <common/graph.c 194>
 6                                              free_nodes: void(), <common/graph.c 230>
 7            lex_create_graph: bool_t(), <cgraph/clexer.c 512>
 8                                    get_next_token: int(), <cgraph/clexer.c 374>
 9                                                     skip_whitespaces: int(), <cgraph/clexer.c 63>
10                                                         skip_strings: int(), <cgraph/clexer.c 145>
11                                                       get_next_token: int(), <cgraph/clexer.c 374>
12                                                            parse_cpp: int(), <cgraph/clexer.c 311>
13                                                                        skip_whitespaces: int(), <cgraph/clexer.c 63>
14                                                             get_name: char*(), <cgraph/clexer.c 196>
15                                                         is_c_keyword: bool_t(), <cgraph/clexer.c 264>
16                                                          is_reserved: int(), <cgraph/clexer.c 242>
17                                                          is_excluded: bool_t(), <cgraph/clexer.c 287>
18                               get_definition_node: g_node_t*(), <common/graph.c 164>
19                                        add_g_node: g_node_t*(), <common/graph.c 256>
20                                                     get_definition_node: g_node_t*(), <common/graph.c 164>
21                                                           create_g_node: g_node_t*(), <common/graph.c 89>
22                                   create_sub_node: g_subnode_t*(), <common/graph.c 145>
23                                 add_to_call_stack: bool_t(), <common/graph.c 325>
24                                                     get_definition_node: g_node_t*(), <common/graph.c 164>
25                                                         create_sub_node: g_subnode_t*(), <common/graph.c 145>
26                 print_graph: void(), <common/printgraph.c 243>
27                               print_preorder: void(), <common/printgraph.c 136>
28                                                 nodes_contain: bool_t(), <common/printgraph.c 77>
29                                                    print_node: void(), <common/printgraph.c 97>
30                                                print_preorder: void(), <common/printgraph.c 136>
31                                print_callers: void(), <common/printgraph.c 192>
32                                                nodes_contain: bool_t(), <common/printgraph.c 77>
33                                                   print_node: void(), <common/printgraph.c 97>
34        print_graphviz_graph: void(), <common/printgraph.c 462>
35                               print_graphviz_preorder: void(), <common/printgraph.c 340>
36                                                                   nodes_contain: bool_t(), <common/printgraph.c 77>
37                                                             print_graphviz_node: void(), <common/printgraph.c 324>
38                                                         print_graphviz_preorder: void(), <common/printgraph.c 340>
39                                print_graphviz_callers: void(), <common/printgraph.c 406>
40                                                               nodes_contain: bool_t(), <common/printgraph.c 77>
41                                                         print_graphviz_node: void(), <common/printgraph.c 324>
42                                         nodes_contain: bool_t(), <common/printgraph.c 77>
43                  free_nodes: void(), <common/graph.c 230>
44                free_g_nodes: void(), <common/graph.c 453>
45                               free_g_node: void(), <common/graph.c 50>

余りに詳しく表示されすぎちゃったかな。少し表示レベルを控えめにしてみる。

[sakae@secd ~/cflow-0.0.6]$ cflow -ACGP -d 2 cgraph/*c common/*c
 1 main: int(), <cgraph/cgraph.c 65>
 2                       usage: void(), <cgraph/cgraph.c 50>
 3             create_excludes: node_t*(), <common/graph.c 520>
 4                               add_excludes: node_t*(), <common/graph.c 493>
 5            lex_create_graph: bool_t(), <cgraph/clexer.c 512>
 6                                    get_next_token: int(), <cgraph/clexer.c 374>
 7                               get_definition_node: g_node_t*(), <common/graph.c 164>
 8                                        add_g_node: g_node_t*(), <common/graph.c 256>
 9                                   create_sub_node: g_subnode_t*(), <common/graph.c 145>
10                                 add_to_call_stack: bool_t(), <common/graph.c 325>
11                 print_graph: void(), <common/printgraph.c 243>
12                               print_preorder: void(), <common/printgraph.c 136>
13                                print_callers: void(), <common/printgraph.c 192>
14        print_graphviz_graph: void(), <common/printgraph.c 462>
15                               print_graphviz_preorder: void(), <common/printgraph.c 340>
16                                print_graphviz_callers: void(), <common/printgraph.c 406>
17                                         nodes_contain: bool_t(), <common/printgraph.c 77>
18                  free_nodes: void(), <common/graph.c 230>
19                free_g_nodes: void(), <common/graph.c 453>
20                               free_g_node: void(), <common/graph.c 50>

表示をオミットする為のテーブルを作り、それから解析して結果をconsセルみたいのに溜めてくんだな。 全部のファイルを収録したら、普通の方式かgraphviz形式かのどちらかで表示する。

全体のコントロールは、cgraph.c、解析はclexer.c、表示系はprintgraph.cか。Web屋さんとかレール屋 さんが好きな、MVCに習って構成されてるな。

起動したのはcflowなのに、それが出てこないのは何故かと思って調べてみたら、cflowはシェルだった。本体は、C言語用のcgraphと アセンブラ用のasmgraphの2本立てになってる。そんじゃ、例によってgdbのお出まし。-g付きで コンパイルしたcgraphをcgとして用意した。

[sakae@secd ~/cflow-0.0.6]$ gdb -q cg
(gdb) b print_preorder
Breakpoint 1 at 0x804afa7: file ../common/printgraph.c, line 139.
(gdb) run cgraph/*.c common/*.c
Starting program: /usr/home/sakae/cflow-0.0.6/cg cgraph/*.c common/*.c

Breakpoint 1, print_preorder (graph=0xbfbfe704, node=0x28404100, depth=0,
    maxlen=4, pad=3, count=0xbfbfe6bc) at ../common/printgraph.c:139
139         int sublen = 0;
(gdb) c
Continuing.
  1 main: int(), <cgraph/cgraph.c 65>

Breakpoint 1, print_preorder (graph=0xbfbfe704, node=0x28404130, depth=1,
    maxlen=27, pad=3, count=0xbfbfe6bc) at ../common/printgraph.c:139
139         int sublen = 0;
(gdb) c
Continuing.
  2                   setlocale: <>

Breakpoint 1, print_preorder (graph=0xbfbfe704, node=0x28404160, depth=1,
    maxlen=27, pad=3, count=0xbfbfe6bc) at ../common/printgraph.c:139
139         int sublen = 0;
(gdb) p *graph
$1 = {excludes = 0x0, defines = 0x28404070, defcount = 62, statics = 0,
  privates = 0, depth = 2147483647, root = 0x804bbc7 "main",
  rootnode = 0x28404100, complete = 0, reversed = 0}
(gdb) p *node
$2 = {next = 0x28404190, list = 0x0, callers = 0x2840d0b8,
  name = 0x2840d0c0 "getopt", namelen = 6, type = 0x0,
  file = 0x2840e080 "cgraph/cgraph.c", line = -1, ntype = FUNCTION,
  private = 0, printed = 0}

ふむ、print_preorderは再帰呼び出ししてんのか、nodeがconsセルみたいになってるから、上等 手段だな。次は、ちょっと、 print_nodeの中を見て見る。

(gdb) b print_node
Breakpoint 2 at 0x804ae18: file ../common/printgraph.c, line 99.
(gdb) c
Continuing.

Breakpoint 2, print_node (node=0x284040d0, pad=3, maxlen=37, count=7)
    at ../common/printgraph.c:99
99          if (node->line != -1)
(gdb) n
121             printf ("%*d %*s: <>\n", pad, count, (int)maxlen, node->name);
(gdb)
  7                                  exit: <>
122     }
(gdb) p pad
$3 = 3
(gdb) p count
$4 = 7
(gdb) p maxlen
$5 = 37
(gdb) p node->name
$6 = 0x2840d050 "exit"

121行目のprintfの使い方、ちょっと面食らっちゃったぞ。フォーマット指示子の中で%*とすると、 動的に扱ってくれるんだ。初めて知ったよ、こんな事。rubyでも、勿論使えるんだろうな。

irb(main):001:0> x=4
=> 4
irb(main):002:0> printf("%.*f\n", x, 12.345678)
12.3457
=> nil
irb(main):003:0> printf("%.*f\n", x, 12.343210)
12.3432
=> nil

小数点以下4桁表示ってのを変数を介して指定、ちゃんと指定通り、しかもご丁寧に5桁目を四捨五入 してくれると言うオマケ(余計なお世話)付き。調べてみたら、この余計なお世話も世界標準に なってましたよ。

エラーの傾向と対策

前回、reg.rbを書いて、いろいろなソースを登録していった。その時に不可解なエラー出てた。 どんなエラーか整理すると、

/usr/src/bin/pax/ar_io.c: Brace level mismatch at line 411
/usr/src/usr.bin/make/main.c: Brace level mismatch at line 326
/usr/src/usr.sbin/jls/jls.c: Brace level mismatch at line 411
/usr/src/usr.sbin/ppp/chap.c: Brace level mismatch at line 911
/usr/src/usr.sbin/sysinstall/cdrom.c: Brace level mismatch at line 173
/usr/src/libexec/rbootd/bpf.c: Brace level mismatch at line 272
/usr/src/contrib/gcc/profile.c: Brace level mismatch at line 802
/usr/src/contrib/less/prompt.c: Brace level mismatch at line 531
/usr/src/contrib/libreadline/display.c: Brace level mismatch at line 1014
/usr/src/contrib/tcp_wrappers/rfc931.c: Brace level mismatch at line 193
/usr/src/contrib/tcsh/ed.defns.c: Brace level mismatch at line 1808

等だ(まだ他にも有ったかも)。こんなエラー何処で出してるか(想像は付くけど)調べてみると、clexer.cに

   537              (token == BODYSTART) ? level++ : level--;
   538              if (level < 0)
   539              {
   540                  /* That should not happen. */
   541                  fprintf (stderr, "%s: Brace level mismatch at line %d\n",
   542                      filename, line);
   543                  goto error;
    :
   555              (token == ARGSTART) ? arglevel++ : arglevel--;
   556              if (arglevel < 0)
   557              {
   558                  /* That should not happen. */
   559                  fprintf (stderr, "%s: Brace level mismatch at line %d\n",
   560                      filename, line);
   561                  goto error;

BODYってのは関数の定義だろうな。ARGSTARTってのは、多分関数の引数だろう。どちらも同じ エラーメッセージじゃちと困るんで、どちらか分かるようにしとく。

[sakae@secd /usr/src/bin/pax]$ ~/cflow-0.0.6/cg *.c
ar_io.c: Brace level mismatch-ARG at line 411

もう一つぐらい調べてみるか

[sakae@secd /usr/src/usr.bin/make]$ ~/cflow-0.0.6/cg *.c
main.c: Brace level mismatch-BODY at line 326

どちらも有りって事か。そんじゃ、解析対象になってるソースを事故分析してみる。まずは、ar_io.cの 方から

   403          else if (strcmp(NM_TAR, argv0) != 0)
   404                  (void)fprintf(listf,
   405  #       ifdef NET2_STAT
   406                      "%s: %s vol %d, %lu files, %lu bytes read, %lu bytes written.\n",
   407                      argv0, frmt->name, arvol-1, flcnt, rdcnt, wrcnt);
   408  #       else
   409                      "%s: %s vol %d, %ju files, %ju bytes read, %ju bytes written.\n",
   410                      argv0, frmt->name, arvol-1, (uintmax_t)flcnt,
   411                      (uintmax_t)rdcnt, (uintmax_t)wrcnt);
   412  #       endif
   413          (void)fflush(listf);

ふむ、確かに、fprint内の引数部分でエラー検出してるな。#ifdefで切り分けてるんか。cflowは そこまで考慮してないんで、407行目で完結してるはずなのに、411でも完結したって事で、エラーに 落としてるんだな。

ならば、cflowの実験的機能を使って、#ifdefを有効にしてあげよう。

[sakae@secd /usr/src/bin/pax]$ cflow -p -DNET2_STAT=1 ar_io.c
 1          pselect: <>
 2           select: <>
 :

エラーにもならずに、ちゃんと解析してくれたよ。そんじゃ次は、make/main.cだな。どんな ソースになってる?

   320  found:
   321                  if (setMAKEFILE)
   322                          Var_SetGlobal("MAKEFILE", MAKEFILE);
   323                  Parse_File(fname, stream);
   324          }
   325          free(fnamesave);
   326          return (TRUE);
   327  }

326行目がエラーと言われても、おいらにはピピーンと響いてこないね。何かお助けマンはいないかと、 clexer.c内をうろうろしてたら、

#if C_DEBUG
            printf ("Adding function definition %s\n", curname);
#endif

こんなプローブが要所に埋め込まれていました。早速、これを生かしてから実行すると、

Adding function call 'Var_SetGlobal' in func 'ReadMakefile', 321
Adding function call 'Parse_File' in func 'ReadMakefile', 322
main.c: Brace level mismatch-BODY at line 326

うむ、表示してる行番号が1つずれているな。って事は、エラーを検出したのは、325行って事だな。 324行目で、ブロックが終了して、325行目で上位のブロックへ戻るはず。clexer.cのエラー表示 ルーチンを見ると、負のブロック・レベルだよって言ってる。

これを信じれば、main.cに問題有り? viのカッコのバランスを調べるコマンド % を使って 324行目の対を調べると、関数(ReadMakefile)の頭へ飛んで行ったよ。 と言うことは、ソースの字面上では、324行はいらない子って事になる。

いらない子をコメントにしたら、エラーも無くちゃんと解析したよ。

[sakae@secd ~/cflow-0.0.6]$ cflow main.c
  1 main: int(), <main.c 867>
  2                     getenv: <>
  3                    estrdup: <>
  4           check_make_level: void(), <main.c 680>
  :

これって、make/main.cのBUG かな? 新しいソースが出るまで心のうちにしまっておこうか。 それとも、OpenBSDのそれと比べてみる? おいら的には、似てる/似てない観点からすれば、 OpenBSD5.2を入れてって方が面白いんですけどね。

おまけでemacsでもcflow

上で出てきた、cflowのcflowだが、ちと醜い(見にくい)。情報量が多すぎるから? だったら 必要最低限な呼び出し関係だけに絞ればいいかいなって、なる訳だ。そんなのをこれから作る? 面倒だなあ。

この間、emacsでもpngを表示出来る事を知っちゃったので、それでいいよね。cflowが吐く、dot言語の 原稿では、ちと醜いものになってしまうんで、vim cflow-0.0.6/common/printgraph.c して、補足 してあげる。

466     printf ("digraph \"%s\" {\n", "TODO");
467     printf ("  graph [rankdir=LR,overlap=false];\n");

467行目を追加して、グラフの体裁を整えるのだ。

382 //      printf (" [label=\"%ld\"];\n", count);
383         printf ("\n");

また、エッジの数字がうざいと感じたら、抑制しちゃおう。そうすれば複雑な図も少しはすっきりと 見えるだる。

cflowからグラフ(のpngファイル)を作るのは 一発芸と言うか一行野郎なんで、aliasにでも入れておこうとしたら、どうも引数の展開が うまくいかない。

しょうがないので、flowなんて名前のスクリプトを書いたよ。

#!/bin/sh
cflow -ACGP -g "$@" | dot -T png > /tmp/flow.png

emacsでpngファイルを見る時、カーソル移動(C-v,M-v,C-a,C-e等)が普通に出来るんで、AAを 見てる気分だよ。emacsでファイルをリロードするにはどうやったらいいか調べたら、C-x C-v と言うのが有るのね。今回初めて知った。

それから、emacsに巨大なpngファイルを 喰わせると、表示を拒否しちゃうのね。 軟弱なんだか自己保身なんだか分からんけど、何とかしてくれ、>emacsな人々よ。