標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

今更だけど、Go では (具体的なレシーバー.メソッド名) を「関数ポインタ」扱い出来た件

たぶん、有識者では常識なんだろうけれども、最近まで認識できていなかった件についてのポエム

type T struct {
    // :
}

func (t *T) Method(){
    // :
}

var t T

と定義されている場合、

var f func() = t.Method

という代入が可能。これの何が嬉しいのかというと、コールバック関数を渡さないといけない時に、具体的な値をあわせて引き渡すことが可能だという点。

昔の自分はそういうのを知らなかったので、呼び出し元で context.WithValue で引き渡すべき値を context.Context に添付し、コールバック関数側は context.Value で引き出すという方法を使っていた。それが悪いというわけではないが、本来静的チェックできるものを動的にチェックとなるので、まれに漏れがあって実行時エラーになったりする。

実際、nyagos にそういうコードが残ってたので、ちょっとそういうの、ちょっとずつ直してる。

( nyagos は Lua 依存のところと、Lua 非依存のところを明確に分けているので、コールバック関数を設定するところで、Lua 引数を想定させていない箇所も多いのだ )

旧コード

type luaKeyT struct{}

var luaKey luaKeyT

// コールバック関数側
func onCommandNotFound(ctx context.Context, sh *shell.Cmd, err error) error {
     L, ok := ctx.Value(luaKey).(Lua)
     if !ok {
          return errors.New("could get lua instance(on_command_not_found)")
     }
     // : 中略
}

// コールバック関数を設定する側
shell.OnCommandNotFound = onCommandNotFound

新コード

type _LuaCallBack struct {
    Lua
}

// コールバック関数側
func (this *_LuaCallBack) onCommandNotFound(ctx context.Context, sh *shell.Cmd, err error)
error {
    L := this.Lua
     // : 中略
}

// コールバック関数を設定する側
shell.OnCommandNotFound = (&_LuaCallBack{Lua: L}).onCommandNotFound

これ、設定漏れがあると could get lua instance(on_command_not_found) という実行時エラーになるんだけど、実際、発生するケースが見付かったりした。

context.WithValue は便利そうに見えて使用事例が意外とないのは、こんな風な値の引き渡し方法が既にあるから、使うケースがレアであるべき…ということがあるのかもしれないなぁ。

最近、更新したプロダクト

CSView v1.5.0

文字コードとして UTF16 をサポートしました。

  • 最初の2バイトが \xFE\xFF、もしくは最初の1バイトが \0 の時
    → UTF16BE
  • 最初の2バイトが \xFF\xFF、もしくは二番目の1バイトが \0 の時
    → UTF16LE

と判断するようにしました。今どき、UTF16 なんて使っているところなんで、ほとんど無いとは思うんですが、昔、自分が仕事でたずさわったツールが UTF16LE の TSV なんて使ってたんですよね… 昔の自分をたすけるつもりで対応しました。

はてなブログクライアント v1.1.0

  • オプション引数を非オプション引数の後にも置けるようにした。
    (例) htnblog -n 10 listhtnblog list -n 10 と書くことが可能

書き忘れて、よくカーソルを戻したりするので

  • htnblog new: ドラフトのコメント欄に End-Point-URL を挿入するようにした

はてなブログは3つまで無料でブログを管理できるんですが、投稿先を勘違いして事故らないように…と

  • htnblog from-stdin: 標準入力から新しい記事を読み込むサブコマンドを用意した
    (フォーマットは htnblog new で表示されるものと同じ)

昔、wifky で書いた「にっき」を今サルベージして、markdown化しているんですが、それのインポートを視野に入れての対応です。

  • エディター設定に "C:/Program Files/vim/vim91/vim.exe" --literal など空白・二重引用符・オプションを含められるようにした

今までは"C:/Program Files/vim/vim91/vim.exe" --literal という名前の実行ファイルだと解釈されていました。本修正で jj や git の動作と同様にスペースは引数との区切り文字だと解釈させるようにしました。そして、jj や git と違って、二重引用符を適切に解釈するようにしていますので、空白が含まれた実行ファイルパスでも大丈夫です。

Sponge v0.2.0

sponge は Go版と Rust 版を作ってますが、Go版の方です。

  • Windowscat -n < FILE | sponge FILE 形式で実行した時、シェルが FILE をクローズしていないため、エラーになる不具合を修正 (一旦、リネームだけして、少なくとも差し替えだけは完了させるようにした)

