似てる、似てない?(3)
女房が実家から柿を取ってきた。余りに見事に実っているので嬉しくなって取ってきたとか。 鳥さんのデザートになる前に回収したんだとかも言ってた。まだ渋くないかね? 鳥さんだって まだ手(口ばしを)を出していないだろうに! 鳥さんは人間よりも、きっと食べごろを知ってるに 違いない。
折角取ってきたものだから、食べてみましたよ。まだ硬いね。甘い事は甘いけど、後味に渋さが 残る。渋さは姿形だけでいいのに。まあ、国民服のユニクロなんかを着てたら、渋さとは程遠い と思われますがね。勿論、島村とかでもだめです。ああ、話が逸れた。
柿を取る時、高い所のは取れないので、手を伸ばして届く範囲のものにしたとか。太陽に よく当たっていないのもあるんだろうな。天辺付近の太陽が十分に当たった所から取っていくのが 良いと思われ。もう少し熟成させておいて、12月になったら、梯子をかけて本格的に採取 しよう。そして近所にお裾分けだな。 少しは、鳥さん用に残しておいてあげよう。頑張って取っても、食べきれないから。実を残すのには もう一つ理由が有る。来年の豊作をお願いする、木守の役目を務めて貰うためだ。どちらが 主目的かは分からないけど、自然と共に生きる知恵が伝えられていると思うぞ。
干し柿は、甘い柿じゃ作れんしなあ。クックパッドで、柿のデザートでも検索してみっかな。 柿オレなら、簡単そう。アイデアだなあ。 ああ、クックパッドと言えば、元中学生のrubyコミッタが入社して頑張っているんだな。こちらの Web Serverは、nginx みたいだけど、ひょっとして彼がsetupしてたりして。
クックパッドは野菜便と称して、とくしまマルシェを 始めたみたいだ。おいらなんかは、わざわざ徳島品を御取り寄せしなくても、近くの野菜マルシェ (と言う無人販売所)へ行けば、いつでも朝取り野菜が手に入るからね。この間は、銀杏が出てた なあ。
取立てほやほやで、例のあの強烈な臭いが残ってたよ。けど、フライパンで炙ればそんな臭いも 飛んじゃうし、後は美味しい実が残る。秋の味覚だなあ。
TODOの消化
前回は、思わぬ方向に逸れてしまったので、本道に戻ります。cflowを軸にして、似てる/似てないを ライブラリィーの呼び出し回数を調べて判定しようとしてた。書いたスクリプトが、汎用に なってなかったので、改良点をTODOとして何点か上げておいたのだ。
最初に、楽な方をやってみる。コマンドラインから、解析対象を指定。cflowに渡すオプションも コマンドラインから渡す。このスクリプトは、仕様上cflowに一つのCファイルしか渡さないので、 複数のCファイルのそれは、あらかじめ作っておいたcflowの結果を渡せるようにする。
#!/usr/local/bin/ruby
def hdlopt()
opt = ''
opt = ARGV.shift if ARGV[0][0] == '-'
list = ARGV
return list,opt
end
def col(fc, db, opt)
p = fc[-2,2] == '.c' ?
IO.popen("cflow #{opt} -c #{fc}") : open(fc)
while al = p.gets
db[$& + fc] += 1 if al =~ /\w+:/ # Key = func_name : file_name
end
p.close()
end
def show(list, ef, 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
### main HERE ###################
db = {}; db.default = 0
list,opt = hdlopt()
list.each{|fc| col(fc, db, opt)}
ofn = ""
ef = {}
ttl(list)
db.keys.sort.each do |k|
fn,sn = k.split(':')
if fn != ofn
show(list, ef, ofn) if ofn != ''
ofn = fn
ef.clear
end
ef[sn] = db[k]
end
show(list, ef, ofn)
ttl(list)
前回からの変更点は軽微なものだ。早速実行してみる。
[sakae@secd ~/z]$ ./col.rb -ACGP *.c
Function F.c L.c N.c O.c
arc4random 1 --- --- 1
error --- 15 --- ---
errx 18 --- 14 19
getargs --- 1 2 ---
getformat 1 1 1 1
getprec 2 2 2 2
getprogname --- --- 1 ---
is_default 4 --- 4 4
isdefault --- 5 --- ---
main 1 1 --- 1
putdata 2 2 2 2
strlcat 1 --- --- 2
strlcpy 3 --- 1 4
strtonum --- --- --- 1
usage 2 --- 3 2
warnx --- --- --- 1
Function F.c L.c N.c O.c
それでは、複数のファイルから構成するやつをば、まずは、dc
[sakae@secd /usr/src/usr.bin/dc]$ cflow -c *.c > ~/z/dc.f
続いて、bcなんだけど、
[sakae@secd /usr/src/usr.bin/bc]$ ls Makefile bc.library extern.h scan.l bc.1 bc.y pathnames.h
Cのソースの元しか無いので、行儀が悪いけど、この場所で展開してあげる。
[sakae@secd /usr/src/usr.bin/bc]$ sudo make Warning: Object directory not changed from original /usr/src/usr.bin/bc yacc -d -o bc.c bc.y yacc: 1 shift/reduce conflict yacc: 16 reduce/reduce conflicts cc -O2 -pipe -I. -I/usr/src/usr.bin/bc -std=gnu99 -fstack-protector -Wsystem-headers -Werror -Wall -Wno-format-y2k -W -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wunused-parameter -Wcast-align -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls -Wold-style-definition -Wno-pointer-sign -c bc.c lex -t scan.l > scan.c cc -O2 -pipe -I. -I/usr/src/usr.bin/bc -std=gnu99 -fstack-protector -Wsystem-headers -Werror -Wall -Wno-format-y2k -W -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wunused-parameter -Wcast-align -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls -Wold-style-definition -Wno-pointer-sign -c scan.c cc -O2 -pipe -I. -I/usr/src/usr.bin/bc -std=gnu99 -fstack-protector -Wsystem-headers -Werror -Wall -Wno-format-y2k -W -Wno-unused-parameter -Wstrict-prototypes -Wmissing-prototypes -Wpointer-arith -Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wshadow -Wunused-parameter -Wcast-align -Wchar-subscripts -Winline -Wnested-externs -Wredundant-decls -Wold-style-definition -Wno-pointer-sign -o bc bc.o scan.o -ledit -ltermcap gzip -cn bc.1 > bc.1.gz
Cファイルが2個生成されたので、cflowしてみる
[sakae@secd /usr/src/usr.bin/bc]$ cflow -c bc.c scan.c > ~/z/bc.f
同じような要領で、gccをやってみると
[sakae@secd /usr/src/contrib/gcc]$ cflow -c *.c > ~/z/cc.f profile.c: Brace level mismatch at line 802
Uum ... どちらのBugなんでしょ。深入りは止めて、出来た所だけで
[sakae@secd ~/z]$ ./col.rb bc.f dc.f
Function bc.f dc.f
BN_init --- 1
BN_zero --- 1
ENCODE 2 ---
S_ISDIR --- 1
array_node 7 ---
:
yygrowstack 4 ---
yylex 2 ---
yyparse 1 ---
yywrap 1 ---
Function bc.f dc.f
水と油ぐらい異なってますなあ。目指す機能は同じなのに。。。
名称
bc - 任意精度の計算言語
名称
dc - 任意精度の計算機
ちゃんとmanしてみると、dcはHPの逆ポーランド電卓系で、bcの方は、無限精度で計算が出来る プログラミング可能な電卓系でしたよ。
CRUD
もう一つのTODO、FreeBSDの各コマンドの成分分析表の作成と閲覧。一遍に作成ってのはちと無謀な 気がするので、気が向いた時に少しづつ作成出来るようにしよう。そう大方針を立てると、今までに 分析したデータを保存しとく必要がある。必然的にデータベースっぽくなる訳だ。
難しい事は考えずに、CRUD(造る、読む、更新する、削除する)の機能が有ればいいんだ。 今流行りの、key valuse store なんだけど、簡単に使えるのあるかな? rubyのライブラリーを 覗いたら、DBM.so が入っていたんで、
require 'dbm'
DBFILE='DBFILE'
db = DBM.open(DBFILE)
db['hoge']=3210
db['hoge'] += 1
p db['hoge']
db.delete('hage')
db.close
でも、上記を予備実験で走らせるとエラー発生。よくよく調べたら、キーもバリューも、文字列って 言う制限があるのね。はて、どうしよう。3秒考えたら、以前にPythonでピクルスっていう、何でも 保存しちゃうのが有った事を思い出した。
Rubyにも対抗上、同様な機能が有るよね。ライブラリーを調べてみたら、Pstoreなんてのが 入っていたよ。今回はこれを使ってみよーっと。標準添付品だし、問題が出たら堂々とmatzさん 一家に文句を言えるからね。こういう考え方は、MySQLは止めてボラクル使っとけてのと一緒だな。 まあ、ボラクルはブランドで維持費をしっかり取られるけど、担当者は安泰!
アプリは、作成系と参照系の2本だてにする。この方が使い勝手が良いと思ったからだ。 まずは、解析データを貯める方。
まずアプリの名前を決めんとな。crudからrを抜いて、cud.rbでもいいんだけど、何となくreg.rb 決定。これ、registerからのパクリね。早まって、posなんて付けてはいけません。コンビニで バイトしてる人はposに馴染みがあるだろうけどね。
そうすると、参照系は、r.rbとなりそうだけど、某Rと被るので却下。regに対抗してacc.rbに しましょ。決してアキュムレータからの連想ではありません。アクセスからのパクリです。
そんじゃ、reg.rbの方から
#!/usr/local/bin/ruby
require 'pstore'
DBFILE='/home/sakae/z/FreeBSD.dat'
def hdlopt()
opt = ''
if ARGV[0] == nil
puts "Usage: reg.rb [--create] [-d] sorce ..."
exit
end
opt = ARGV.shift if ARGV[0][0] == '-'
list = ARGV
return list,opt
end
def col(fc, db, opt)
if Dir.glob(fc + '/*.c').size == 0
puts "#{fc} ... ignore"
return
end
p = IO.popen("cflow #{opt} -c #{fc}/*.c")
while al = p.gets
db[$& + fc] += 1 if al =~ /\w+:/ # Key = func_name : file_name
end
p.close()
end
def makedb(pb)
pb.transaction do
pb["roots"] = {}
end
end
def deldb(pb,list)
pb.transaction do
hh = pb["roots"]
hh.keys.each do |s|
fn,sn = s.split(':')
hh.delete(s) if list.include?(sn)
end
end
end
def adddb(pb, db)
pb.transaction do
hh = pb["roots"]
hh.merge!(db)
end
end
### main HERE ###################
list,opt = hdlopt()
pb = PStore.new(DBFILE)
if opt == '--create'
makedb(pb)
elsif opt == '-d'
deldb(pb, list)
else
db = {}; db.default = 0
list.each{|fc| col(fc, db, opt)}
adddb(pb, db)
end
全くもって、おいらのスクリプトには、クラスなんて考えるのが面倒そうなものは出てきません。 (だったら、こういうのを勉強して、 是非ともクラス図やシーケンス図や、UML図を描く練習しれ)
行き当たりばったりの作です。Usageが変な所に埋め込まれているのを笑ってください。 いきがかり上、突っ込んで有ります。(本当は引数無しで実行した時のエラー防止)
使い始める前に一度だけ、--createを付けて実行してください。FreeBSD.datと言うpstore用の DBファイルが用意されます。すでにデータが溜まっているDBファイルが有っても、問答無用で 初期化されます。だから、オプション名を長くしてあります。こうしとけば、手が滑って クリアしちゃったって事も無いでしょ。
オプションは、もう一つ有って、-dがそれになります。こちらは、登録しておいたデータを 指定して削除するものです。データは、FreeBSDのコマンド単位に指定します。
オプション無しでは、データ名と言うかコマンド名を指定します。この時、データと言うかソースを 解析して登録してくれます。
[sakae@secd /usr/src/bin]$ ls Makefile date/ hostname/ pkill/ rmdir/ Makefile.inc dd/ kenv/ ps/ setfacl/ cat/ df/ kill/ pwait/ sh/ chflags/ domainname/ ln/ pwd/ sleep/ chio/ echo/ ls/ rcp/ stty/ chmod/ ed/ mkdir/ realpath/ sync/ cp/ expr/ mv/ rm/ test/ csh/ getfacl/ pax/ rmail/ uuidgen/ [sakae@secd /usr/src/bin]$ ~/z/reg.rb [a-z]* expr ... ignore pax/ar_io.c: Brace level mismatch at line 411 rmail ... ignore
これ、FreeBSDの/bin直下に有るコマンドのソースエリアです。ここに移動して登録してみました。 コマンド名 == dir名 ですんで、引数としてこのdir名を与えています。 expr等にはCのソースが無いので、解析と登録が行われません。
また、根元で使っているcflowは、main関数を見つけられないと解析を諦めてしまうようで、csh みたいに、本体のソースが入っていないと、エラーも出ずに登録も行われないと言う挙動に なります。
余談だけど、cshの本体は contrib/tcshに有りました。cshのdirの中には、iconv関係のソースが ぽつんと置かれているだけでした。(コンパイル手順書のMakefileは勿論入ってます)
いろいろな場所(/usr/src/usr.sbinとか)に移動しながら蜜を集めてきたら、FreeBSD.datの サイズは、570k ぐらいになりました。
今回、pstoreの扱い方を漁っていた時、 Webと連携してる人が居て、やはりサイズを気にされて ました。pstore.rbを覗いてみると、簡易wikiの例が入っていて、やはりWebかと思っちゃい ますね。
そうそう、irbを使って対話的にpstoreと格闘する技が紹介されたので、再掲しとく。
$ irb
> require 'pstore'
> db = PStore.new("cache.pstore")
> table = nil
> db.transaction(true) { table = db.instance_variable_get("@table") }
> table.keys
これで安心。これから、アクセス系のacc.rbを作るよ。
pstoreの例で参考した先人様