to deepl.com

Table of Contents

guile on FreeBSD

FreeBSDのguileでもgdbしたいぞ。ちょいとMakefileを改変。

sakae@fb:/usr/ports/lang/guile3 $ diff -u Makefile.org Makefile
--- Makefile.org        2024-05-07 16:02:34.316579000 +0900
+++ Makefile    2024-05-07 16:04:09.306207000 +0900
@@ -35,7 +35,8 @@
 GNU_CONFIGURE= yes
 GNU_CONFIGURE_MANPREFIX=${PREFIX}/share
 CONFIGURE_ARGS=        --program-suffix=-${GUILE_VER} \
-               --enable-lto=no
+               --enable-lto=no \
+               CFLAGS=-g
 # LTO is unconditionally off here because it fails to build with llvm,
 # fails to build even with gcc on some arches (reported for armv7 in
 # particular), and making it conditional on gcc/arch is trickier than

そして、rootになってからmake。宗教上の理由によりsudoは導入してません。 stage0までコンパイルが進んだら、そこで中止。

後は、/usr/ports/lang/guile3/work/guile-3.0.9/libguile/.libs に作成された、太めなlibguile-3.0.so.1.6.0を/usr/local/libに転送。pkgか ら入れた物の上書きになる。 仕上げとして、リンクを貼って導入完了。

sakae@fb:~ $ ls -l /usr/local/bin/guile
lrwxr-xr-x  1 root  wheel  60 May  7 16:32 /usr/local/bin/guile@ -> /usr/ports/lang/guile3/work/guile-3.0.9/libguile/.libs/guile

guileをコンパイルするのに使用されたツール類は、下記の通り。物によって は、事前準備と称して大量にツールがコンパイルされる場合が有るので注意。

-r-xr-xr-x  1 root   wheel     39252 May  7 15:36 bsddialog*
-r-xr-xr-x  1 root   wheel     81388 May  7 15:36 portconfig*
-r-xr-xr-x  1 root   wheel    230196 May  7 15:40 gmake*
-rwxr-xr-x  1 root   wheel     33548 May  7 15:40 pkgconf*
-rwxr-xr-x  1 root   wheel     14164 May  7 15:40 bomtool*
lrwxr-xr-x  1 root   wheel         7 May  7 15:40 pkg-config@ -> pkgconf

stat

何で突然にFreeBSDのguileでgdb出来る様にしたか? それは、簡単にユーザー ランドのコマンドをgdb出来るから。guileと言えどもプリミティブにはユーザー ランド相当のコマンドが実装されてる。ならば、FreeBSDだけで、guileを追跡 するのが楽になるだろう。その手始めに、前回やったstatを取り挙げてみる。

外観検査

FreeBSDのstatは、こんなオプションが付いている。オイラーの胸の内では、 FreeBSDってカーネルがBSDになってるLinuxと思うぞ。

-x      Display information in a more verbose way as known from some
        Linux distributions.

man -S 2 stat すると、詳しい説明が出てくる。

st_ctim          Time when file status was last changed (inode data
                 modification).  Changed implicitly by any syscall that
                 affects file metadata, including st_mtim, such as
                 chflags(2), chmod(2), chown(2), truncate(2), utimes(2),
                 and write(2).  Also, any syscall which modifies
                 directory content changes the st_ctim for the affected
                 directory.  For instance, creat(2), mkdir(2), rename(2),
                 link(2), and unlink(2).

st_birthtim      Time when the inode was created.

ファアルの誕生日って、inodeが作成された日時か。人間世界になぞらえたら、 本籍が作成された時になるんだな。

sys/stat.h