こちらも、wifky の「にっき」のサルベージ中に気付きました。

cat -n FILE | sponge FILE の時は大丈夫だったんですが、cat -n < FILE | sponge FILE だと、FILE をクローズするタイミングが遅いので、sponge が上書きリネームする時に失敗するという問題がりました。次のように修正しました (ファイル名は例です)

  • 旧動作:
    1. Rename FILE.tmp → FILE … FILE がオープンされているのでエラー
  • 新動作:

    1. Rename FILE → FILE~ … オープンされているファイルのリネームは可能
    2. Rename FILE.tmp → FILE
  • 元ファイルを別名で残すオプションを用意(-b 接尾語)

  • 一時ファイルと同名のファイルが存在している時、エラー終了させるようにした

上だと FILE.tmp に相当する名前のファイルが既にあるとエラーになります。

  • 新ファイルのパーミッションを旧ファイルと同じになるようにした。
  • 一時ファイルのフォーマットとして (original-name)-sponge(process-id) を使うようにした

FILE.tmp とかいう名前だと、ぶつかりやすいので

  • エラーや Ctrl-C で中断された時、ゼロバイトのファイルを作成しないようにした

これはファイルを遅延openする型を作りました。最初の1バイトを書き込むまで open 処理を実施しないようにしただけです。

  • -h で、args[0], バージョン, OS, GOARCH を標準エラー出力に表示するようにした。

これは自分の標準的な動作です。

nyagos 4.4.15_0 → 少し延期

4月1日にリリースするつもりでしたが、3月31日に不具合修正をしたので、様子見期間を設けるため、一週間ほど延期です。

Ctrl-C の入力で、今入力している行を無効化していたのですが、foreach においても「それだけの動作」になっていました。Ctrl-C は内部的には readline.CtrlC というerror型で処理していたんですが、それを判断する箇所をより上位で行うような形にして、foreach 文にて CtrlC を判断できるよう修正しました。1

その他は docs/release_note_ja.md に書いています。SKK 関連の修正はかなり前に行っていたのですが、それだけでも、もっと早く 4.4.14_1 という体裁でリリースしておいた方がよかったかもしれません。


  1. これは文章では分かりにくいので、もうコミット を見た方が早い気も

Git 互換DVCS、jj(jujutsu) でのテキストエディター指定における空白の扱い

基本は

に記載のとおり

  1. 環境変数 JJ_EDITOR
  2. 設定ファイルの ui.editor
  3. 環境変数 VISUAL
  4. 環境変数 EDITOR

の順で参照される。この時、1,3,4 について空白は引数の絶対的な区切リ文字として機能する。つまり

  • set "EDITOR=C:/Program Files/Notepad++/notepad++.exe" → NG
  • set "EDITOR=\"C:/Program Files/Notepad++/notepad++.exe\"" → NG
  • set "EDITOR=""C:/Program Files/Notepad++/notepad++.exe""" → NG
  • set "EDITOR=^"C:/Program Files/Notepad++/notepad++.exe^"" → NG
  • set "EDITOR=notepad++.exe" → OK (%PATH% にディレクトリが登録されている場合)
  • set "EDITOR=C:\PROGRA~1\Notepad++/notepad++.exe\"" → OK
  • set "JJ_EDITOR=C:\PROGRA~1\vim\vim91\vim.exe --literal" → OK

となるようだ。なお、C:\PROGRA~1C:\Program Files のショートファイル名(DOS互換の8.3形式のファイル名) だが、常にそうとは限らない。

2についても単純な文字列として設定する場合は 1,3,4 と同じ扱いになるが、配列として定義する場合のみ、空白を設定可能になる。つまり、以前

で記載した書き方になる。

[ui]
editor = [ "C:/Program Files/vim/vim91/vim.exe", "--literal" ]

ただし、この設定は jj config edit --repo でしか設定できず、jj config get ui.editor で現在の設定内容を確認できなくなるという制限があるようだ。うーん

ちなみに Git も

$ set "GIT_EDITOR=C:/Program Files/Vim/vim91/vim.exe"

$ git commit --amend
hint: Waiting for your editor to close the file... C:/Program Files/Vim/vim91/vim.exe: line 1: C:/Program: No such file or directory
error: There was a problem with the editor 'C:/Program Files/Vim/vim91/vim.exe'.
Please supply the message using either -m or -F option.

おんなじだった。

make-scoop-manifest の引数仕様をシンプルにした

