Mg (2)
今回は気分を変えて、Mg解体の続きをNetBSDでやってみる。
OSを色々入れていると、性格が分かって面白い。gdbを使った解体ショーをやるには、gdbserverが 入っているリナちゃんとかが楽なんだけど、汝平等に愛せよの精神を発揮です。
こんな事を言い出すには訳がある。たまたま読んでた『アラブからこんにちわ』なんて 本に、アラブの男性は、4人まで妻を娶る事が出来るって下りが出てきた。
この本、20年前に、アラブ首長国連邦の男性の元に嫁いだ勇気ある女性のエッセイ が元になっている。最近、よくTVでやってる外国に嫁いだ日本人妻、のはしりのような人だな。
で、この4人まで娶れるってのはイスラム法で認めているとか。世の野郎どもには羨ましがられる かも知れないけど、この法には理由がある。曰く、昔のイスラムは戦争に明け暮れていた。 当然、戦死する人も多数。戦争未亡人を救済するようなセーフティネットとして、未亡人を 妻にして良い。その代わり、平等に愛せよって掟が課せられる。
第一夫人に家を与えたらなら、第二、、、夫人にも、同等の行いをしなければならない。 差別はいかんぜよ、の世界。こういうのが宗教の教義に組み込まれているのがユニークだ。
他にもイスラム法には有名なラマダン(断食)なんてのが有る。 日本もラマダンを、やらないかね。ラマダンにもちゃんと意味が有るって事を、 遅ればせながら知った次第。
NetBSDでもMg
ウブに入れてるのと同じソースが良かろうってんで、コンパイルしてみたんだけど、エラー。 エラーの内容をちゃんと追うのが筋だけど、楽な方に逃げます。
/usr/pkgsrc/editor/mg って、ちゃんとしたレシピが有りますから、それでやります。 そしたら、NCURSESの製作から始めてくれましたよ。画面制御は外注してるのね。まあ、予想 出来た事だけど。コンパイルは-O2 -g ってオプションで実行された。これじゃ解体には ちょっと不便。work/の中に展開されるmg-20110905の中のMakefileを変更。そこで、再コンパイル。
エラーだよ。何故? 深く追わないで、editor/mgの中のMakefileで実行。コンパイルが完了 してると抜かしおる。はて?
nb7$ ls -a work/ . .home .. .install_done .PLIST .packages .PLIST-1src .patch_done .PLIST-2mac .patch_makevars.mk .PLIST-3mag .pkgdb .PLIST_deps .pkginstall .PLIST_nokeywords .rdepends .barrier_cookie .rrdepends .build_done .stage-install_makevars.mk .build_makevars.mk .subst_path_done .buildlink .subst_unwrap_done .compiler .tools .configure_done .tools_done .configure_makevars.mk .tools_makevars.mk .depends .warning .depends_done .warning-done .destdir .work.log .error .wrapper .error-done .wrapper_done .extract_done .wrapper_makevars.mk .extract_makevars.mk mg-20110905 .gcc
workの直下に隠しファイルが出来ていて、細かい工程の実施状況が記録されてるのね。それを makeは確認してコンパイル済みって認識してるんだな。しょうがないので、それらしいフラグ ファイルである、.build_doneを削除。これで、ちゃんとコンパイルしてくれた。 それにしても、色々細分化されてるな。
mgみたいに画面を占有するアプリは、gdbserver経由がgdb画面と分離出来て便利。が、NetBSDやら OpenBSDには、gdbserverが無いんだ。(何たって、gdbはリナを中心に開発されてますから) こういう時は、mgを起動した後、gdbからattachするのが良い。前回の調査で、どんな所を 回っているか分かってますから。
なお、ウブに入れたソースと今回のpkgsrcからのものには、年代の差がある。しかも、ソースに 色々とパッチが当っている。、、、けど、細かい事は気にするな。大意を掴むのが目的ですから。
(gdb) b doscan Breakpoint 2 at 0x8054cc0: file kbd.c, line 138. (gdb) c Continuing. Breakpoint 2, doscan (map=0x8068d20 <fundmap>, c=27, newmap=newmap@entry=0xbfbfe7b0) at kbd.c:138 138 { (gdb) c Continuing. Breakpoint 2, doscan (map=0xbb902110, c=102, newmap=newmap@entry=0xbfbfe7b0) at kbd.c:138 138 {
キー入力を受け取って、keymapを引く部分。M-fってキー入力したにも関わらず、2回呼ばれた。 変数cは、キーボードから受け取った文字コードのはず。27とか102ってどんな文字だ?
こういう時は、gdbをサスペンドするなり、別端末を立ち上げてからmanすればいいんだけど、 gdbからshellコマンドを呼び出せる事を思い出そう。
(gdb) !man ascii
で、27はESC、102はf文字相当だった。C-nしたら、14が返ってきた。ASCIIテーブル上は、soって文字、 コントロールn相当だな。一文字キーインしてるのに、片や1文字、片や2文字返ってくる。 どゆ事? これはOSの深い所に入っていかないと駄目かな? それともlibncursesの成せる技?
取り合えず先に進む。裸のgdbコンソールではちと辛いので layout srcして、ソースを閲覧しながら。 doscanの出口では、押されたキーが解析されて、何をすべきかが関数の形で返ってくる。
Breakpoint 1, doscan (map=0x8068d20 <fundmap>, c=65, newmap=newmap@entry=0xbfbfe7c0) at kbd.c:138 (gdb) finish Run till exit from #0 doscan (map=0x8068d20 <fundmap>, c=65, newmap=newmap@entry=0xbfbfe7c0) at kbd.c:138 0x080552a2 in doin () at kbd.c:169 Value returned is $15 = (int (*)(int, int)) 0x80552e0 <selfinsert>
これは大文字のAを押した。そのまま入力されるんで、selfinsert関数が選ばれた。
Breakpoint 1, doscan (map=0x8068d20 <fundmap>, c=27, newmap=newmap@entry=0xbfbfe7c0) at kbd.c:138 (gdb) finish Run till exit from #0 doscan (map=0x8068d20 <fundmap>, c=27, newmap=newmap@entry=0xbfbfe7c0) at kbd.c:138 0x080552a2 in doin () at kbd.c:169 Value returned is $16 = (int (*)(int, int)) 0x0 Breakpoint 1, doscan (map=0xbb902110, c=102, newmap=newmap@entry=0xbfbfe7c0) at kbd.c:138 (gdb) finish Run till exit from #0 doscan (map=0xbb902110, c=102, newmap=newmap@entry=0xbfbfe7c0) at kbd.c:138 0x080552a2 in doin () at kbd.c:169 Value returned is $17 = (int (*)(int, int)) 0x805c6b0 <forwword>
今度は、M-fを押した、一度目のscanでmapがESC用に切り替わり、二度目のscanで、相当する 関数forwordが選ばれた。
ここまでは、ふーんだな。初心に帰って、mainを順に追って行くと、いかにもってのが 出て来る。keymap.c/maps_init。
毎朝、PCを立ち上げた時、今日の調べ物(この駄文を 書くためのネタ)に対する最適なOSを選ぶ事にしてる。で、今日はウブちゃんです。
maps_init(void) { int i; struct maps_s *mp; maps = &fundamental_mode; => for (i = 0; map_table[i].p_name != NULL; i++) { mp = &map_table[i]; mp->p_next = maps; maps = mp; } }
map_tableがどうなってるかと言うと
(gdb) p maps $5 = (struct maps_s *) 0x8075208 <map_table+72> (gdb) p *$ $6 = { p_map = 0x8074be0 <helpmap>, p_name = 0x806aa7f "help", p_next = 0x80751fc <map_table+60> } (gdb) p *$.p_next $7 = { p_map = 0x8074ca0 <cX4map>, p_name = 0x806aa72 "c-x 4 prefix", p_next = 0x80751f0 <map_table+48> }
"c-x prefix", "esc prefix", "overwrite", "indent", が続き
$12 = { p_map = 0x807512c <fillmap>, p_name = 0x806aa46 "fill", p_next = 0x8075178 <fundamental_mode> } (gdb) $13 = { p_map = 0x80750c0 <fundmap>, p_name = 0x806aa3a "fundamental", p_next = 0x0
こんな具合にマップが定義されてた。
続いて、funmap.c/funmap_initでユーザーが指定するmgコマンド名と関数アドレスの 対応を登録してた。
(gdb) p *funs $17 = { fn_funct = 0x8062b02 <yank>, fn_name = 0x806a91a "yank", fn_next = 0x8074b88 <functnames+1992> } (gdb) p *$.fn_next $18 = { fn_funct = 0x8054138 <filewrite>, fn_name = 0x806a90f "write-file", fn_next = 0x8074b7c <functnames+1980> } : $184 = { fn_funct = 0x8056975 <apropos_command>, fn_name = 0x8069e54 "apropos", fn_next = 0x0 }
それぞれがリンクリストになってるのは、後で追加が簡単に出来るようにする為の上等手段。
theo_init () at theo.c:57 57 funmap_add(theo, "theo"); 58 maps_add((KEYMAP *)&theomap, "theo");
これ、何だろう? Bot用の文字列っぽいのが色々登録されてるけど。大体theoって、OpenBSDの 教祖様の名前じゃないですか。そんなのがアプリに潜りこんでいるってのは、大親分の威力と 言うか、私物化の極み。 ちょっと、ご挨拶してみっかって事で、M-x theo して起動。味もそっけもない画面になるだけ。
そうか、教祖様には信者から話しかけないといけないんだな。
host down Linux is fucking POO, not just bad, bad REALLY REALLY BAD
ホスト、落ちましたって話しかけたら、返事があった。カナダの訛りかな。
こういう時は、翻訳ソフトの力を借りよう。以前に入れたクリップボード・トランスレータでもいいんだけど、 たまたま図書館で読んだ雑誌に出てた Google chrome 経由で翻訳アプリを使うってのを試してみる。
chromeを起動したら右上にあるいかにもスマホの画面みたいに アプリがいっぱいあるってぽい象形図形(アイコン)をクリック。色々出てきた中から 翻訳アプリをセレクト。
翻訳アプリに切り替わるので、左側枠にコピペ。翻訳せいや ボタンを押すと、右側枠に 結果が出て来る。URLを貼り付けると、右側にリンクが出てくるので、クリックすると Webのページも翻訳できる。世の中、進歩してんのね。 で、翻訳結果。
Linuxはうんちをクソされ、ちょうど悪くない、悪い本当に悪いです
と、theo様は、おっしゃいました。OpenBSD信者なら、是非theo様、語録を読むべし!
I would explain, but I am too drunk. #ifdef is for emacs developers. our kernels have no bugs. the kernel is a harsh mistress.
こういうのに満ち溢れていて、楽しいぞ。
ああ、脱線しちゃったな。mg起動の流れを追っているんだった。
cmode_init () at cmode.c:94 94 funmap_add(cmode, "c-mode"); 95 funmap_add(cc_char, "c-handle-special-char"); 96 funmap_add(cc_brace, "c-handle-special-brace"); :
こちらの方は良く分かるんだけどね。この後、pageupとかtty特有のキーバインドを登録 してるな。
再びdoscanの中。押されたキーの値により、捜すkeymapを更新していき、対応する 関数を割り出すって事をやってるな。
129doscan(KEYMAP *map, int c, KEYMAP **newmap) 130{ 131 struct map_element *elec = &map->map_element[0]; 132 struct map_element *last = &map->map_element[map->map_num]; 133 PF ret; 134 135 while (elec < last && c > elec->k_num) 136 elec++; 137 138 /* used by prefix and binding code */ 139 ele = elec; 140 if (elec >= last || c < elec->k_base) 141 ret = map->map_default; 142 else 143 ret = elec->k_funcp[c - elec->k_base]; 144=> if (ret == NULL && newmap != NULL) 145 *newmap = elec->k_prefmap; 146 147 return (ret);
131,132行目で、探すマップの範囲を決定。135,136行目で、マップを探し、140行目で、ヒット しなかったらデフォルトを採用。ヒットしたら、mapから対応する関数を取り出す。
xcscope
コードを追うのをちょっと一休みして遊んでみる。Mgにはcscopeとやりとりするコードが組み込まれているけど、 ちょっと機能不足。そんな時は、本物のemacsから、elpa経由で xcscope.elを入れてやると便利に使えるようになる。
普通cscopeとのインターフェースならcscope.elだと思うんだけど、そうなっていない。 その理由が、scscope.elのコメントに記されていた。
;; * Other notes: ;; ;; 1. This module is called, "xcscope", because someone else has ;; already written a "cscope.el" (although it's quite old).
それはそうと、これを使うと本家の物(etagを使ったtagジャンプ)を凌駕する便利さが有る。C語のファイルを開いた時、 直ぐにxcscopeを使いたかったら、init.elとかに
(cscope-setup)
と書いておけば良い。これ、内部の話だから、外(の、cscope.el作者さん)には、関係 無いよね。一度cscopeを使うと、*cscope* バッファーで、一文字コマンドを受け付けてくれる。 よく使うのを列挙してくれてる。
RET=Select, SPC=Show, o=SelectOneWin, n=ShowNext, p=ShowPrev, q=Quit, h=Help
他にもgで定義先に飛べるなんて便利。grepの代わりにtとかね。詳細は、Ctl-h m して見れ。
M-x theo
上でtheo様語録を見た。これって、mgのmanには記載されていない。御自らtheo.cを保守してる にも関わらず、UnDocumentsってどゆ事? OpenBSDらしくないぞ!
親分曰く、ソースを読んでくれた者への特典ですって、暗にほのめかしているんだな。 だって、mgを拡張する時の手順がおしげもなく例示されているんですから。正に、
If people keep adding such huge stuff, soon mg will be bigger than emacs.
を行える人材のリクルート用なんです。
そもそも、M-x theo した時の流れが、今まで見てきたルートから逸れてるよ。 注意深く流れをトレースすれば、追い詰められるんだけど。
M-xって打ち込んだ時、流れが変わる。下記のような所で待ってるよー。
(gdb) bt #0 0xb7fdbbe0 in __kernel_vsyscall () #1 0xb7ea4733 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81 #2 0x0805fd40 in ttgetc () at ttyio.c:182 #3 0x08056d38 in getkey (flag=0) at kbd.c:99 #4 0x0804f8e8 in veread (fp=0x8069890 "M-x ", buf=0xbfffef1c "\320\a", nbuf=64, flag=9, ap=0xbfffef10 "X\275\a\b") at echo.c:220 #5 0x0804f6f4 in eread (fmt=0x8069890 "M-x ", buf=0xbfffef1c "\320\a", nbuf=64, flag=9) at echo.c:165 #6 0x08052578 in extend (f=0, n=1) at extend.c:562 #7 0x08057a78 in mgwrap (funct=0x805254b <extend>, f=0, n=1) at kbd.c:439 #8 0x08056f68 in doin () at kbd.c:168 #9 0x080594fb in main (argc=0, argv=0xbffff0c8) at main.c:188
そして、extendの中で、入力した文字列 theo を引数にして名前解決ならぬ、担当関数の 割り出しが行われる。割り出しに失敗すると、No matchって言われるし、関数名(やファイル名)の補間も ここで行っている。補間をどう実現してるか調べるにはうってつけっぽい。
何しろ、theo様御推薦の、mgな人、養成資料ですから。
cscope I/F
最初に立てた見所見物のうち残っているのは、mgがどうやってcscopeとインターフェースしてるかだな。
cscope.cを見ると、cscreatelistってのに、それっぽいコードが載ってる。関数名から想像 するに、cscope-create-list-of-files-to-index 用だろうね。
何か他の適当なものがいいな。mgを使って、aproposし、cscopeを検索。そかこら適当なの
Find this text string: cscope-find-global-definition ------------------------------------------------------------------------------- *** funmap.c <unknown>[61] {csdefinition, "cscope-find-global-definition",}, -------------------------------------------------------------------------------
を検索。よって、csdefinition にBPを置いてみる。なんとも、mgの解体をするのにmgに 協力を仰ぐっていう楽しい事になってますよ。で、裏方のgdbserver+emacs+gdb陣は、
Breakpoint 2, csdefinition (f=0, n=1) at cscope.c:94 94 return (do_cscope(CSDEFINITION)); (gdb) info macro CSDEFINITION Defined at /home/sakae/src/mg-20150323/cscope.c:26 #define CSDEFINITION 1
これぐらいの事なら、mg側のcscope陣にやらせても良かったか。で、do_cscopeに突入すると
425=> clen = snprintf(cmd, sizeof(cmd), "cscope -L -%d %s 2>/dev/null", 426 i, pattern); : 430 if ((fpipe = popen(cmd, "r")) == NULL) { : 452=> while ((buf = fgetln(fpipe, &len)) != NULL) { : 458 pclose(fpipe); : 462 return (popbuftop(bp, WNONE)); 463}
パイプを使って、cscopeに捜索をお願いして、結果を取り込むって定石が展開されてます。 こういう定石なら囲碁より良く知ってるよ。
参考までにcscopeのmanを引いてみると
-L Do a single search with line-oriented output when used with the -num pattern option. -[0-9]pattern Go to input field num (counting from 0) and find pattern.
ふーん。そんじゃ、オイラーも試してみる。
sakae@uB:~/src/mg-20150323$ cscope -L -1 getkey kbd.c getkey 78 getkey(int flag)
sakae@uB:~/src/mg-20150323$ cscope -L -2 getkey kbd.c ttwait 84 if (prompt[0] != '\0' && ttwait(2000)) { kbd.c ewprintf 86 ewprintf("%s", prompt); kbd.c update 88 update(CMODE); kbd.c ttgetc 99 c = ttgetc(); kbd.c CCHR 102 if (c == CCHR('H')) kbd.c CCHR 103 c = CCHR('?'); kbd.c CCHR 104 else if (c == CCHR('?')) kbd.c CCHR 105 c = CCHR('H'); kbd.c CCHR 110 c = CCHR('['); kbd.c getkeyname 114 promptp = getkeyname(promptp,
第二引数と1とか2は、
25#define CSSYMBOL 0 26#define CSDEFINITION 1 27#define CSCALLEDFUNCS 2 28#define CSCALLERFUNCS 3 29#define CSTEXT 4 30#define CSEGREP 6 31#define CSFINDFILE 7 32#define CSINCLUDES 8
このような意味でしたよ。この定数の翻訳も書いてあった。
57static const char *csprompt[] = { 58 "Find this symbol: ", 59 "Find this global definition: ", 60 "Find functions called by this function: ", 61 "Find functions calling this function: ", 62 "Find this text string: ", 63 "Change this text string: ", 64 "Find this egrep pattern: ", 65 "Find this file: ", 66 "Find files #including this file: " 67};
正に、分からない事はソースに聞け、ですな。
readme
上で触れた、一文字入力で、二文字分のデータが返ってくる闇。ちゃんと暴こうとすると 結構大変みたい。
ユーザーが普通使うコードはASIIコード。それに対して、キーボードの(物理的な)キー そのものにもキーコード(番号)が振ってあるとな。だから、シフトキーとaのキーを同時に押すと、 Aに相当するコードが返ってくる。
2つのキーが同時に押されているって事を検出して、OSは送出するASCIIコードを変更してる 訳だ。OSの奥深い所でね。
キーコードを得るには、普通xevなんてのを使うとな。それがどんな風になってるかは、下記の 資料を見ると良い鴨。
Linuxで入力待ちなしkeyread関数のようなものはありますか?
FreeBSDでもMg
以前、vboxからFreeBSDを使うと、HostのWindows7のCPU資源を食い潰す(不具合)に、気付いて いた。今回対策を考えてみる。ぐぐる様に聞くと、ウブが重くてしょうがないとか、CentOSが 新しくなったら、CPU資源を食い潰すようになったとかがやたらヒット。
そういう雑音を除去して、それらしいのに抽出した。
c:\...\VirtualBox>VBoxManage.exe modifyvm FreeBSD10 --cpuexecutioncap 30
これってvboxの自主規制だな。Windowsのエクスポーラーあたりで、ulimitをかける機能って 無いの? と、無い物強請りです。きっとMacOSには有るんでしょうな。
で、効果の程を確かめる。遅くなった。当たり前だろ。CPUをちょっとしか使わないように 設定したんだから。もっと遅くして工夫するって修行も良いかも。 さっぱり(本物の)emacsが上がってこない。emacsはやたらに資源を食い潰す、重いアプリで ある事を実感。こういう時は、mgの出番ですよ。対になるのは、edですよ。勿論。
portから入れて、Makefileをいじって再コンパイル。いつの間にか、portsの工程管理が 変わっていたぞ。
$ ls -a ./ .extract_done.mg._usr_local ../ .patch_done.mg._usr_local .PLIST.mktmp .stage_done.mg._usr_local .build_done.mg._usr_local mg-20150323/ .configure_done.mg._usr_local stage/
stageなんてのが新設されてた。stageの下にusr/local/ なんてのが出来て、そこに仮の インストールをするのね。よって、再コンパイルに先立ち、.build*と.stage*それに.PLIST*を消去の事。
BSD系のmanをその場で読むには、次のようにする。ちとリナより面倒。
$ mkdir -p man/man1 $ mv mg.1 man/man1/ $ man -Mman mg
KB3035583
Windowsに取付いたウィルス、みな迷惑してますね。 「Windows 10にアップグレードしませんか?」のポップアップ広告プログラムがさらにアグレッシブに変更される
人民が英知を傾けて、悪霊を追い払う御符を作っているようですが、相手は更に 強力な技を仕掛けてきたようだ。攻防戦の結末はいかに? そこらの漫画より面白そう。 (不謹慎な!)