struct stat {
        dev_t     st_dev;               /* inode's device */
        ino_t     st_ino;               /* inode's number */
        nlink_t   st_nlink;             /* number of hard links */
	:
        struct  timespec st_mtim;       /* time of last data modification */
#ifdef  __STAT_TIME_T_EXT
        __int32_t st_ctim_ext;
#endif
        struct  timespec st_ctim;       /* time of last file status change */
#ifdef  __STAT_TIME_T_EXT
        __int32_t st_btim_ext;
#endif

st_btim_ext は、後で慌てて付け足しました感が漂っていますよ。これも それも、Linuxに追いつけ追いこせ、ならではですな。ちなみに、OpenBSDには、 ファイルの誕生日を記録するエリアは有りません。これぞ純粋なBSDでありま す。

statコマンドの追跡

FreeBSDでgdbするのは楽チンであります。

sakae@fb:~ $ gdb -q stat
Reading symbols from stat...
Reading symbols from /usr/lib/debug//usr/bin/stat.debug...
(gdb) b main
Breakpoint 1 at 0x40279d: file /usr/src/usr.bin/stat/stat.c, line 211.
(gdb) r -x hy.scm
 :
366                                     rc = lstat(file, &st);
(gdb)
369                     if (rc == -1) {
(gdb)
376                             output(&st, file, statfmt, fn, nonl);
(gdb) p st
$1 = {st_dev = 96, st_ino = 487773, st_nlink = 1, st_mode = 33188,
  st_padding0 = 0, st_uid = 1001, st_gid = 1001, st_padding1 = 0,
  st_rdev = 1017674, st_atim_ext = 0, st_atim = {tv_sec = 1715144585,
    tv_nsec = 929129000}, st_mtim_ext = 0, st_mtim = {tv_sec = 1714778452,
    tv_nsec = 384292000}, st_ctim_ext = 0, st_ctim = {tv_sec = 1715060699,
    tv_nsec = 627559000}, st_btim_ext = 0, st_birthtim = {tv_sec = 1714778452,
    tv_nsec = 384137000}, st_size = 78, st_blocks = 8, st_blksize = 32768,
  st_flags = 0, st_gen = 0, st_spare = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}

リンクファイルにも対応したlstatが利用されてた。

(gdb) c
Continuing.
  File: "hy.scm"
  Size: 78           FileType: Regular File
  Mode: (0644/-rw-r--r--)         Uid: ( 1001/   sakae)  Gid: ( 1001/   sakae)
Device: 0,96   Inode: 487773    Links: 1
Access: Wed May  8 14:03:05 2024
Modify: Sat May  4 08:20:52 2024
Change: Tue May  7 14:44:59 2024
 Birth: Sat May  4 08:20:52 2024
[Inferior 1 (process 6692) exited normally]

stat at guile

今度は、guileで確認。

scheme@(guile-user)> (stat "hy.scm" #f)
$1 = #(96 487773 33188 1 1001 1001 1017674 78 1715144585 1714778452 1715060699 32768 8 regular 420 929129000 384292000 1715060699)
scheme@(guile-user)> (stat:mtime $1)
$2 = 1714778452

statからの回答はベクターなんだな。そこから必要なデータを抽出する関数が 定義されてる。

sakae@fb:~ $ date -r 1714778452
Sat May  4 08:20:52 JST 2024

一応、答合わせをしてみた。

stat:mtime

sakae@fb:/usr/ports/lang/guile3/work/guile-3.0.9/module $ grep stat:mtime -r .
 :
./ice-9/posix.scm:(define (stat:mtime f) (vector-ref f 9))
./ice-9/posix.scm:(define (stat:mtimensec f) (vector-ref f 16))

ちゃんとベクターの順番を保持してる手続が定義されてたぞ。

syscall

そんな訳で、OpenBSDでのシステムコールをちょいと覗いてみます。 7.5に上げた時、仮想マシンが水泡と化してしまったので、卓上でソースの追 跡だけをしましょ。えと、どうやってTAGSを作ったかな? kernel tags ついこの間、やったばかりじゃん。

kern/vfs_syscalls.c

/*
 * Get file status; this version follows links.
 */
int
sys_stat(struct proc *p, void *v, register_t *retval)
{
        struct sys_stat_args /* {
                syscallarg(const char *) path;
                syscallarg(struct stat *) ub;
        } */ *uap = v;

        return (dofstatat(p, AT_FDCWD, SCARG(uap, path), SCARG(uap, ub), 0));
}

pathからファイル情報のindexを得て、次に繋ぐとな。最終結果はsbに格納さ れる。名前付けのルールが分かると読むのもスムース。

dofstatat(struct proc *p, int fd, const char *path, struct stat *buf, int flag)
{
        :
        NDINITAT(&nd, LOOKUP, follow | LOCKLEAF, UIO_USERSPACE, fd, path, p);
        :
        if ((error = namei(&nd)) != 0)
                return (error);
        error = vn_stat(nd.ni_vp, &sb, p);

kern/vfs_vnops.c

/*
 * vnode stat routine.
 */
int
vn_stat(struct vnode *vp, struct stat *sb, struct proc *p)
{
        struct vattr va;
        int error;
        mode_t mode;

        error = VOP_GETATTR(vp, &va, p->p_ucred, p);
        if (error)
                return (error);
        /*
         * Copy from vattr table
         */
        memset(sb, 0, sizeof(*sb));
        :
        sb->st_size = va.va_size;
        sb->st_atim.tv_sec  = va.va_atime.tv_sec;
        sb->st_atim.tv_nsec = va.va_atime.tv_nsec;
        sb->st_mtim.tv_sec  = va.va_mtime.tv_sec;
        sb->st_mtim.tv_nsec = va.va_mtime.tv_nsec;
        :

大体こんな流れになるかな。

ファイルって、構造体なんだ

と、まあstatを肴に色々調べてきての結論。ファイルって構造体をなんだ。っ てか、構造体をdumpしたものが、ファイルの実体なんだね。disk上に置くのが 普通だ。苦もなく使えるからね。tarコマンドを使って、任意のファイルだけを 一まとまりにも出来る(シリアライズだな)。

ものの本では、やれパーミションがどうとか、サイズがどうとかファイルに即 した説明がされているけど、広く捉えると、構造体って事で、型が付いてしま う。そして、我々が日頃お世話になってるテキスト・ファイルは、これらの型 情報を除いた、いわゆるコンテンツって事になる。

Debian/WSL2 stat(2) に statの簡易版の例が出てた。

main(int argc, char *argv[])
{
    struct stat sb;

    if (lstat(argv[1], &sb) == -1) {

    printf("File type:                ");
    switch (sb.st_mode & S_IFMT) {
    case S_IFBLK:  printf("block device\n");            break;
    case S_IFCHR:  printf("character device\n");        break;
    case S_IFDIR:  printf("directory\n");               break;
    case S_IFIFO:  printf("FIFO/pipe\n");               break;
    case S_IFLNK:  printf("symlink\n");                 break;
    case S_IFREG:  printf("regular file\n");            break;
    case S_IFSOCK: printf("socket\n");                  break;
    default:       printf("unknown?\n");                break;
    }
    :
    printf("Last status change:       %s", ctime(&sb.st_ctime));
    printf("Last file access:         %s", ctime(&sb.st_atime));
    printf("Last file modification:   %s", ctime(&sb.st_mtime));

これもそれも、guileをやってて、statに巡り合わせてくれたおかげです。感 謝感激雨あられ、であります。そうでなかったら、ファイルって何だか小難し いものだと、すっと思っていた事でしょう。

to deepl.com

前回、guileのコードを読んでいて(いや、眺めていてだな)、コメントで詳し い説明が有り、なんとかそれをdeepl.comで翻訳させたいと思った。

コメント・マークを外すのは、emacsで簡単にできる(コメントエリアを選択し ておいてからM-;)。後は、それをコピーしてから、下記のスクリプトに喰わせ て、改行をスペースに変換(行の連結っぽいな)する。文の終了を明確に視覚化 する為、'.' を '.\n' に変換してる。

sakae@atom:~$ cat /usr/local/bin/deepl.sh
#! /bin/sh
cat -          |
tr '*\n' '  '  |
sed 's#\.#.\
\
#g'

これらを、全部emacs上で、実行できたら楽だろうな。

予備調査

コメント外しはemacsで、どう実現してるのだろう? emacsに聞いてみる。 C-h k M-; で、どんな関数か炙り出す。

M-; runs the command comment-dwim (found in global-map), which is an
interactive byte-compiled Lisp function in ‘newcomment.el’.

It is bound to M-;.

(comment-dwim ARG)

Call the comment command you want (Do What I Mean).
If the region is active and ‘transient-mark-mode’ is on, call
‘comment-region’ (unless it only consists of comments, in which
case it calls ‘uncomment-region’); in this case, prefix numeric
argument ARG specifies how many characters to remove from each
comment delimiter (so don’t specify a prefix argument whose value
is greater than the total length of the comment delimiters).
Else, if the current line is empty, call ‘comment-insert-comment-function’
if it is defined, otherwise insert a comment and indent it.
Else, if a prefix ARG is specified, call ‘comment-kill’; in this
case, prefix numeric argument ARG specifies on how many lines to kill
the comments.
Else, call ‘comment-indent’.
You can configure ‘comment-style’ to change the way regions are commented.

newcomment.elって1600行もある、機能豊富なファイルだった。核は、 (comment-region BEG END &optional ARG)か。ちょいと追跡すると、 uncomment-region-default-1 が、隠れた本体っぽいぞ。 でも、グチャグチャしてて、追う気Nothing。

Ask to ChatGPT

前回はChatGPTが頓珍漢な答をしてきたので、名誉挽回のチャンスを与えてあ げよう。一遍に色々な事を依頼すると、発狂するので、Step by Stepで行きま す。

Q: emacs lispで、次の機能を実現する関数を提示してください。 テキスト文書に対して、指定したregion内にある改行をスペース 文字に変更し、それを表示する。

A: 次のEmacs Lisp関数は、指定されたregion内の改行をスペース文字に変換して表示する機能を実現します。

(defun replace-newlines-with-space (start end)
  "Replace newlines with space in the region from START to END."
  (interactive "r")
  (save-excursion
    (goto-char start)
    (while (and (< (point) end) (search-forward "\n" end t))
      (replace-match " " nil nil))))

この関数は、`replace-newlines-with-space`という名前で定義されています。`start`と`end`はregionの始点と終点を表し、`interactive`フォームによって、ユーザーがregionを選択した際にこの関数を呼び出すことができます。

関数の本体では、まず`save-excursion`を使ってポイントの位置を保存します。次に、regionの始点に移動し、`while`ループでポイントが終点を超えない限り、改行文字 `\n` を検索し、見つかった場合はスペースに置換します。`search-forward`関数は、指定された文字列を現在のポイント位置から検索します。見つかったらその位置にポイントを移動し、見つからなければ `nil` を返します。`replace-match`関数は、見つかった改行文字をスペース文字に置換します。

この関数を実行するには、Emacsでテキスト文書を開き、regionを選択し、`M-x replace-newlines-with-space`を実行します。

Q: ちゃんと機能しました。どうもありがとう。 次の依頼は、この関数に機能追加です。 '.' が発見された場合 '.\n\n' に変更したいです。

A: 次のように関数を変更して、'.'が見つかった場合に'.\n\n'に変更する機能を追加できます。

(defun replace-newlines-with-space-and-dot (start end)
  "Replace newlines with space and '.' with '.\n' in the region from START to END."
  (interactive "r")
  (save-excursion
    (goto-char start)
    (while (and (< (point) end) (re-search-forward "\\(\n\\|\\.\\)" end t))
      (if (string= (match-string 0) ".")
          (replace-match ".\n" nil nil)
        (replace-match " " nil nil)))))

この変更では、`re-search-forward`を使用して、改行文字 `\n` または '.' を検索します。そして、`match-string`関数を使用して、見つかった文字列を取得し、'.'の場合には'.\n'に置換します。

この関数を実行するには、同じくEmacsでテキスト文書を開き、regionを選択 し、`M-x replace-newlines-with-space-and-dot`を実行します。

教育的に長い名前を提示してきたので、deepl ぐらいな短名に変更。これを評 価しといて、別バッファーで、目的のcps.scmを開く。そして、コメント外し、 そしてdeeplに変換。

In CPS, a term is a labelled expression that calls a continuation.
 A function is a collection of terms.
  No term belongs to more than one function.
  The function is identified by the label of its entry term, and its body is co\
mposed of those terms that are reachable from the entry term.
  A program is a collection of functions, identified by the entry label of the \
entry function.

長い行は右端で継続してる印が表示される。これをそのままdeeplに渡すのは まずい。 M-x toggle-truncate-lines して、抑制をしては見るものの上手くいかない。emacsをテキスト端末から起 動してるゆえの問題。諦めて、一度ファイルに出力して、それをcatしてから 使え。なんだか手間は元の方法と一緒だな。

modfy

ちょっと悔しいので、改造してみた。

(defun deepl (start end)
  "Replace newlines with space in the region from START to END."
  (interactive "r")
  (save-excursion
    (goto-char start)
    (while (and (< (point) end) (search-forward "\n" end t))
      (replace-match " " nil nil)))
  (save-excursion
    (kill-region start end)))

変更されたリージョンをcutしてしまう。そのまま別なバッファーへ行って、 C-y すれば、ペーストされるって寸法。2度リージョンを指定しなくても良く なる。まあ実験つぅ事です。

もしGUI版のemacsを使っているなら、別なバッファーはfirefoxとかが担当で きる。この環境なら、文句なく便利に使えるだろう。例えば、pythonで関数の 説明文書が掲載されてるのが普通。こんなのも、選択しておいてM-x deeplし、 firefoxの英文受け口でC-yするだけだ。

上記コードでは、kill-region で cut 動作をさせちゃったけど、 kill-ring-saveに変更しておけば、copy動作になって、そのまま文書が残る。お好 きにしてください。

ipad

家に有る第4世代のipad(確か10年ぐらい前のもの)は、女房の専用になってい る。電池が消耗したのか、使っていると突然に画面がブラックアウトするって 言ってた。で、新らしいのが欲しいと。

最新機が出るまで待てと、宥めていたら発表されたぞ。発表時のプロモーショ ンビデオが炎上してる。 「Appleに失望」新iPad Proの動画炎上 楽器やカメラをプレス機で破壊

最初にこれおかしいよと指摘したのはMAC界では有名な、林先生だ。その後、 じわじわと世界に広がり、世界的な炎上事件になってしまった。怖いねぇ。

アップル、炎上したプレス機CMについて謝罪 海外メディアの取材に答えて そんでもって、謝罪。当然だわな。

先生と言えば、最近読んだ競走馬を主人公(馬)にした小説がメチャ面白かった ぞ。さりげなく競馬界の事情が説明されてたりして、非常に為?になった。

馬が優勝すると賞金の80%は馬主へ、10%は調教師へ、5%はジョッキーへ、残り は厩舎へ分配するのが常らしい。この配分を見ると調教師って偉い存在って分 かる。業界内では調教師に敬意を表して先生と呼ぶとか。知らない事ばかりで 、世界が広まった様。

著者と書評家の対談を見付けた。 馳 星周『フェスタ』競走馬を愛し、夢を託したすべての人たちの物語 これも面白かったぞ。

話が逸れた。iPadのモデルを比較する

女房はスマホ代わりにiPadを使っているだけだから、一番安い物でいいな。 更に安くならないか? いいことたくさん。 下‍取‍り‍で‍お‍得にア‍ッ‍プ‍グ‍レ‍ード。 再生して売ろうという魂胆なんだな。だから、新らし目のものしか眼中に無いって事だ。

勿論、手持ち品は対象外品でしたよ。


This year's Index

Home