line-edit.scm

壊れた

台所の3口コンロのうちの火力が強いメインのやつが壊れた。料理担当の女房がおお弱り。新品への買い替えが必要との事。品が無くて1月待ちとガス屋さんから連絡が来た。

そう言えば、友人の所の風呂炊き給湯器が壊れ、半年待ちとか言っていた。ホームセンターからたらいを買ってきて、カラスの行水をやってると言ってたけど、いまだ不自由してるのだろうか?久しぶりに連絡取ってみようかな。

TVに繋いでいるHDDが壊れかけている。起動したりしなかったり。叩くと直る事がある。重症っぽい診断。もう10年ぐらいは使っているんで、諦めて買い替えるか。値上がりしてるんだろうな。怖くて調べられないですよ。

笑わない数学なんてのを撮りそこねた。見るニュートン誌だから諦めてもいいんだけど、ものは試しとNHKプラスに挑戦。firefoxはサポートしてませんですって。ググル系のブラウザーにしなさいって、多様性を損なう事になるけど、それで委員会? たかがブラウザーごときで、系列を指定させるって、何考えているんだ。皆様のNHKじゃなくて、ググルに媚びを売るNHK。

ついでに、働き方改革を謳うのは、新たな番組制作をしたくない言い訳。おかげで、平家物語なんて言う昔の人形劇(勿論、再放送)を見てるぞ。ひょっこりひょうたん島の人形劇を彷彿させてくれて、これはこれで、楽しいけど。

SSDの延命

転ばぬ先の何とやらとは、よく聴く話だ。たまたま、こんなのに出会った。

SSDのデータはなぜ消失するのか、寿命を延ばすテクニック

今迄Windowsは、コールド起動とシャットダウンって運用してた。これだと不要な読み書きが発生するんで、SSDにはよくないとの事。スリープを使えとな。

スリープからの復帰で、ネットに繋らないとかマウスの認識に失敗するとか、画面がブラックアウトしないかとか、OSSなOSで報告される事象がないか、点検しながら使ってみよう。

もし駄目なら、元の運用に戻すだけですから。それから、仮想記憶がどーたらって示唆もあるけど、オイラーは、そんなメモリー喰いな使いかたはしてない。メモリー喰いの最大なやつは、ブラウザーですって知ってるからね。

そもそもWindowsを上げさげして使う習慣がついたのは、昔勤めていた会社の情報システム部門の影響だな。毎朝rebootしないとWindowsNTが可笑しくなると言う話からだ。その頃オイラーは、FreeBSD 2.2.8だからでWebをやってたけど、Uutimeが軽く1000日を越えてもなんなく動いていた。それに引き換えWindowsって駄目なOSなのねってのが、刷り込まれてしまったのさ。

今はまともになったっぽい。嬉しいのは、VMWAREで仮想OSを起動したままに出来る事。起動時に、指定したメモリーサイズ分のワークファイルが作成されるのは知ってたけど、いかんともしがたいって事で、放置してたんだ。今度はそんな事を気にしなくてもよくなるのか。

at OpenBSD

OpenBSDなコンソール /dev/ttyC0 でgoshを動かしてみる。 Scm_Getc が、'\r' を受領した後、 Scm_Putc が呼び出された。

(gdb) bt
#0  Scm_Putc (c=10, p=0x431c2ee0) at ./portapi.c:160
#1  0x09448c29 in libionewline (SCM_FP=0x43174180, SCM_ARGCNT=1, data_=0x0) at libio.scm:787
#2  0x09327c40 in run_loop () at ././vmcall.c:192
#3  0x0931ff9f in user_eval_inner (program=<optimized out>, codevec=<optimized out>) at vm.c:1572
#4  0x0932102a in apply_rec (vm=0x6a007acc, proc=<optimized out>, nargs=<optimized out>) at vm.c:1691
#5  Scm_ApplyRec (proc=<optimized out>, args=<optimized out>) at vm.c:1711
#6  safe_eval_wrap (kind=<optimized out>, arg0=<optimized out>, args=0xb, cstr=0x15f41070 "(read-eval-print-loop)", env=0x293\
20508 <userModule>, result=0x0) at vm.c:1844
#7  0x0932110e in Scm_EvalCString (expr=0x15f41070 "(read-eval-print-loop)", env=0x29320508 <userModule>, packet=0x0) at vm.c\
:1877
#8  0x15f44d21 in enter_repl () at main.c:753
#9  0x15f4546e in main (ac=1, av=0xcf7f8974) at main.c:897

このPutsは、REPLのPの部分か。ならば、GetsはRの部分なんだな。と、言う事はGetcでエコーが行われているんだな。