GitHub のレポジトリから scoop 用の manifest を生成するツール: make-scoop-manifest。使い方として次の3種類があると説明をしていたが、分かりにくかった。

  1. レポジトリの場所は git remote show -n で取得し、ZIPファイルがローカルにある場合(コスト最小)
  2. レポジトリの場所はオプションで指定する場合
  3. ZIP ファイルがそもそも手元にない場合

これを 3. をデフォルトにする方向に変更した。

make-scoop-manifest {options...} [REPOSTITORY] {zip-files...}
  • -D,-g は明示的に書かなくてもよいものとした。
    • 互換性は維持しているため、別に書いてもよい(動作は変わらない)
  • レポジトリの指定は基本引数で行う
    • 従来どおりの OWNER/REPOSITORY 形式で指定可能
    • URL を書いてもよい https://github.com/nyaosorg/nyagos
    • カレントディレクトリがワークディレクトリの場合は省略できる
      • その場合、git remote show で情報を得る
  • ZIPファイルは基本的にレポジトリからダウンロードする
    • ローカルディスクの ZIP ファイルを与えることで、ダウンロードを省くこともできる(という説明にする)

結果:

  • make-scoop-manifest -g benhoyt/goawk -D > goawk.json
    make-scoop-manifest benhoyt/goawk > goawk.json
  • make-scoop-manifest -license MIT -D -g mattn/bsky -64 "" > bsky.json
    make-scoop-manifest -license MIT mattn/bsky -64 "" > bsky.json

と書けるようになった。コマンドライン的には -D-g を省けるようになっただけだが、説明がシンプルで済むのが大きい。

一方、Makefile に組み込んで、ローカルディスクにある zip ベースでマニフェストを作る場合もまったく変わらない。

manifest:
    make-scoop-manifest *-windows-*.zip > $(NAME).json

余談: 偽"flag" パッケージを作った

make-scoop-manifest -license MIT mattn/bsky -64 "" というコマンドラインは実は NG である。というのも、非オプション引数の "mattn/bsky" の後にオプション -64 を書いているからだ。標準の "flag" パッケージではこれは許されない。

だが、それだと単純に -D-g を削除して、引数の順番を変えなければいけないことを失念したユーザを戸惑わせてしまう。

ということで、非オプション引数の後にもオプション引数を取れるバージョンの「偽"flag"パッケージ」を作って対応した。

String とBool だけしか使ってなかったから比較的簡単にできた。

SAKURA Pocket を FTP over tls/rclone/restic 経由でバックアップストレージにする

コストパフォーマンス

100GB あたり

プラン 金額 月額
OneDrive(月契約) ¥260-/月 ¥260-
OneDrive(年契約) 2,440/年 ¥204
さくらのレンタルサーバ
(ライトプラン)
¥1,571-/年 ¥131

なので、FTP しか使わないなら十分安い。

自分の場合、CGIサーバを一つ維持しなければいけなかったが、ディスク容量100GBがほぼ無駄になっていたので、それを活用する意味もある。ただし、さくらのレンタルサーバーは昔からあるプランであるため、ファイル名文字コードEUC-JP だったり仕様に古いところがある

FTP over tls を使う

素のFTPだと、転送量がGBクラスのダウンロード時にエラーが発生しがちになる。信頼性確保のため、FTP over tls 接続を使う

rclone に登録する

  1. $ rclone config
  2. メニュー: Current remotes: e/n/d/r/c/s/q>n (接続先を新規作成)
  3. name> → 接続先の名前(なんでもよい。例: sakura)
  4. Storage>16 (FTP)
  5. host> → サーバ名 (*.sakura.ne.jp の方)
  6. port> → Enterのみでよい
  7. FTP username. → ユーザ名
  8. FTP password. → パスワード
  9. Use Implicit FTPS tls>false
  10. explicit_tls>true
  11. Edit advanced config? y/n>n
  12. Keep this "sakura" remote? y/e/d>y
  13. メニュー: Current remotes: e/n/d/r/c/s/q>q

適当なファイルをローカルにコピーしてテスト

$ rclone copy sakura:sakura_pocket/foo/bar .

sakura_pocket は、さくらのウェブツール さくらぽけっと で参照できるエリア。他でもよいが、こちらだとウェブブラウザでのチェックもしやすい。

restic を設定する

バックアップツールの restic から SAKURA Pocket を rclone 経由で使うには、

