Mg
前回TECOをやった時、emacsの起源なんて題目でWebを調べていたんだ。そしたら、たまたま 行き着いたページにemacsのクローンなんてのが紹介されてた。
Mgとか言うのがOpenBSDではデフォで採用されてて、最初から入っているそうな。 長年(でもないけど)OpenBSDを使ってきたけど、そんなの知らなかったぞ。本当に入って いるのか? 疑い深いオイラーは早速調べてみたのであります。
起動するとemacsそっくりさんが顔を出した。特徴的なステータスラインだけお眼にかける。
-----Mg: *scratch* (fundamental)--L1-------------------
manは有るか?
NAME mg ? emacs-like text editor SYNOPSIS mg [-n] [-f mode] [+number] [file ...] DESCRIPTION mg is intended to be a small, fast, and portable editor for people who can't (or don't want to) run emacs for one reason or another, or are not familiar with the vi(1) editor. It is compatible with emacs because there shouldn't be any reason to learn more editor types than emacs or vi(1).
ひょっとして、誰かが知らない間に入れてくれたのかな? 親分が認定してるなら、 決定的な証拠が有るはず。そう、ソースツリーに登録されてるか?
[ob: ~]$ ls /usr/src/usr.bin/mg Makefile cscope.c funmap.c macro.c random.c ttydef.h README def.h funmap.h macro.h re_search.c ttyio.c autoexec.c dir.c grep.c main.c region.c ttykbd.c basic.c dired.c help.c match.c search.c tutorial bell.c display.c kbd.c mg.1 spawn.c undo.c buffer.c echo.c kbd.h modes.c sysdef.h version.c chrdef.h extend.c key.h obj@ tags.c window.c cinfo.c file.c keymap.c paragraph.c theo.c word.c cmode.c fileio.c line.c pathnames.h tty.c yank.c
これで決定的になったな。サイズは170kバイトと小ぶりだよ。DebanのデフォEditorである nanoってどれぐらいだろう? ああ、デフォのEditorと言ったらviだな。こちらはサイズが 344kも有った。emacsは大きい(8.0M)、重いってのは嘘だな。
まてまて、クローンですから、まがいものですから、どこかで手抜きが有るに違いありません。 どこで手抜きしてるか、暴いてみろ。それはやぶさかではありませんが、親分の監視の目が 光ってる所では、おおっぴらにはできませんですがな。
だったら、NetBSDなりLinuxでやってみれ。そうすな、最近保障期間が切れた、15.04版のウブ ででもやってみますか。保障が切れたんで、煮ようが焼こうが好きに振る舞えば。。。。
でも、どこにソース有る? そんなのportsに聞いてみれ!あそこには原料の入手先やらが 書いてあるから。
ああ、READMEに面白い事が書いてあった。
You may never have to learn another editor. (But probably will, at least long enough to port Mg...) Mg was formerly named MicroGnuEmacs, the name change was done at the request of Richard Stallman.
portsの更新
portsは、tar玉になったやつを取ってきて、展開しただけっていう放置ぶり。無保守状態。 これじゃいかん、年も改まった事だし、最新に更新しておこう。
FreeBSDだと、cd /usr/ports; sudo make update だけで済んじゃうだけど、OpenBSDとかは どうやるの? OpenBSD を current にする によると、
export CVSROOT="anoncvs@anoncvs.jp.openbsd.org:/cvs" cd /usr/ports cvs -d$CVSROOT up -Pd
NetBSDだとChapter 2. どこからpkgsrcを得て、どうやって最新に保つか に、説明が有った。よかったね。
所で、FreeBSDでは2万数千のportsが登録されてるけど、OpenBSDでは幾つぐらい有るんだろう? ちょいと調べてみる。
[ob: ~]$ find /usr/ports -type d -maxdepth 2 | wc -l 6569
何でも有りと違って、厳選してるのかな? それとも、、、、。NetBSDはどうだ?
nb7$ find /usr/pkgsrc -type d -maxdepth 2 | wc -l 14122
mgは何処に?
mgは、こちらだそうです。
NetBSDのpkgsrcには、似たような名前でmg2aなんてのも有るなあ。mgと友達? mg2a/DESCRを参照すると、NetBSD用にちょいと装飾を施しましたなんて事が書かれていたよ。
あれ、mgと似た名前でngなんてのも有るぞ。それにしても凄い名前だ。
Ng(Nihongo Micro Gnu Emacs) is a Mg(Micro Gnu Emacs)'s japanese port. Ng supports EUC, JIS and SJIS code. Ng also have (rather simple) C-mode. It is also very useful even if you don't need Japanese support.
上で挙げたnanoも有った。Picoってのが原型でGNUが実装しなおしてnanoになったとな。 nanoはPicoに対してライセンス的な問題は有りませんと謳ってる。 RMSはicro Gnu Emacsって名前に異議を唱えてmgに改名させたって商標権を主張したけど、 自分は注意深いのね。そう言えば、ルックアンドフィールがそっくりな事には異議を 唱えなかったのは大人な対応だなあ。
それに対して、アプルと韓国某は、不毛な戦いを繰り広げているように思うのは、 オイラーだけ? どちらが勝つかで売れ行きや株価に直結するから、大らかには いかないのね。
上に出てきた、ピコ、ナノ、マイクロって単位の接頭語だな。まだ、フェムトとかミリには お眼にかかっていない。大きい方は、キロ、メガ、ギガ、テラ、ペタとかが有るな。誰か、editor の命名に迷ったら採用してみれ。新たな元素を作って、命名権を手に入れる事よりずっと簡単だぞ。 言った者勝ちですから。
mg をウブに突っ込んでみる
上記サイトからtar玉を取ってきた。configureなんてお馴染みのものは入っていない。その代わりに、 README_PORTINGなんてのが置いてあった。早速読んでみると、libbsdを入れて、リナちゃんに BSDの真似をしてもらおうって寸法みたい。OpenBSD親分の拡販政策だな。
下記のように歴史のある製品だけど、ちょいと入れられるようにしておかないと広められないからね。 そう言えば、昔はLinuxも簡単にコンパイル出来たけど、今はどうなの?みんなバイナリーで 提供されちゃって敷居が下がるのはいいけど、作る楽しみが奪われている気がするぞ。
* Nov 16, 1986: First release to mod.sources * Mar 3, 1987: First Release (mg1a) via comp.sources.unix * May 26, 1988: Second release: (mg2a) via comp.sources.misc * Jan 26, 1992: Linux port released by Charles Hedrick. This version later makes its way onto tsx-11, Infomagic, and various other Linux repositories. * Feb 25, 2000: First import into the OpenBSD tree, where it is currently maintained with contributions from many others.
Linuxって1992年にはメジャーになってたの? 多分emacsを動かすのが 難しかったのが、mgがもてはやされたのかな。わーい、おいらのlinuxでもemacsが動いて いるよ。凄いだろーってな具合で、普通の人を騙すのはちょろいものだったのでしょう。 (多分、きっと。)
BSD系にはMakefileが、Linux系にはGNUmakefileが用意されてるので、make一発で入る。 インストールはしないで、解体の材料にする。manが読めたらいいな。man manしたら、 こうすると、ローカルの原稿を読めるって書いてあった。
sakae@uB:~/src/mg-20150323$ man -l mg.1
で、manをつらつらと眺めている。デフォのキーバインドが説明されてた。簡易版のチュートリアルも 有るんで、emacsの復習にはうってつけ。知らないコマンドと言うかキーバインドが有ったりして、 奥が深い事を改めて感じる。
どんなコマンドが有るかなとかキーバインドはどうだっけってのは、mgに聞くのが正解。 M-x help すると a b c: って聞いてくる。aはaprops、bはkeyのbinding、cは指定した キーの関数対応。
manしてて、cscopeなんてのが眼についたので、aproposしてみた。
cscope-create-list-of-files-to-index cscope-find-called-functions cscope-find-egrep-pattern C-c s e cscope-find-files-including-fileC-c s i cscope-find-functions-calling-this-functionC-c s c cscope-find-global-definition C-c s d cscope-find-this-file C-c s f cscope-find-this-symbol C-c s s cscope-find-this-text-string C-c s t cscope-next-file cscope-next-symbol C-c s n cscope-prev-file cscope-prev-symbol C-c s p
何やら、cscopeに力が入っているようだ。親分がemacsやるならcscopeは常識でしょって、 必須習得を要求してんだな。素直に従ってみるか。
cscopeとは
etags作ってemacsでソースの中をすいすい飛び回るやつです。emacsは何でも有りでunixの 流儀にそぐわない。
C語のソースをすいすい見る為に開発されたのがcscope。manの一行説明では
NAME cscope - interactively examine a C program
見たいソース軍のある所へ移動して、cscopeするだけで、すいすい閲覧出来る。起動すると 画面の下側に
Find this C symbol: Find this global definition: Find functions called by this function: Find functions calling this function: Find this text string: Change this text string: Find this egrep pattern: Find this file: Find files #including this file: Find assignments to this symbol:
こんな質問項目が出て来る。質問したい項目まで、Ctl-n, Ctl-p もしくは、矢印キーで 移動して、入力すればよい。結果は、上側に番号が振られたファイル名で示されるので、 その番号を指定。viがリードモードで起動する。って、vi知ってる前提だな。
上の結果欄と下の入力欄との切り替えはTABキーが担当。すいすい飛び回れて便利。 なお、cscopeの終了は、Ctl-d ってのは、一部の人にはお約束事項だろう。 そして、すいすい飛び回る為のデータベースを初回の起動時にcscope.outって名前で 自動作成してる。サブdirとかが有る場合は、cscope -R すると良い。
ちょいとネットを探ってみると、
Emacsからcscopeソースコードナビゲータを使ってみよう
emacsから使う人も居るみたい。cscopeでカーネルを読む人が居るって事は、それなりに 便利なんでしょう。mgにも組み込んだから、使ってみれ、だな。
mg 解体
例によって、何処で止まっているか?
(gdb) bt #0 0xb7fdbbe0 in __kernel_vsyscall () #1 0xb7ea5733 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81 #2 0x0805fd40 in ttgetc () at ttyio.c:182 #3 0x08056d38 in getkey (flag=1) at kbd.c:99 #4 0x08056ef9 in doin () at kbd.c:162 #5 0x080594fb in main (argc=0, argv=0xbffff0c8) at main.c:188
gdbで追いかけるのもいいけど、今回はソースをザッピングしてみる。名前から想像するんだぞ。 気ままに見ていてもしょうがないので、少し目標を立てておく。
1.それぞれのユーザーコマンドの呼び出し方法。2.キーバインドはどうやってるか?3.cscopeとの やり取りはどうやってるか? ぐらいかな。
眼についたのが、funmap.c
/* $OpenBSD: funmap.c,v 1.49 2015/03/19 21:22:15 bcallah Exp $ */ /* * If the function is NULL, it must be listed with the * same name in the map_table. */ struct funmap { PF fn_funct; const char *fn_name; struct funmap *fn_next; }; static struct funmap *funs; static struct funmap functnames[] = { {apropos_command, "apropos",}, {toggleaudiblebell, "audible-bell",}, {auto_execute, "auto-execute",}, : {cssymbol, "cscope-find-this-symbol",}, {csfindtext, "cscope-find-this-text-string",}, {csnextfile, "cscope-next-file",}, : {yank, "yank",}, {NULL, NULL,} };
しっかり、OpenBSDの刻印が押されていますなあ。次に眼が行ったのは、keymap.cっていう、 いかにもってやつ。
/* * initial keymap declarations, deepest first */ static PF cHcG[] = { ctrlg, /* ^G */ help_help /* ^H */ }; static PF cHa[] = { apropos_command, /* a */ wallchart, /* b */ desckey /* c */ }; static PF cCsc[] = { cscallerfuncs, /* c */ csdefinition, /* d */ csegrep, /* e */ csfindfile, /* f */ rescan, /* g */ : cssymbol, /* s */ csfindtext /* t */ };
こんなのが幾つも登録されてた。なんとなく、分かったような分からないような。。。 おっ、cscope.cってもろな、名前のファイルが有るなあ。内容はcscopeの通信みたい。 man cscopeするとヒントが得られるかな?
ここからは、動的に追って行く。大雑把な流れとして、main.c/mainの中で
175 for (;;) { : 184 update(CMODE); : 188 switch (doin()) { 189 case TRUE: 190 break; 191 case ABORT: 192 ewprintf("Quit"); 193 /* FALLTHRU */ 194 case FALSE: 195 default: 196 macrodef = FALSE; 197 } 198 }
184行目で、コマンド実行結果の画面への反映。188行目で、キーボード入力、解析、実行って 事みたい。
doinにBPを置いて、tutorialファイルを読み込み、そこからM-f してみた。 kbd.cの中のdoin。
159=> curmap = curbp->b_modes[curbp->b_nmodes]->p_map; 160 key.k_count = 0; 161 while ((funct = doscan(curmap, (key.k_chars[key.k_count++] = 162 getkey(TRUE)), &curmap)) == NULL) 163 /* nothing */; 164 165 if (macrodef && macrocount < MAXMACRO) 166 macro[macrocount++].m_funct = funct; 167 168 return (mgwrap(funct, 0, 1)); 169}
何か、大事そうなcurbpから必要なものをcurmapに取り出しているな。大本データはいかに?
(gdb) p curbp $35 = (struct buffer *) 0x808aff8 (gdb) p *$ $36 = { : b_modes = {0x8075178 <fundamental_mode>, 0x0, 0x0, 0x0}, : b_fname = "/home/sakae/src/mg-20150323/tutorial", '\000' <repeats 987 times>, : b_fi = { fi_uid = 1000, fi_gid = 1000, fi_mode = 33188, fi_mtime = { tv_sec = 1415991727, tv_nsec = 0 } }, : b_lines = 342 }
どうやら、読みこんだファイルに対しての情報のようだ。そして、160行めに達した時、
(gdb) p curmap $37 = (KEYMAP *) 0x80750c0 <fundmap> (gdb) p *$ $38 = { map_num = 8, map_max = 8, map_default = 0x80576a0 <selfinsert>, map_element = {{ k_base = 0, k_num = 7, k_funcp = 0x8075000 <fund_at>, k_prefmap = 0x8074c60 <ccmap> }} }
ファイルの種類に応じて、使えるkeymapが抽出されるんだな。 と、だらだら見て行ってもきりがないので、初心に返って、ヘッダーファイルを覗く。 見るのは、def.h だって、一番太っちょなんだもん。
struct buffer { struct list b_list; /* buffer list pointer */ struct buffer *b_altb; /* Link to alternate buffer */ struct line *b_dotp; /* Link to "." line structure */ struct line *b_markp; /* ditto for mark */ struct line *b_headp; /* Link to the header line */ struct maps_s *b_modes[PBMODES]; /* buffer modes */ int b_doto; /* Offset of "." in above line */ int b_marko; /* ditto for the "mark" */ short b_nmodes; /* number of non-fundamental modes */ char b_nwnd; /* Count of windows on buffer */ char b_flag; /* Flags */ char b_fname[NFILEN]; /* File name */ char b_cwd[NFILEN]; /* working directory */ struct fileinfo b_fi; /* File attributes */ struct undoq b_undo; /* Undo actions list */ struct undo_rec *b_undoptr; int b_dotline; /* Line number of dot */ int b_markline; /* Line number of mark */ int b_lines; /* Number of lines in file */ };
これ、編集してるファイルに対応してるんだな。そして、表示してるバッファーに対応する 構造体 mgwin も定義されてた。
一方、キーマップは、bufferの中でつかっているけど、その定義はkbd.hに有った。
struct map_element { KCHAR k_base; /* first key in element */ KCHAR k_num; /* last key in element */ PF *k_funcp; /* pointer to array of pointers */ /* to functions */ struct keymap_s *k_prefmap; /* keymap of ONLY prefix key in */ /* element */ }; #define KEYMAPE(NUM) { \ short map_num; /* elements used */ \ short map_max; /* elements allocated */\ PF map_default; /* default function */ \ struct map_element map_element[NUM]; /* really [e_max] */ \ } typedef struct keymap_s KEYMAPE(1) KEYMAP; struct maps_s { KEYMAP *p_map; const char *p_name; struct maps_s *p_next; };
ややこしい定義だな。こういうのは図に描くと理解が早まるかな。 長くなってきたので、この辺で。次の回に続く(かも)。