vbox$  wsconsctl
display.type=vga-pci
display.fontwidth=8
display.fontheight=16
display.emulations=vt100
display.screentypes=80x25,80x25bf,80x40,80x40bf,80x50,80x50bf
 :
gosh$ 123A    ;; C-M-x
Unknow keystroke C-[. Type M-h b for the list of key bindings.
gosh$ 123     ;; ESC C-x

easy way by Debian

こんなアドバイスが有った。

shiro

ESC [ dd;dd R は現在のカーソル位置問い合わせの返事です。ターミナルは「現在の表示内容」は覚えてないので、
「範囲指定して取り込み」ということはやってません。キーストロークを拾ってGauche側で内容を管理しています。libsrc/text/line-edit.scm あたり。

少し、下調べしてみる。

sakae@pen:/tmp$ strace -o LOG gosh
gosh$ 89
89
gosh$ (exit)
sakae@pen:/tmp$ wc LOG
  1580  10595 137582 LOG

straceを使ってgoshをHack(89)してみた。終了はSIGQUITでもいいんだけど、画面制御がおかしくなるので、正しい作法の(exit)です。多少ログサイズが増えるけど、よしとしよう。

で、膨大なログの最後の方300行ぐらいを見ればいいかな。

ioctl(0, TCGETS, {B38400 -opost isig -icanon -echo ...}) = 0
  :
read(0, "8", 1)                         = 1
  :
read(0, "9", 1)                         = 1
select(1, [0], NULL, NULL, {tv_sec=0, tv_usec=0}) = 0 (Timeout)
write(1, "\33[?25l", 6)                 = 6
write(1, "\33[0m", 4)                   = 4
write(1, "\33[31;1H", 7)                = 7
write(1, "gosh$ ", 6)                   = 6
write(1, "\33[J", 3)                    = 3
write(1, "\33[31;7H", 7)                = 7
write(1, "8", 1)                        = 1
write(1, "\33[31;8H", 7)                = 7
write(1, "9", 1)                        = 1
write(1, "\33[31;9H", 7)                = 7
write(1, "\33[?25h", 6)                 = 6
read(0, "\r", 1)                        = 1
  :
ioctl(0, TCGETS, {B38400 -opost isig -icanon -echo ...}) = 0
ioctl(0, SNDCTL_TMR_START or TCSETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
write(1, "\33[0m", 4)                   = 4
write(1, "\33[?25h", 6)                 = 6
exit_group(0)                           = ?
+++ exited with 0 +++

ご丁寧に、読み取ったデータを書出しているね。エスケープシーケンスは前回の資料で要確認だな。その他、画面制御の為のioctlが頻繁に発せられている事が分る。

また、ログの前段階で、termios.soを読み込んでいるので、画面制御をそれに委ねているのかな? gauche.termiosを見る限りでは、今回の用例には、機能不足のように思える。

truss by FreeBSD

BSDでシステムコールの追跡をしようと思ったら、ktrace/kdumpが頭に浮かぶ。ただ残念な事に、詳細が出てこないんだ。何かヒントは無いものかとktrace(1)してたら、dtrace,trussなんてのが紹介されてた。

HISTORY
     The truss command was written by Sean Eric Fagan for FreeBSD.  It was
     modeled after similar commands available for System V Release 4 and
     SunOS.

昔使った事があるな。早速、コンソールでhackしてみる。

ioctl(0,TIOCGETA,0xffbfeb38)                     = 0 (0x0)
ioctl(0,TIOCGETA,0xffbfe3c8)                     = 0 (0x0)
ioctl(0,TIOCGETA,0xffbfe3d8)                     = 0 (0x0)
ioctl(0,TIOCSETA,0x2186bc34)                     = 0 (0x0)
 :
write(1,"gosh$ ",6)                              = 6 (0x6)
write(1,"\^[[6n",4)                              = 4 (0x4)
read(0,"8",1)                                    = 1 (0x1)
read(0,"9",1)                                    = 1 (0x1)
read(0,"\r",1)                                   = 1 (0x1)
SIGNAL 3 (SIGQUIT) code=SI_KERNEL

goshを起動して89を入力、そして^\でSIGQUITを発生。入力に対するechoの処理が無いぞ。 次は、PuTTYから実行。

ioctl(0,TIOCGETA,0xffbfea58)                     = 0 (0x0)
ioctl(0,TIOCGETA,0xffbfe2e8)                     = 0 (0x0)
ioctl(0,TIOCGETA,0xffbfe2f8)                     = 0 (0x0)
ioctl(0,TIOCSETA,0x21867b44)                     = 0 (0x0)
 :
read(0,"9",1)                                    = 1 (0x1)
select(1,{ 0 },0x0,0x0,{ 0.000000 })             = 0 (0x0)
write(1,"\^[[?25l",6)                            = 6 (0x6)
write(1,"\^[[0m",4)                              = 4 (0x4)
write(1,"\^[[31;1H",7)                           = 7 (0x7)
write(1,"gosh$ ",6)                              = 6 (0x6)
write(1,"\^[[J",3)                               = 3 (0x3)
write(1,"\^[[31;7H",7)                           = 7 (0x7)
write(1,"8",1)                                   = 1 (0x1)
write(1,"\^[[31;8H",7)                           = 7 (0x7)
write(1,"9",1)                                   = 1 (0x1)
write(1,"\^[[31;9H",7)                           = 7 (0x7)
write(1,"\^[[?25h",6)                            = 6 (0x6)
read(0,"\r",1)                                   = 1 (0x1)
  :
write(1,"\r\n",2)                                = 2 (0x2)
  :
SIGNAL 3 (SIGQUIT) code=SI_KERNEL

89の入力に対してちゃんと画面の再描画をやってる。この違いをgauche上のコードで検討すればいいんだな。

なお、画餅になるけど、ports/devel/straceなんていうtrussより親切なやつがあるんだけど、procfsの問題で、パッケージが作成されていない。古いFreeBSDならOKみたいだ。 問題発覚が、7/20ってなってるから、そのうち直るかな。

mount -t procfs proc /proc

ちょっと悔しいので、マウントしてから、goshのproc statusを確認。

sakae@fbox:/proc/6631 $ cat status
gosh 6631 6628 6631 6627 ttyv0 ctty 1661067538,772988 0,137273 0,24224 ttyin 1001 1001 1001,1001,0,5 -

意味は、procfs(5)に説明されてた。興味ある所では、

•   device name of the controlling terminal, or a minus sign
    (“-”) if there is no controlling terminal.
•   a list of process flags: ctty if there is a controlling
    terminal, sldr if the process is a session leader, noflags if
    neither of the other two flags are set.
•   the wait channel message (;; ttyin is this case)

こんな所かな、とんだ道草だわい。

gosh 6707 818 6707 818 pts/2 ctty 1661068675,264285 0,147724 0,23324 ttyin 1001 1001 1001,1001,0,5 -

ついでに、PuTTYからの起動status

into line-edit

ここからは、libsrc/text/line-edit.scmのあたりを見ていく。それに先だって、支援してくれている、console.scmの支援体制を確認。

(export <vt100> <windows-console>

        call-with-console
        putch putstr getch get-raw-chars chready? canonical-mode? beep
        query-screen-size query-cursor-position move-cursor-to
        hide-cursor show-cursor cursor-down/scroll-up cursor-up/scroll-down
        reset-terminal clear-screen clear-to-eol clear-to-eos
        set-character-attribute reset-character-attribute
        with-character-attribute
        ensure-bottom-room

        make-default-console vt100-compatible?))

こういう時、pythonみたいに、関数がどのモジュール由来か表記してあると楽だ。けど、鬱陶しい側面もあるので、コードを書くのを優先するか、読むのを優先するかで違ってくるな。

まずは、入力されないと始まらないって事で、getchに着目して使われいる所を探してみた。

(define (next-keystroke ctx)
  (if (queue-empty? (~ ctx'keystroke-queue))
    (let1 ch (getch (~ ctx'console))
      (if (and (%sigcont-received?)
               (canonical-mode? (~ ctx'console)))
        (let loop ()
          (let1 ch (getch (~ ctx'console) 10000) ;10ms wait
            (unless ch (raise 'sigcont-received))
            (loop)))
        ch))
    (queue-pop! (~ ctx'keystroke-queue))))

ここから、next-keystrokeを探して上位に辿っていく。

(define (handle-command h ch loop redisp)
   :
   [(is-a? h <keymap>)
    (let* ([ch (next-keystroke ctx)]
           [h (keymap-ref h ch)])
      (handle-command h ch loop redisp))]

このあたりかなあ? どうも自信がない。バックトラックします。

折角取得したtrussの結果を活用。selectの後に、エスケープシーケンスが続いている。

write(1,"\^[[?25l",6)    = 6 (0x6)   ;; hide-cursol
write(1,"\^[[0m",4)      = 4 (0x4)   ;; reset-character-attribute ??
  :
write(1,"\^[[?25h",6)    = 6 (0x6)   ;; show-cursol
read(0,"\r",1)           = 1 (0x1)

write出力から、console.scmを参照して、エスケープパターンを割り出してみた。それをline-editに適用すると、%redisplay の中の一節に辿りつく。

consoleでの実行では、どうも%redisplayがすっかり抜け落ちているように見える。

ESC疑惑 ?

ちゃんとエスケープシーケンスが実行出来無いのでは? M-h h こんなキーシーケンスで、説明が得られる

Gauche input editing quick cheat sheet:
  M-h b                  for keymap
  M-h k <keystroke> ...  for help of specific keystroke
  :

その時のtrussの結果。

write(1,"\^[8",2)                                = 2 (0x2)
read(0,"\^[",1)                                  = 1 (0x1)
  :
select(1,{ 0 },0x0,0x0,{ 0.001000 })             = 1 (0x1)
read(0,"h",1)                                    = 1 (0x1)
read(0,"h",1)                                    = 1 (0x1)
 :
write(5,"Gauche input editing quick cheat"...,665) = 665 (0x299)
 :

最初にESCが送られてから、h h て流れになるのね。Mは、慣れでALTキーを利用。このキーの代わりに、C-[ とか、純粋にESCキーを押すと、

sakae@fbox:/tmp $ gosh
Unknown keystroke C-[. Type M-h b for the list of key bindings.
gosh$ hh

こんな注意をうけた。微妙な所だな。

コンソールで同じ事をやると、全くなしのつぶて、注意も受けないし、反応も全くない。

ただ、ESCとかALTキーを多用するvi,emacsは、コンソール上で何等問題なく動いているんで?????な訳ですよ。迷宮入りさせて、後は専従班が細々と捜査を継続するか。時効は無いからね。

sakae@fbox:/tmp $ truss -o LOG truss /bin/echo hello

こういう息抜きをするのは、scheme脳になった人?

vt(4)

SYNOPSIS
     options VT_ALT_TO_ESC_HACK=1

これは何?

/sys/dev/vt/vt_core.c

#if VT_ALT_TO_ESC_HACK
                        if (vd->vd_kbstate & ALKED) {
                                /*
                                 * Prepend ESC sequence if one of ALT keys down.
                                 */
                                terminal_input_char(vw->vw_terminal, 0x1b);
                        }

もろにALTキーをESCキーに置き換えている。あれ? ALTキーって、SHIFTキーみたいにモデファイアーな位置付と思っていたけど、実体があるそれ自身でキーコードを発生するやつなのか。

不安の種が増えたので、裏取り。Altキー やっぱり、修飾キーだった。思い込みはいかんぞな。

実体の無いキーなら、ESCキーの代わりにはならないはず。コンソールでviを使って確認したら、やっぱり機能してなかった。どんなハードとの組み合わせで働くのだろう? 謎がまた一つ増えたぞ。

ちなみに、今のカーネルでは、この機能は有効になっていないっぽい。但し、/sys/conf/NOTESにはカーネル・コンフィグの全てのオプションが列挙されてて、そこにもちゃんと説明されてたぞ。こういう場合は、兄弟OSであるOpenBSDも調べてみればいいのか。

ああ、キーボードで思い出した。カウボーイは西部で馬が倒れても、鞍だけは持って行くとな。キーボードは、人間とコンピュータを結びつける超重要なインターフェース。理想のキーボードを作っちゃった高名な先生がいましたねぇ。お元気かしら? SICPの読書会では、よく同席させて頂きましたけど、あれから何年達つだろう。

i

笑わない数学の復習、虚数編。数の扱いについて厳密な要求をしてくるschemeに登場願う。で、舞台は、 ガウス平面。花形な場面です。

まずは、超有名なあれ。 e(πi) = -1 same as e(πi) + 1 = 0

gosh$ (use math.const)
gosh$ (exp (* pi 0+1i))
-1.0+1.2246467991473532e-16i

上記で右辺が0となってる式の方がかっこいいと思うぞ。1はかけ算の単位元だし、0は足し算の単位元だ。さりげなく取入れられてるのが審美眼的。

gosh$ (define I 0+1i)
I
gosh$ (exp (* pi/2 I))
6.123233995736766e-17+1.0i
gosh$ (exp (* 0 I))
1.0

超有名なやつはπ回転させたやつ。π/2 と 回転無しもやってみた。なお、分かり易いように虚数単位を I としてみた。微妙な誤差に惑わされるかも知れないけど、取り敢えず許せ。ちゃんとそれっぽく表示させたいなら、real-partとかで取出してきて、丸めてしまえばよい。

gosh$ (define z (exp (* 1 I)))
z
gosh$ z
0.5403023058681398+0.8414709848078965i
gosh$ (angle z)
1.0
gosh$ (magnitude z)
1.0
gosh$ (radians->degrees 1)
57.29577951308232

これらの式に出て来る偏角はラジアン単位だ。普通の度に変換する手続も用意されてる。コンビニな世界だなあ。

gosh$ (cos 1)
0.5403023058681398
gosh$ (sin 1)
0.8414709848078965

単位長(magnitude)のベクトルの偏角(angle)1での座標位置が、ei になるんだね。


This year's Index

Home