curses
Table of Contents
patch bochs
OpenBSD用のbochsのコードを見ていたら、/dev/ptmxと言うデバイスの代わり をするデバイスが有ればコードを共通化できる事に気がついた。そんなデバイ スが/dev/ptmって名前で用意されてる事を知った。んで、下記のパッチを用意。
ob$ diff term.cc.org term.cc 36c36 < #define BX_DEBUGGER_TERM (BX_DEBUGGER && !defined(__OpenBSD__)) --- > #define BX_DEBUGGER_TERM (BX_DEBUGGER && 1) 192c192 < scr_fd = open("/dev/ptmx",O_RDWR); --- > scr_fd = open("/dev/ptm",O_RDWR);
これで、良いはず。
ob$ gmake c++ -c -I.. -I./.. -I../iodev -I./../iodev -I../instrument/stubs -I./../instrument/stubs -g -O2 -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -pthread term.cc -o term.o term.cc:194:20: error: expression is not assignable stdin = stdout = fdopen(scr_fd,"wr"); ~~~~~~ ^ term.cc:202:9: error: expression is not assignable stdin = old_stdin; ~~~~~ ^ term.cc:203:10: error: expression is not assignable stdout = old_stdout; ~~~~~~ ^ 3 errors generated.
が、あえなく撃沈。 c++語は、本当にcフラフラ語と言っておこう。今からフラフラ語をやるって筋 が悪いぞ。 ホワイトハウスが開発者に対しC++やC言語からRustやJavaなどのメモリ安全性に優れたプログラミング言語への移行を勧める
[sakae@arch bochs-2.7]$ make g++ -c -I.. -I./.. -I../iodev -I./../iodev -I../instrument/stubs -I./../instrum : sr/include/blkid -I/usr/include/sysprof-6 -pthread term.cc -o term.o
念の為リナでやってみると、問題の箇所もスンナリと通過した。GNUの勝手な 解釈がバグの温床になってます、ってか。
#include <ncurses.h> #include <fcntl.h> #lincude <atdlib.h> #include <string.h> static int scr_fd = -1; int main(){ int x, y, w, h; char *str="Hello World"; int key; scr_fd = posix_openpty(O_RDWR); stdin = stdout = fdopen(scr_fd,"wr"); grantpt(scr_fd); unlockpt(scr_fd); fprintf(stderr, "cu -l %s .. and Hit key\n",ptsname(scr_fd)); key = getchar(); initscr(); noecho(); cbreak(); keypad(stdscr, TRUE); getmaxyx(stdscr, h, w); y = h/2; x = (w-strlen(str))/2; while (1) { erase(); move(y, x); addstr(str); refresh(); key = getch(); if (key == 'q') break; switch (key) { case KEY_UP:y--; break; case KEY_DOWN:y++; break; case KEY_LEFT:x--; break; case KEY_RIGHT:x++; break; } } endwin(); return (0); }
cursesを組み込み。
ob$ cc d.c -lcurses d.c:13:18: error: expression is not assignable stdin = stdout = fdopen(scr_fd,"wr"); ~~~~~~ ^ 1 error generated. ob$ egcc d.c -lcurses d.c: In function 'main': d.c:13:18: error: lvalue required as left operand of assignment stdin = stdout = fdopen(scr_fd,"wr"); ^
が、エラーですよ。リナのgccは、自分に都合よく改造してあるのか。
rubyでcurses
cursesって、呪いって意味が有るのね。bochsの呪縛から逃れて、cursesして やるぞ。
Terminalの基礎とRuby、そしてcursesについて
これをやってみたいだけに、ruby 3.3.0をOpenBSDに入れた。欲求駆動型のス タイルだ。例によって、libyamlが無いからって、最後の最後で文句垂れるの やめれ。こういう不親切は、matzさんに通報すればいいのかな。tar玉から入れ るガチの人には常識ですって回答だったら、どうしよう。
ob$ ri Curses = Curses (from gem curses-1.4.4) ------------------------------------------------------------------------ == Description An implementation of the CRT screen handling and optimization library. : == Examples * hello.rb * rain.rb
ob$ locate rain.rb /var/OPT/lib/ruby/gems/3.3.0/gems/curses-1.4.4/sample/rain.rb
雪国では、雨よりまだ雪なんだけどな。
i = cycle_index(i) place_string(ypos[i] - 1, xpos[i], "-") place_string(ypos[i], xpos[i] - 1, "|.|") place_string(ypos[i] + 1, xpos[i], "-")
こんな雨マークが数個登録されてた。これを雪マークに変更するには、美大を 出る素養が必要か。
ユーザーランド上のcurses
軽くcursesを利用してるコマンドを抽出してみる。
ob$ cd /usr/src ob$ find usr.bin -name Makefile | xargs grep -l curses usr.bin/bc/Makefile usr.bin/ftp/Makefile usr.bin/infocmp/Makefile usr.bin/less/less/Makefile usr.bin/mg/Makefile usr.bin/systat/Makefile usr.bin/talk/Makefile usr.bin/telnet/Makefile usr.bin/tic/Makefile usr.bin/tmux/Makefile usr.bin/top/Makefile usr.bin/tput/Makefile usr.bin/tset/Makefile usr.bin/ul/Makefile usr.bin/vi/build/Makefile
systat
代表例として、systatを調査。複数のファイルから構成されているので、利用 してるであろうファイルを抽出。
ob$ grep curses *.[ch] engine.c:#include <curses.h> engine.c: GCC_PRINTFLIKE(1,2) /* defined in curses.h */ engine.c: GCC_PRINTFLIKE(1,2) /* defined in curses.h */ engine.h:#include <curses.h> main.c:#include <curses.h> pftop.c:#include <curses.h> pigs.c:#include <curses.h>
ふむ、エンジンのあたりが、主戦場だろう(と当たりをつける)。 ならば、きっと initscr(),endwin()の組が有るはず。
ob$ grep initscr *.c ob$ grep newterm *.c engine.c: screen = newterm(NULL, stdout, stdin);
initscrの代わりに、newtermが使われているとな。この関数は、
setup_term
って、いかにもって所で使われている。そして、端末のサイズ
が変更されても、追従できるように do_resize_term
なんてのも定義されて
た。
do_resize_term(void) { struct winsize ws; : resizeterm(ws.ws_row, ws.ws_col); columns = COLS; lines = LINES;
サイズが変更されると、cursesの変数である画面サイズが更新されるんで、そ れをsystatの変数にコピーしてるんでな。
その他にcursesゆかりの関数は、どんなのが利用されてる? 調べるのは簡単。 リンクを外して、リンカーが怒るのを眺めるだけ。
ld: error: undefined symbol: noecho >>> referenced by engine.c >>> engine.o:(setup_term) ld: error: undefined symbol: resizeterm ld: error: undefined symbol: wclear ld: error: undefined symbol: beep ld: error: too many errors emitted, stopping now (use -error-limit=0 to see all errors)
リンカーもエラーが多すぎると、呆れて黙りこんじゃうのがデフォの挙動なの か。しつこくネチネチしないのは大人の対応だな。
systatでは、左右の矢印キーで、項目を切り替えられる。もちろんkeypad関数 が利用されてる。して、画面毎に、タイトルや配置が変る。それをどうやって サポートしてるか?
2 users Load 0.45 0.49 0.47 ob.my.domain 07:59:56 IFACE STATE DESC IPKTS IBYTES IFAILS OPKTS OBYTES OFAILS COLLS iwn0 up:U 1 125 0 0 36 0 0 re0 up:D 0 0 0 0 0 0 0 enc0 dn:U 0 0 0 0 0 0 0 lo0 up 0 0 0 0 0 0 0 pflog0 up 0 0 0 0 0 0 0 Totals 1 125 0 0 36 0 0
NICの状態表示ね。if.cにてハンドリングされてる。その大事な定義部分。
/* Define fields */ field_def fields_if[] = { {"IFACE", 8, 16, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, {"STATE", 4, 6, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, {"IPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"IBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {ifails, 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"OPKTS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"OBYTES", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {ofails, 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"COLLS", 5, 8, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"DESC", 14, 64, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, };
これの意味付けは、engine.hに定義されてた。
typedef struct { const char *title; int norm_width; int max_width; int increment; int align; int start; int width; unsigned flags; int arg; } field_def;
後は、自在にカーソルを移動させてデータを更新していくんだな。Webで、任 意のフィールドを更新するのがあるけど、きっとcursesあたりを参考にしたに 違いない。
openpty
こちらは、仮想端末の関係。cursesと関連有る?無い?
ob$ find . -name '*.c' | xargs grep openpty -l ./mg/main.c ./script/script.c ./ssh/sshpty.c ./vi/ex/ex_script.c
fdforkpty
これもptyがらみ。
ob$ find . -name '*.c' | xargs grep ptm : ./tmux/job.c: pid = fdforkpty(ptm_fd, &master, tty, NULL, &ws); ./tmux/spawn.c: new_wp->pid = fdforkpty(ptm_fd, &new_wp->fd, new_wp->tty, NULL, &ws);
qemu
OpenBSDのqemuが割と小気味良く動くので、FreeBSD 14.0を入れる事にした。 これもcursesの動作確認ね。で、qemuが言うには、メモリーがアロケート出来 ませんですとの事。今回は観念してググるを偵察。
ob$ ulimit -d 524288 ob$ ulimit -Sd `ulimit -Hd` ob$ ulimit -d 3145728
ulimitにハードリミットとソフトリミットが有るみたい。何時でも最大限に使 いたいなら、/etc/login.confに設定するのが定石との事。オイラーはそこま で、欲張りではないので、qemuの起動の前で実行する様にした。
インストール時に、debug関係もインストールできる様だったので、 base-dbg.txzも選択したら、何時間待っても先に進まなかったので、これは断 念した。
FreeBSD 13.3-RELEASE
qemeでは、負荷が重いんだなと看破。丁度13.3も出た事なんで、アップグレー ドがてらやってみるか。VMWareだとbootデバイスをCDにする時のBIOS画面に入 いるのが難しかったな。
bios.forceSetupOnce = "TRUE" ## F2 key bios.bootDelay = "15000"
こんな設定によかったはず。んが、install,LiveCDの機能しかサポートしてな かった。急遽インストールって事で、base-dbg.txzも選択したよ。素直に完了。
いきなり、gdbします。
sakae@fb:~ $ gdb -q pwd Reading symbols from pwd... Reading symbols from /usr/lib/debug//bin/pwd.debug... (gdb) b main Breakpoint 1 at 0x4018e8: file /usr/src/bin/pwd/pwd.c, line 65. (gdb) r Starting program: /bin/pwd Breakpoint 1, main (argc=1, argv=0xffbfec20) at /usr/src/bin/pwd/pwd.c:65 65 while ((ch = getopt(argc, argv, "LP")) != -1) (gdb) l 60 int physical; 61 int ch; 62 char *p; 63 64 physical = 1; 65 while ((ch = getopt(argc, argv, "LP")) != -1) 66 switch (ch) { 67 case 'L': 68 physical = 0; 69 break;
どんな舞台が設えてあるかと言うと
sakae@fb:~ $ cd /usr/lib/debug/ sakae@fb:/usr/lib/debug $ ls bin/ lib/ sbin/ boot/ libexec/ usr/ sakae@fb:/usr/lib/debug $ ls bin/ cat.debug getfacl.debug pwd.debug chflags.debug hostname.debug realpath.debug : sakae@fb:/usr/lib/debug $ file bin/cat.debug bin/cat.debug: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), no program header, for FreeBSD 13.3, FreeBSD-style, with debug_info, not stripped
ちゃんとgdb用のバイナリーが置いてありました。
sakae@fb:/usr/lib/debug $ du -sh * 2.6M bin 222M boot 31M lib 320K libexec 16M sbin 1.2G usr
都合、1.6Gぐらい容量が増加してしまったけど、何時でもgdbってのは、リナ とは違うんですって優越感に浸れて嬉しい限りです。