Yearly Archives: 2022

2022-11-06

win-ssh-agentをMSYS2で使う

長年win-ssh-agentを使っているのですが、ふとMSYS2で動作するのか、また、ビルドできるのか気になったので試してみました。

MSYS2でwin-ssh-agentをビルドする

ビルドするには、おそらく

pacman -S base-devel
pacman -S msys2-devel
pacman -S msys2-runtime-devel

あたりが入っていれば良いのだと思います。色々やった後の環境なので、いつの間にか入っているパッケージで必要なものがまだあるかもしれません。

それらを入れてMSYS環境からmakeしてみたところ、無事ビルドが成功して動作もしました。出来上がったexeはCygwinと同じように専用のDLL(msys-2.0.dll)に依存します。win-ssh-agentはpopenやらwait3やらcygwin_internalやら素のWindowsには無い関数を使用しているので仕方ないところです。全部Win32で書き直せば依存関係を減らせますが、CygwinかMSYS2環境下で動かすのが目的なのでやる必要は無いでしょう。

CygwinとMSYS2の両方で使う

通常Cygwin側でwin-ssh-agentを実行してもMSYS2側のsshでは認識されません(毎回パスフレーズが求められます)。反対にMSYS2側でwin-ssh-agentを実行してもCygwin側のsshでは認識されません。

なぜかというと環境変数に保存されるパスがそれぞれの環境独自のパスになっているからです。例えば次のように。

SSH_ASKPASS=/usr/loca/bin/win-ssh-askpass.exe
SSH_AUTH_SOCK=/tmp/ssh-????????????\agent.????

/usr/tmp がどこにマップされているかは環境(CygwinかMSYS2か)で異なります。

これをc:から始まる通常のフルパスに直すとCygwinからでもMSYS2からでも認識されます。

SSH_ASKPASS=c:\cygwin\usr\local\bin\win-ssh-agent/win-ssh-askpass.exe
SSH_AUTH_SOCK=c:\cygwin\tmp\ssh-????????????\agent.????

MSYS2も元はCygwinということできっと方式は同じなのでしょう。

win-ssh-agentではわざわざWindows標準のパスをCygwin用のパスに変換している場所があります(SSH_ASKPASS設定時)。また、ssh-agentから返ってくるCygwin用のパスを変換せずにSSH_AUTH_SOCKに設定しています。これらの場所で確実にWindows用のフルパスに変換してしまえば、CygwinからでもMSYS2からちゃんと認識してくれるようになります。

具体的には、 misc.h, misc.cpp に conv_path_posix_to_win という関数があるので、それをコピーしてstd::stringを返すバージョン(conv_path_posix_to_win_aとか)を作り、setenvするところ(こことかこことか)で変換してしまえばOKです。ちゃんとやるなら全部ワイド文字列にすべきですが、面倒なのでいいや。

最近の事情

最近はWindows版のOpenSSHだとかWSLだとかもあるので状況はより複雑なようです(see: 混沌を極めるWindowsのssh-agent事情 - Qiita)。幸い私はそれらを使っていないのでまだまだこのままで大丈夫なようです。

最近の悩みと言えばCygwinとMSYS2で同じような物を二つ入れておくのが何だか無駄に思えてきたことです。どちらも数GBくらい容量を食いますからね。昔から(それこそMeadowの頃から)常用しているのはCygwinでMSYS2はたまにしか使っていませんが、GitにせよEmacsにせよUnix由来のソフトウェアのWindows版はMSYS2でビルドすることが多いので、それならMSYS2で統一してしまおうかなと考えています。今回のはそのための布石です。ただ、Emacsの設定からCygwin依存部分を除去するのが案外面倒なので二の足を踏んでいます。不具合を無理矢理直している所もあるので。

2022-11-04 ,

org-modernでタグを正確に右寄せする

org-modernはタグを右寄せせず見出しのすぐ右に並べる(org-tags-column=0)ことを想定していますが、私は右寄せ(org-tags-column=-74)のまま使っています。

そうすると何が困るかというと、タグの位置が不揃いになることです。

修正前:タグの位置が不揃い
図1: 修正前:タグの位置が不揃い

org-modernはタグを少し小さめのフォント(:height 0.9)で表示しますし、コロン(:)の前後に空白を入れるのでどうしても幅が変わってしまいます。

文字を小さくしているので文字数単位での調整では修正できません。ピクセル単位での調整が必要です。

というわけで次のように直しました。(org-modern/org-modern.el at 59b2e3c94756b4e37b2cf7b9f81028c6d4758672 · minad/org-modern より修正)

;; org-modern.el (2022-11-04版より修正)

;;;~略~

(defun org-modern--tag ()
  "Prettify headline tags."
;;;~略~
          (setq colon-beg cbeg colon-end cend)))

      ;; 以下を追加。タグの位置を揃える。
      (my-org-modern-align-tags-right beg end)
)))

