似てる、似てない?(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の例で参考した先人様