diff --git a/ble.pp b/ble.pp index 4b564c3f..a65f20db 100644 --- a/ble.pp +++ b/ble.pp @@ -628,11 +628,11 @@ function ble/bin/.freeze-utility-path { } if ((_ble_bash>=40000)); then - function ble/bin#has { type "$@" &>/dev/null; } + function ble/bin#has { type -t "$@" &>/dev/null; } else function ble/bin#has { local cmd - for cmd; do type "$cmd" || return 1; done &>/dev/null + for cmd; do type -t "$cmd" || return 1; done &>/dev/null return 0 } fi @@ -749,37 +749,112 @@ function ble/bin/awk.supports-null-record-separator { } #------------------------------------------------------------------------------ +# readlink -f (Originally taken from akinomyoga/mshex.git) -# readlink -f (taken from akinomyoga/mshex.git) ## @fn ble/util/readlink path ## @var[out] ret -function ble/util/readlink { - ret= - local path=$1 - case "$OSTYPE" in - (cygwin|msys|linux-gnu) - # 少なくとも cygwin, GNU/Linux では readlink -f が使える - ble/util/assign ret 'PATH=/bin:/usr/bin readlink -f "$path"' ;; - (darwin*|*) - # Mac OSX には readlink -f がない。 - local PWD=$PWD OLDPWD=$OLDPWD - while [[ -h $path ]]; do - local link; ble/util/assign link 'PATH=/bin:/usr/bin readlink "$path" 2>/dev/null || true' - [[ $link ]] || break - - if [[ $link = /* || $path != */* ]]; then - # * $link ~ 絶対パス の時 - # * $link ~ 相対パス かつ ( $path が現在のディレクトリにある ) の時 - path=$link - else - local dir=${path%/*} - path=${dir%/}/$link - fi + +if ((_ble_bash>=40000)); then + _ble_util_readlink_visited_init='local -A visited=()' + function ble/util/readlink/.visited { + [[ ${visited[$1]+set} ]] && return 0 + visited[$1]=1 + return 1 + } +else + _ble_util_readlink_visited_init="local -a visited=()" + function ble/util/readlink/.visited { + local key + for key in "${visited[@]}"; do + [[ $1 == "$key" ]] && return 0 done - ret=$path ;; + visited=("$1" "${visited[@]}") + return 1 + } +fi + +## @fn ble/util/readlink/.readlink path +## @var[out] link +function ble/util/readlink/.readlink { + local path=$1 + if ble/bin#has ble/bin/readlink; then + ble/util/assign link 'ble/bin/readlink -- "$path"' + [[ $link ]] + elif ble/bin#has ble/bin/ls; then + ble/util/assign link 'ble/bin/ls -ld -- "$path"' && + [[ $link == *" $path -> "?* ]] && + link=${link#*" $path -> "} + else + false + fi +} 2>/dev/null +## @fn ble/util/readlink/.resolve-physical-directory +## @var[in,out] path +function ble/util/readlink/.resolve-physical-directory { + [[ $path == */?* ]] || return 0 + local PWD=$PWD OLDPWD=$OLDPWD CDPATH= + builtin cd -L . && + local pwd=$PWD && + builtin cd -P "${path%/*}/" && + path=${PWD%/}/${path##*/} + builtin cd -L "$pwd" + return 0 +} +function ble/util/readlink/.resolve-loop { + local path=$ret + builtin eval -- "$_ble_util_readlink_visited_init" + while [[ -h $path ]]; do + local link + ble/util/readlink/.visited "$path" && break + ble/util/readlink/.readlink "$path" || break + if [[ $link == /* || $path != */* ]]; then + path=$link + else + # 相対パス ../ は物理ディレクトリ構造に従って遡る。 + ble/util/readlink/.resolve-physical-directory + path=${path%/}/$link + fi + while [[ $path == ?*/ ]]; do path=${path%/}; done + done + ret=$path +} +function ble/util/readlink/.resolve { + # 初回呼び出し時に実装を選択 + _ble_util_readlink_type= + + # より効率的な実装が可能な場合は ble/util/readlink/.resolve を独自定義。 + case $OSTYPE in + (cygwin | msys | linux-gnu) + # これらのシステムの標準 readlink では readlink -f が使える。 + # + # Note: 例えば NixOS では標準の readlink を使おうとすると問題が起こるらしい + # ので、見えている readlink を使う。見えている readlink が非標準の時は -f + # が使えるか分からないので readlink -f による実装は有効化しない。 + # + local readlink + ble/util/assign readlink 'type -P readlink' + case $readlink in + (/bin/readlink | /usr/bin/readlink) + _ble_util_readlink_type=readlink-f + builtin eval "function ble/util/readlink/.resolve { ble/util/assign ret '$readlink -f -- \"\$ret\"'; }" ;; + esac ;; esac + + if [[ ! $_ble_util_readlink_type ]]; then + _ble_util_readlink_type=loop + ble/bin/.freeze-utility-path readlink ls + function ble/util/readlink/.resolve { ble/util/readlink/.resolve-loop; } + fi + + ble/util/readlink/.resolve +} +function ble/util/readlink { + ret=$1 + if [[ -h $ret ]]; then ble/util/readlink/.resolve; fi } +#--------------------------------------- + _ble_bash_path= function ble/bin/.load-builtin { local name=$1 path=$2 @@ -1580,9 +1655,17 @@ function ble/base/sub:test { echo "BLE_VERSION: $BLE_VERSION" fi echo "BASH_VERSION: $BASH_VERSION" - source "$_ble_base"/lib/test-main.sh || error=1 - source "$_ble_base"/lib/test-util.sh || error=1 - source "$_ble_base"/lib/test-canvas.sh || error=1 + (($#)) || set -- main util canvas + local section + for section; do + local file=$_ble_base/lib/test-$section.sh + if [[ -f $file ]]; then + source "$file" || error=1 + else + ble/util/print "ERROR: Test '$section' is not defined." + error=1 + fi + done [[ ! $error ]] } function ble/base/sub:update { ble-update; } diff --git a/docs/ChangeLog.md b/docs/ChangeLog.md index 32f25dc8..36be2f06 100644 --- a/docs/ChangeLog.md +++ b/docs/ChangeLog.md @@ -270,6 +270,7 @@ - decode (`ble/builtin/bind`): improve compatibility of the deprecated form `bind key:rlfunc` (motivated by cmplstofB) `#D1698` b6fc4f0 - complete: work around a false warning messages of gawk-4.0.2 `#D1709` 9771693 - main: work around `XDG_RUNTIME_DIR` of a different user by `su` (reported by zim0369) `#D1712` 8d37048 +- main (`ble/util/readlink`): work around non-standard or missing `readlink` (motivated by peterzky) `#D1720` 0000000 ## Internal changes and fixes diff --git a/lib/test-main.sh b/lib/test-main.sh index 185703c3..4de81040 100644 --- a/lib/test-main.sh +++ b/lib/test-main.sh @@ -2,7 +2,7 @@ ble-import lib/core-test -ble/test/start-section 'main' 16 +ble/test/start-section 'main' 19 # ble/util/{put,print} ( @@ -30,6 +30,18 @@ ble/test/start-section 'main' 16 # ble/util/readlink ( + ble/bin/.freeze-utility-path readlink ls + function ble/test:readlink.impl1 { + ret=$1 + ble/util/readlink/.resolve-loop + } + function ble/test:readlink.impl2 { + ret=$1 + ble/function#push ble/bin/readlink + ble/util/readlink/.resolve-loop + ble/function#pop ble/bin/readlink + } + ble/test/chdir mkdir -p ab/cd/ef @@ -42,6 +54,27 @@ ble/test/start-section 'main' 16 [[ $ret != /* ]] && ret=${PWD%/}/$ret' \ ret="${PWD%/}/ab/cd/ef/file.txt" + # loop symbolic links + ln -s loop1.sh loop0.sh + ln -s loop2.sh loop1.sh + ln -s loop3.sh loop2.sh + ln -s loop1.sh loop3.sh + for impl in impl1 impl2; do + ble/test "ble/test:readlink.$impl loop0.sh" ret='loop1.sh' + done + + # resolve physical directory + mkdir -p phys.dir + touch phys.dir/1.txt + ln -s ../../../phys.dir ab/cd/ef/phys.link + ln -s ab/cd/ef/phys.link phys.link + local pwd=$PWD xpath= + ble/test code:' + path=phys.link/1.txt + ble/util/readlink/.resolve-physical-directory + declare -p path PWD >&2 + [[ $path == */phys.dir/1.txt && $PWD == "$pwd" ]]' + ble/test/rmdir ) diff --git a/note.txt b/note.txt index efd02b24..5fb9f3c3 100644 --- a/note.txt +++ b/note.txt @@ -1727,9 +1727,6 @@ bash_tips * PROMPT_COMMAND / trap DEBUG で問題が起こる? (found by rashad-moves) https://github.com/rashad-moves/HomeConfigurationFiles/commit/efbac4153fd5021f1bc00d42c618fd9d6f4090b9 - * NixOS で皆が readlink 周りに修正を入れている - https://github.com/peterzky/peterzky-overlay/commit/7b98f05e9b8f84f2d43d84db6b2d76c8e93a38df#diff-34e5f3d20be258f6630e6113d3e1409be74cae463b58eb52b5ebe493e9ee2309R20 - * complete (source:rhs): 変数名依存の補完に対応しても良いのでは。 * complete: ARGEX (eval 文脈) の補完 @@ -5726,6 +5723,37 @@ bash_tips Done (実装ログ) ------------------------------------------------------------------------------- +2021-12-30 + + * readlink 対策: NixOS で皆が readlink 周りに修正を入れている [#D1720] + https://github.com/peterzky/peterzky-overlay/commit/7b98f05e9b8f84f2d43d84db6b2d76c8e93a38df#diff-34e5f3d20be258f6630e6113d3e1409be74cae463b58eb52b5ebe493e9ee2309R20 + + 今迄は /usr/bin:/bin にある readlink しか信用しない事にしていたが、取り敢え + ず readlink が存在さえしていれば単一のパスの読み取りには使えると想定する事 + にする。-f が使えるかどうかの判定に /usr/bin/readlink または/bin/readlink + を使う事にする。 + + readlink が存在しない場合にどうするかについて。 + + https://qiita.com/ko1nksm/items/873cfb9c6ceb6ef32ec9 + + このページを見ると ls -l を用いて link を読み取り、cd -P を用いてパスの解決 + を行っている。うーん。cd -P を実行した時にディレクトリが戻らなくなるのが心 + 配だが、まあその様な事は基本的に起こらないと想定して良いだろうか。 + + 元々の実装では別に途中のディレクトリ名の解決はしなくても良いという態度だっ + たが使い方によっては問題になるかもしれないと心配になって来たので、やはり cd + -P を用いて解決する事にした。 + + * readlink -f を使わない実装について簡単なテストを書いてみたが取り敢えずは + 動いている様子である。 + + というか寧ろ readlink -f を用いて失敗した時にどう振る舞うのかというのが心 + 配な気もする。 + + x done: 解決対象のファイル名が - で始まる時に変な事が起こるのではないか? → + これについては修正した。 + 2021-12-21 * complete (action:mandb): brace の後は space ではなくて , にするべき [#D1719] diff --git a/src/util.sh b/src/util.sh index 8bdbfc83..2d5bdb9c 100644 --- a/src/util.sh +++ b/src/util.sh @@ -2815,15 +2815,13 @@ function ble/function#push { if [[ $proc ]]; then local q=\' Q="'\''" builtin eval "function $name { builtin eval -- '${proc//$q/$Q}'; }" + else + builtin unset -f "$name" fi return 0 } function ble/function#pop { local name=$1 proc=$2 - if ! ble/is-function "$name"; then - ble/util/print "ble/function#push: $name is not a function." >&2 - return 1 - fi local index=-1 while ble/is-function "ble/function#push/$((index+1)):$name"; do @@ -2831,13 +2829,19 @@ function ble/function#pop { done if ((index<0)); then - builtin unset -f "$name" + if ble/is-function "$name"; then + builtin unset -f "$name" + return 0 + else + ble/util/print "ble/function#push: $name is not a function." >&2 + return 1 + fi else local def; ble/function#getdef "ble/function#push/$index:$name" builtin eval -- "${def#*:}" builtin unset -f "ble/function#push/$index:$name" + return 0 fi - return 0 } function ble/function#push/call-top { local func=${FUNCNAME[1]}