Skip to content

Commit

Permalink
main (ble/util/readlink): work around non-standard or missing "readlink"
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Dec 30, 2021
1 parent 92d9734 commit a41279e
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 39 deletions.
141 changes: 112 additions & 29 deletions ble.pp
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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; }
Expand Down
1 change: 1 addition & 0 deletions docs/ChangeLog.md
Expand Up @@ -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

Expand Down
35 changes: 34 additions & 1 deletion lib/test-main.sh
Expand Up @@ -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}
(
Expand Down Expand Up @@ -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
Expand All @@ -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
)

Expand Down
34 changes: 31 additions & 3 deletions note.txt
Expand Up @@ -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 文脈) の補完
Expand Down Expand Up @@ -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]
Expand Down
16 changes: 10 additions & 6 deletions src/util.sh
Expand Up @@ -2815,29 +2815,33 @@ 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
((index++))
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]}
Expand Down

0 comments on commit a41279e

Please sign in to comment.