(require 'shr)

(defvar my-org-modern-tags-right (* (default-font-width) (- 80 3))) ;;揃えるピクセル位置

(defun my-org-modern-align-tags-right (beg end)
  (let* (;;(beg-px (car (window-text-pixel-size nil (line-beginning-position) beg)))
         (end-px (car (window-text-pixel-size nil (line-beginning-position) end)))
         ;;(width-px (if (<= beg-px end-px) (- end-px beg-px) (+ (- (window-text-width nil t) beg-px) end-px)))
         ;; shr-string-pixel-widthを使う。
         ;; つまり別バッファに移してからwindow-text-pixel-sizeで幅の計測を行う。
         ;; 同じバッファでやるとタグが非表示部分の中にあると幅が0になってしまう。
         (width-px (shr-string-pixel-width (buffer-substring beg end)))
         (tags-right my-org-modern-tags-right))
    (when (and (< end-px tags-right) (> width-px 0))
      ;;(put-text-property (1- beg) beg 'display (list 'space :width (list (if (< end-px tags-right) (- tags-right end-px) 0))))
      ;; align-toを使う。widthだと後からorg-indentでずれてしまう。
      (put-text-property (1- beg) beg 'display (list 'space :align-to (list (- tags-right width-px))))
      )))

つまり、org-modernがタグのfontifyを行った直後にタグのピクセル幅を計算し、タグの直前にある空白文字をdisplayプロパティによる空白に置き換えます。そのdisplayプロパティには (space :align-to 揃える位置 - タグ幅) を指定します。

結果は次の通りピッタリ右端が揃いました。

修正後:タグの位置がピッタリ揃っている
図2: 修正後:タグの位置がピッタリ揃っている

リージョンで囲っている部分はdisplayプロパティが適用された空白です。

キモは shr-string-pixel-width 関数です。テキストのピクセルサイズを求めるには window-text-pixel-size 関数を使わなければなりませんが、この関数はバッファとウィンドウを要求します。 shr-string-pixel-width は一時的なバッファを一時的に現在のウィンドウに関連付けて window-text-pixel-size を呼び出してくれます。そんなことをして即時に正しく計算できるのか(レイアウト処理を遅延していないのか)、パフォーマンスは大丈夫なのか心配になりましたが、今のところ問題は見つかっていません。window-text-pixel-sizeのソースコードを読んでおいた方が良いのかもしれません。

検索するとEmacs 25.1で問題があったのを修正した記録があります。

bug#24950: 25.1; shr-string-pixel-width cannot be called from a dedicate

今回の修正は強制的にタグを右端に揃えるのでorg-tags-columnがなんであろうとお構いなしです。環境によって最適なウィンドウ幅は変わります。タグを何桁目で揃えるのか悩み所でしたが、org-tags-column=0にして右寄せは完全にビューの役割にするのも良いかもしれません。

2022-11-03 ,

Org言語のソースコードブロックをエクスポートしたときにorg-modernが影響してしまう問題

久しぶりにOrgの書き方を説明する記事を書いた時に気がついたのですが、エクスポート結果がorg-modernが適用された状態になっていました。

例えば次のOrg文書をエクスポートしたときに……

org-modeでは色々なものを次のように書きます。

#+begin_src org
,* table

| 項目 | 金額 |
|------+------|
| あれ | 1000 |
| それ | 2000 |

,* list

- [ ] item1
- [ ] item2
- [ ] item3

,* link

こんにちは。 [[https://www.google.com/][Google]] 

↑これは以前直した所。
素のorg-modeでも単に下線でGoogleとだけ表示されてしまい
リンクの書き方が分からない。

#+end_src

次のようになっていました。

org-modernの影響を受けたHTMLエクスポート結果
図1: org-modernの影響を受けたHTMLエクスポート結果

これじゃOrgの書き方の説明になりません。もちろん正しくは次のようになっていなければなりません。

通常のHTMLエクスポート結果
図2: 通常のHTMLエクスポート結果

これは全てのバッファでorg-modernを有効にするために、次のように設定したからです。

(add-hook 'org-mode-hook 'org-modern-mode)

org-modeがHTMLでエクスポートするとき、ソースコードブロックの中身に色づけを行います。その処理はorg-html-fontify-code関数にあるのですが、その仕組みは、一時バッファを作成して言語用のモードを立ち上げ、コードを挿入し、font-lock-ensureでバッファの色づけを行い、htmlizeを使ってテキストプロパティを元にHTMLに変換します。

なので、Emacs上で編集しているときの見た目をHTML上で再現してしまうわけです。なんてこった!

同種の現象は以前リンクの書き方でもありました。

org言語のソースブロックをエクスポートしたときにリンクをそのまま出力する

なので今回も同じようなアプローチを取ります。

次の部分はリンクの問題と共用です。

(defvar-local my-org-in-html-fontify-code nil)

(defun my-org-html-fontify-code-advice (old-func &rest rest)
  (cl-letf (((default-value 'org-link-descriptive) nil) ;;リンクを展開表示する
            ((default-value 'my-org-in-html-fontify-code) t)) ;;org-html-fontify-codeの中にいることを示す
    (apply old-func rest)))

(advice-add 'org-html-fontify-code :around 'my-org-html-fontify-code-advice)

その上で org-modern-mode を起動する部分を次のようにします。

(defun my-org-modern-enable ()
  ;; エクスポート中(ソースコードブロックのfontify中)はorg-modern-modeを起動しない。
  (unless my-org-in-html-fontify-code
    (org-modern-mode)))

(add-hook 'org-mode-hook #'my-org-modern-enable)

つまり、色づけ処理に入るときにフラグを立てて、モードを立ち上げるときにそれを確認するわけです。

はい、いつも通り無理矢理ですね。

そもそも org-modern を常用している人って世界中にどれくらいいるんでしょうね。重くなるし編集しづらくなるし色々問題も起きるのでハッキリ言っておすすめはしません(笑)。