set "RESTIC_REPOSITORY=rclone:sakura:sakura_pocket/(さくらポケットのディレクトリ)"
set "RESTIC_PASSWORD=(restic を初期化した時のパスワード:SAKURAではない)"

と設定する。nyagos を使っている場合は .nyagos で次のように設定すればよい。

nyagos.env.RESTIC_REPOSITORY="rclone:sakura:sakura_pocket/(さくらポケットのディレクトリ)"
nyagos.env.RESTIC_PASSWORD="(restic のパスワード)"

動作確認

pushd "%APPDATA%"
restic backup .minecraft
restic check   --read-data

check サブコマンドの --read-data オプションは全データをダウンロードしてくるので、指定は初回のみにしておいた方が無難 (毎回やると転送量が大きいので迷惑になる)

check snapshots, trees and blobs
[0:04] 100.00%  5 / 5 snapshots
read all data
[34:21] 100.00%  518 / 518 packs
no errors were found

よかった。tls なしの FTP でやってたとき、ここでエラーが表示されてしまっていたのだ。--upload-limit--download-limit などで転送速度を落してもダメだったので、一時はバックアップ先を OneDrive に変更していたのだが… tls を指定することで解決してよかった。あきらめきれず、未練がましくマニュアルをグダグダ見るのも無駄ではなかった。

vim 9.1 (stable) の scoop installer 用の暫定的な manifest を作った

vim の manifest は scoop-installer の Main bucket に登録されているが、ずっと vim 9.0 のままで、最新版の 9.1 にあがる様子が一向にない。マニフェスト: vim.json が参照しているページ: https://ftp.nluug.nl/pub/vim/pc/vim がずっと 9.0 のままだからだ。

vim 9.1 、入力モードでカーソルの形状がアンダーバー状に変わったり、なかなか良さそうだ。全部手作業でインストールしてもよいのだが、今後の更新やアンインストール時のことを考えると、きちんとパッケージ管理した形でインストールできた方が望ましい。

  • (案1)winget install vim.vim
    • C:\Program Files\vim\vim91 分、%PATH% 長くなって、いやん
    • 管理者権限が必要
  • (案2)nightly 版が GitHub Releases にあがっている
    • 比較的手軽に自動更新の manifest が作れる
    • stable ではない
    • 毎晩更新されるので、無駄に bucket のコミット数が増える

両方ともイマイチなので、make-scoop-manifest で自前で最新の stable版の manifest を作る方法を検討した。

今の make-scoop-manifest は GitHub の Releases しか見ないようになっているが、まずこれを普通のHTMLのページも見られるようにした1。これで、vim.org の Download ページ: https://www.vim.org/download.php のHTML からマニフェストを作ってみた。いつものように hymkor bucket 経由でインストール可能になっている。

scoop bucket add hymkor https://github.com/hymkor/scoop-bucket
scoop install vim-hogehoge

インストール時、あまり要らんことはさせたくなかったので、install.exe や uninstall.exe を呼び出さないなどの点で、Mainvim.json と違いがある。なぜ vim-hogehoge という名前かというと、Main版との違いが分かるような良い名前がなかったためだ。それならば、いっそのこと「雑に作ったもの」と分かるような名前でいいやと考えた。

なお、新make-scoop-manifest.exe は改造点が多過ぎるので、リリース・解説はもうちょっとだけ後にしたいと思う。

(追記:2024.03.18)

make-scoop-manifest の任意のHTMLをスキャンする新仕様だが、よく考えるとそのオプションの内容を検討するより、人間が目視でHTML を確認した方が早く、意味がなかったため、廃案とした。

繰り返し手動で更新するならば使いどころがあったかもしれないが、BucketTemplate を複製して使っている場合は GitHub Actions で自動更新できてしまうのだった。


  1. 他にもいろいろ機能追加が必要だったのだが、書き出すとキリがないので、今回は省略する。

1ファイルなスクリプトはインストール方法で、scoop よりcurl を案内した方がいいかも

hymkor/Download-Count.ps1: A PowerShell script that reports how many assets on GitHub's Releases page have been downloaded.

にあるとおり:

scoop bucket add hymkor https://github.com/hymkor/scoop-bucket
scoop install Download-Count.ps1

よりは

curl -O https://raw.githubusercontent.com/hymkor/Download-Count.ps1/master/Download-Count.ps1

というスクリプト単品をストレートにダウンロードしてもらう方が早くて良いかもしれない。

ただ、これだとダウンロードカウンターが回らないんだよなー