term
Table of Contents
fdisk
ちょっと息抜きにOpenBSDでのfdiskをやってみる。味噌はvnconfigを利用して 普通のファイルをdiskに見立てる技だなあ。河豚板さんからの情報によります。
ob$ dd if=/dev/zero of=mydisk bs=1M count=4000
4000+0 records in
4000+0 records out
4194304000 bytes transferred in 60.370 secs (69476627 bytes/sec)
ob$ doas vnconfig vnd0 /home/sakae/mydisk
ob$ doas fdisk vnd0
Disk: vnd0 geometry: 81920/1/100 [8192000 Sectors]
Offset: 0 Signature: 0x0
Starting Ending LBA Info:
#: id C H S - C H S [ start: size ]
-------------------------------------------------------------------------------
0: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
1: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
2: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
3: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
ob$ doas fdisk -iy vnd0
Writing MBR at offset 0.
ob$ doas fdisk vnd0
Disk: vnd0 geometry: 81920/1/100 [8192000 Sectors]
Offset: 0 Signature: 0xAA55
Starting Ending LBA Info:
#: id C H S - C H S [ start: size ]
-------------------------------------------------------------------------------
0: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
1: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
2: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
*3: A6 1 0 29 - 81919 0 100 [ 128: 8191872 ] OpenBSD
ob$ doas fdisk -gy vnd0
ob$ doas fdisk -v vnd0
Primary GPT:
Disk: vnd0 Usable LBA: 34 to 8191966 [8192000 Sectors]
GUID: 6b16cdd8-db24-4e03-b46a-554dbc1d1e1b
#: type [ start: size ]
guid name
------------------------------------------------------------------------
0: OpenBSD [ 64: 8191903 ]
b1f99615-5eba-47fe-8e94-cbd3c194dd5c OpenBSD Area
Secondary GPT:
Disk: vnd0 Usable LBA: 34 to 8191966 [8192000 Sectors]
GUID: 6b16cdd8-db24-4e03-b46a-554dbc1d1e1b
#: type [ start: size ]
guid name
------------------------------------------------------------------------
0: OpenBSD [ 64: 8191903 ]
b1f99615-5eba-47fe-8e94-cbd3c194dd5c OpenBSD Area
MBR:
Disk: vnd0 geometry: 81920/1/100 [8192000 Sectors]
Offset: 0 Signature: 0xAA55
Starting Ending LBA Info:
#: id C H S - C H S [ start: size ]
-------------------------------------------------------------------------------
0: EE 0 0 2 - 81919 0 100 [ 1: 8191999 ] EFI GPT
1: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
2: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
3: 00 0 0 0 - 0 0 0 [ 0: 0 ] Unused
詳細は、/usr/src/sbin/fdisk/part.c あたりを見て楽しめ、との事。 rdroot/boottmp/usbfadmの大作もお忘れなく。fdiskの編集コマンドを作成して それを実行するとは大変な苦労だなあ。
bug gone
前回発見したバグを潰しておく。
fn make_colection(sec string) ![]string {
org :=os.ls('/usr/share/man/man${sec}')!
mut rv := []string{}
for s in org {
rv << s.before('.${sec}')
}
rv.sort()
return rv
}
元はbefore('.')だったものに、セクション番号を追加しただけ。
ob$ ./mc 5 login login.conf
これで安心。待て、ループ内の不変式は、ループ外へ移動させとくのが常識だろう。 毎回計算するのは無駄。でも賢いコンパイラーなら、そんなの自動でやってくれるよ。
そんな事より、beforeの実装を見ておく方が有益だぞ。
// before returns the contents before `sub` in the string.
// If the substring is not found, it returns the full input string.
// Example: assert '23:34:45.234'.before('.') == '23:34:45'
// Example: assert 'abcd'.before('.') == 'abcd'
// TODO: deprecate and remove either .before or .all_before
pub fn (s string) before(sub string) string {
pos := s.index_(sub)
if pos == -1 {
return s.clone()
}
return s[..pos]
}
ここでもスライスが大活躍してました。いや、それより、こちらの方が重大な責務を 負っている裏方ですよ。
// index returns the position of the first character of the input string.
// It will return `-1` if the input string can't be found.
@[direct_array_access]
fn (s string) index_(p string) int {
if p.len > s.len || p.len == 0 {
return -1
}
if p.len > 2 {
return s.index_kmp(p)
}
mut i := 0
for i < s.len {
mut j := 0
for j < p.len && unsafe { s.str[i + j] == p.str[j] } {
j++
}
if j == p.len {
return i
}
i++
}
return -1
}
今回の場合は、こちらが発動されるのか。
// index_kmp does KMP search.
@[direct_array_access; manualfree]
fn (s string) index_kmp(p string) int {
term
今度は前回ちょろっと作成したコード部分で出力を充実させてみよう。目標は補完 の候補をemacsの様にドロップダウン・リストの形式で表示する事。候補リストが段々 絞られてリストが縮小してく様を見てみたいんだ。
これには前前回にひょんな事から登場したtermライブラリィーで何とかなりそうな雰囲気が あるぞ。早速、調べてみると、例が出てた。ちょっと改変した。
import term
import os
fn main() {
term.clear() // clears the content in the terminal
width, height := 20, 5
println(term.header_left('TITLE', '='))
term.set_cursor_position(x: width / 2, y: 1 + (height / 2))
println(term.bg_blue('hello world'))
term.set_cursor_position(x: 0, y: height)
for {
if var := os.input_opt('press q to quit: ') {
if var != 'q' {
continue
}
break
}
println('')
break
}
println('Goodbye.')
}
実行結果。
==== TITLE =====================================================================
hello world
press q to quit: q
Goodbye.
sakae@lu:mc$
大分、期待してたのとは違う。色落ちしちゃってるけど、青くなってるのは、hello world 文字列のバックグラウンドだけ。全体が青になると思っていた。タイトルの = がエリア サイズの20を無視して、本来の端末幅まで広がってる。widthで宣言した20でクリッピング してくれないの? 兎も角、期待した様な画面制御は実施してくれていなかった。ちゃんとやろうとすると、 茨の道だな。 どうせ茨ならグラフィック関係に道が開けている、 A V module for designing terminal UI apps の方が良さそうだな。
>>> import term
>>> term.yellow('hello')
hello ;; 黄色で文字列が表示されました。
>>> term.yellow('hello').split('')
['', '3', '3', 'm', 'h', 'e', 'l', 'l', 'o', '', '3', '9', 'm']
>>> mut out := ''
>>> for x in s { out += '${x.hex()} ' }
>>> out
1b 5b 33 33 6d 68 65 6c 6c 6f 1b 5b 33 39 6d
termって基本色付け師なんですね。最後はschemeで関数を実行するっぽくhexに変換 してみた。これを見ると、 エスケープ・シーケンスな文字列変換の寄せ集めだ。
なお、yellow()は、入力の値によってのみ出力が決まる純粋関数だ。それに対して
set_cursor_position() みたいな関数には、出力が無い。そんな関数に意味が
有るんか? ええ、この関数を呼び出すとカーソルの位置が変更されるって言う、
副作用バリバリの関数なんだ。この種の関数も結構定義されてるぞ。
examples of term
ちょっとつまらないので、豊富なサンプルを炙り出してみた。
sakae@lu:examples$ find . -name '*.v' | xargs grep 'import term' ./veb/websocket/server.v:import term ./flag/animated_help_text.v:import term.ui as tui ./flag/flag_layout_editor.v:import term.ui as tui ./brainvuck.v:import term ./path_tracing.v:import term ./term_display_sixel.v:import term ./poll_coindesk_bitcoin_vs_usd_rate.v:import term ./term.ui/rectangles.v:import term.ui as tui ./term.ui/pong.v:import term ./term.ui/pong.v:import term.ui ./term.ui/term_drawing.v:import term.ui ./term.ui/event_viewer.v:import term.ui as tui ./term.ui/cursor_chaser.v:import term.ui as tui ./term.ui/vyper.v:import term.ui as termui ./term.ui/text_editor.v:import term.ui as tui ./terminal_control.v:import term ./process/wrapping_interactive_python.v:import term ./term_key_pressed.v:import term ./vmatrix.v:import term ./vmatrix.v:import term.termios ./pendulum-simulation/modules/sim/runner.v:import term ./websocket/ping.v:import term ./websocket/client-server/client.v:import term ./websocket/client-server/server.v:import term ./game_of_life/life.v:import term
vmatrix.vって以前python版を試したね。どんな違いが有るか比べてみるのの一興 かと。
term.ui/text_editor.v は昔懐かしいスクリーン・エディターだ。かの昔に富士通のFM-11と言う
パソコンを使っていた時、自作したんだった。このパソコンは面白い事に、2CPU構成に
なってて、いわゆるsub CPUと呼ばれる奴がIO制御を担当してた。メインCPU側では、IOの
リクエスト・テーブルを用意してコールする。大型コンピュータで言うIOプロセッサーの
発想を継承してた。だから、とっても画面制御なんかは、やり易かった。
例の奴は典型的なイベント・ドリブンな作りになってる。グラフィックなアプリだと皆、 こういう構造にはなるな。皆大好きなvscodeも一緒だからね。
fn event(e &tui.Event, mut a App) {
mut buffer := a.ed
if e.typ == .key_down {
match e.code {
.escape {
exit(0)
}
.enter {
buffer.put('\n')
}
:
48...57, 97...122 { // 0-9a-zA-Z
if e.modifiers == .ctrl {
if e.code == .s {
a.save()
}
} else if !(e.modifiers.has(.ctrl | .alt) || e.code == .null) {
buffer.put(e.ascii.ascii_str())
}
}
else {
if e.modifiers == .alt {
if e.code == .comma {
a.visit_prev_file()
return
}
if e.code == .period {
a.visit_next_file()
return
}
}
buffer.put(e.utf8)
}
}
このコードを読むと、Ctl-s でファイルを保存。Alt-, 若しくは、Alt-. で、 編集対象ファイルを切り替え。それ意外の場合は、画面に反映だ。 ここでは省略しちゃったけど、マウス操作のイベントにも対応してる。 これを充実させていけば、emacsになるか。viみたいにモードが無いeditorは、 ウェルカムであります。
イベントをシステムコールと思えば、これはもうカーネルと作りが同じ構造ですよ。 幾ら行数が多くても(654行)作りが単純なんで読むの苦労は無い。
test of term
上で見てきた様に、termモジュールは、端末の状態を変化させる関数が定義されている
(例: set_cursor_position )。これらは、どう試験されているんだろう? テストに
目覚めた今では、興味津々であります。
ちょいと予想してみる。
- そもそも無理だから、テストはしない。
- コードにフックが仕掛けてあり、テストの時だけエスケープ文字列が返る。
- 頑張って秘密の方法を編み出して、テストしてる。
さあ、どれか。2の予想を確認するには、提供されてる関数の定義を見れば一発だ。
// Sources for ANSI Control Sequences
// https://github.com/RajeshPatkarInstitute/Panim
// https://www.gnu.org/software/screen/manual/html_node/Control-Sequences.html
// https://en.wikipedia.org/wiki/ANSI_escape_code
// Support for Windows
// https://en.wikipedia.org/wiki/ANSI.SYS
// #include <windows.h>
// C.SetConsoleMode(C.ENABLE_VIRTUAL_TERMINAL_INPUT)
// Setting cursor to the given position
// x is the x coordinate
// y is the y coordinate
pub fn set_cursor_position(c Coord) {
print('\x1b[${c.y};${c.x}' + 'H')
flush_stdout()
}
ご丁寧に出所元も提示されてた。特に変な仕掛けは施してない。 それじゃ、テストコードにご対面。
fn test_get_cursor_position() {
original_position := term.get_cursor_position()!
cursor_position_1 := term.get_cursor_position()!
assert original_position.x == cursor_position_1.x
assert original_position.y == cursor_position_1.y
term.set_cursor_position(
x: 10
y: 11
)
cursor_position_2 := term.get_cursor_position()!
term.set_cursor_position(
x: 5
y: 6
)
cursor_position_3 := term.get_cursor_position()!
term.set_cursor_position(original_position)
eprintln('original_position: ${original_position}')
eprintln('cursor_position_2: ${cursor_position_2}')
eprintln('cursor_position_3: ${cursor_position_3}')
// 0,0 is returned on dumb terminals
if cursor_position_2.x == 0 && cursor_position_2.y == 0 {
return
}
if cursor_position_3.x == 0 && cursor_position_3.y == 0 {
return
}
assert cursor_position_2.x == 10
assert cursor_position_2.y == 11
assert cursor_position_3.x == 5
assert cursor_position_3.y == 6
}
冒頭で軽くチェックしてからセットしゲットして、それを比較とな。 運良く、対称的な関数が存在してたから、事無きを得たけど、term.clear() なんてのは、テスト出来るのかな?
// clear clears current terminal screen.
pub fn clear() bool {
if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' {
return false
}
print('\x1b[2J')
print('\x1b[H')
flush_stdout()
return true
}
定義は、これ。で、テストは諦めたようだ。画面がクリアーされちゃうと、ユーザー からクレームが来るかも知れないって小心者なんだろうか? そんなのテストだから しょうがないでしょって強い態度に出る風潮は無くなったのか。
いやいや、それは違うで。画面がクリアーされたかどうかを確認する術が無いのさ。 だからテストが出来ないんだ。どうしてもやりたかったら、端末の前に座っている ユーザー(猫でもいいけど)に参加を強いる事になる。センサー代りになってもらって クリアされたならtrueを、何の変化も無かったらfalseを入力してもらう。これぞ、 ユーザー参加型テストさ。
module term
// utf8_getchar returns an utf8 rune from standard input
pub fn utf8_getchar() ?rune {
:
犬も歩けば棒に当たる、かも知れないので、こんなのが将来役にたつかも知れないな。 心に留めておこう。
div by 0
ちょいと前回の割り算のゼロ割りは、未定義が疑問だったので質問。
Q: 割り算について質問します。ゼロで割るのは数学的にもコンピュータ的にも禁止 されてます。素人考えでは、答は無限大でも良さそうに思えます。 何故禁止なんでしょうか?
A: とても良い質問ですね! 「ゼロで割ると無限大でいいんじゃないの?」という疑問は、誰もが一度は抱く素直な発想です。 でも、数学的にもコンピュータ的にも「ゼロ除算(0で割ること)」が禁止されているのには、**深い理由**があります。以下でわかりやすく説明しますね。
—
### ✅ まず、「割り算」とは何か?
割り算(例えば \( a ÷ b \))は、本質的に「掛け算の逆」です。
つまり、「\( a ÷ b = c \)」というのは「\( b × c = a \)」を意味します。
たとえば:
\[ 6 ÷ 3 = 2 \quad \text{(なぜなら } 3 × 2 = 6 \text{)} \]
—
### ❌ ゼロで割るとはどういうこと?
たとえば「\( 6 ÷ 0 = ? \)」と考えたとします。
この意味は「**0 に何を掛けたら 6 になるか?**」という問いになります。
しかし、**どんな数を 0 に掛けても、結果は常に 0** ですよね?
\[ 0 × 1 = 0,\quad 0 × 9999 = 0,\quad 0 × ∞ = \text{未定義} \]
つまり、**「0 × 何か = 6」になるような「何か」は存在しない**んです。 だから、数学的に「答が存在しない」=「定義できない」となるわけです。
—
### 🔁 「無限大」でいいんじゃないの?という疑問
この疑問も自然です。たとえば、こんなことを考えるかもしれません:
\[ \frac{1}{1} = 1,\quad \frac{1}{0.1} = 10,\quad \frac{1}{0.01} = 100,\quad \dots \]
→ 確かに、**分母が0に近づくと答が大きくなる**から、 「ゼロで割れば無限大でいいのでは?」と思えるわけです。
でも!
- \(\lim_{x \to 0^+} \frac{1}{x} = +∞\)
- \(\lim_{x \to 0^-} \frac{1}{x} = -∞\)
というように、**ゼロに近づく方向によって答が正にも負にもなる**ため、 「0で割る」ことを「∞」と決めつけると**矛盾や混乱**が生じてしまいます。
—
### 💻 コンピュータ的にも困る理由
プログラムで「0で割る」とどうなるかというと…
- **クラッシュする**(ゼロ除算エラー)
- 例外(division by zero)として処理が止まる
- 一部の言語ではNaNやInfinityが返るが、予測不能なバグの元
たとえば:
int x = 5 / 0; // 実行時エラー!
これは安全面からも、システム保護のためにも**厳しく禁止**されてます。
—
### 🧠 結論
- ゼロで割ると、「何を掛けても元に戻らない」ため**数学的に定義できない**。
- ゼロに近づけて「無限大」とする考えは、**方向によって値が変わる**ので不安定。
- コンピュータでは**システム的にエラーやクラッシュの原因**になるため禁止。
—
気になったら、**「極限」や「不定形(∞÷∞など)」の概念**も面白いですよ! 興味あればそこも深掘りしますね。
README
SHAPE なんて本を読んでいる。幾何学の考え方を社会に適用したらどうなるって新鮮な 切り口の本。じっくり読まねば。気分転換に、 他人屋のゆうれい なんてのも読み始めた。それともう一冊、ドリトル先生航海記。
家から図書館の蔵書検索をしたら在庫してた。ただ家での検索では、収納棚番号は 不明。図書館で端末を使えば棚の位置が出てくるけど、もうWindowsの日本語 入力方法を失念してしまっている(ええ、年初から完全なSKKユーザーです)。
IT弱者を装って、ちゃっかり司書さんに聞いちゃいましたよ。日本十進分類で整理 されてましたね。図書事業をCCCへアウトソーシング(例: 佐賀県武雄市)すると、独自の分類番号で 管理されるみたいだけど、普通の分類方法で良かった。家からの検索でも 惜しまずに分類番号を公開してよね。配列のインデックスぐらいは公開しても 問題無いっしょ。待て、配列のインデックス検索なら、
933 = 国会図書館.index('ドリトル先生航海記')
どうやら、こんな風に調べておくと良さそう。 一般的には、これで当りをつけるみたい。 日本十進分類(NDC)検索 でも、オイラーは分類に弱いからなあ。例えばホームセンターへコーヒー・フィルターを購入 しに行ったとする。どこら辺に有る? ドリップ用品の付属品と考えれば、台所用品 売り場。豆を買うついでに、フィルターもお忘れなくって思えば、コーヒー売り場に あって、しかるべくだ。右往左往してAI監視カメラにマークされるぞ。
んな訳で、3冊をパラレル読書してる。が、ゆうれい本が面白くて、俄然優先度が 上昇。いわゆる一気読み状態。こういう時は、20-20-20のルールを励行すべし。 目をいたわりましょうってんで米国の眼科医が推奨してる。 20分事に、20フィート(6m)離れた所を、20秒間みつめましょう。 ついでに、肩と首のストレッチで肩を廻す、首も廻すせば完璧。