Skip to content

Commit

Permalink
util: replace builtin "readonly" with a shell function
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Mar 1, 2023
1 parent 70d88af commit 8683c84
Show file tree
Hide file tree
Showing 26 changed files with 975 additions and 761 deletions.
4 changes: 4 additions & 0 deletions README-ja_JP.md
Expand Up @@ -230,6 +230,10 @@ Vimモードの実装は2017年9月に始まり2018年3月に一先ず完成と
つまり、同名の異なるローカル変数さえ定義することができません。
この問題は `ble.sh` 固有の制限ではなく、あらゆる Bash の枠組みがグローバルの読み込み専用変数に影響を受けます。
一般的にグローバルスコープに読み込み変数を設定することはセキュリティ的な理由がない限りは非推奨と考えられています (参照 [[1]](https://lists.gnu.org/archive/html/bug-bash/2019-03/threads.html#00150), [[2]](https://lists.gnu.org/archive/html/bug-bash/2020-04/threads.html#00200), [[3]](https://mywiki.wooledge.org/BashProgramming?highlight=%28%22readonly%22%20flag,%20or%20an%20%22integer%22%20flag,%20but%20these%20are%20mostly%20useless,%20and%20serious%20scripts%20shouldn%27t%20be%20using%20them%29#Variables))。
また、`ble.sh` はビルトインコマンド `readonly` をシェル関数で置き換え、グローバル変数を読み込み専用にするのをブロックします。
例外として、全て大文字の変数 (`ble.sh` が内部使用するものを除く) および `_*` の形の変数 (`_ble_*` および `__ble_*` を除く) を読み込み専用にすることは可能です。
- `ble.sh` は Bash のビルトインコマンド (`trap`, `readonly`, `bind`, `history`, `read`, `exit`) をシェル関数で上書きし、`ble.sh` と干渉しないようにその振る舞いを調整します。
ユーザーまたは他の枠組みが元のビルトインを直接呼び出した場合、または `ble.sh` の定義したシェル関数を別のシェル関数で上書きした場合、正しい動作を保証できません。

# 1 使い方

Expand Down
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -245,6 +245,10 @@ For example,
This is not the problem specific to `ble.sh`, but any Bash framework may suffer from the global readonly variables.
It is generally not recommended to define global readonly variables in Bash except for the security reasoning
(Refs. [[1]](https://lists.gnu.org/archive/html/bug-bash/2019-03/threads.html#00150), [[2]](https://lists.gnu.org/archive/html/bug-bash/2020-04/threads.html#00200), [[3]](https://mywiki.wooledge.org/BashProgramming?highlight=%28%22readonly%22%20flag,%20or%20an%20%22integer%22%20flag,%20but%20these%20are%20mostly%20useless,%20and%20serious%20scripts%20shouldn%27t%20be%20using%20them%29#Variables)).
Also, `ble.sh` overrides the builtin `readonly` with a shell function to prevent it from making global variables readonly.
It allows only uppercase global variables and `_*` to become readonly except `_ble_*`, `__ble_*`, and some special uppercase variables.
- `ble.sh` overrides Bash's built-in commands (`trap`, `readonly`, `bind`, `history`, `read`, and `exit`) with shell functions to adjust the behavior of each built-in command and prevent them from interfering with `ble.sh`.
If the user or another framework directly calls the original builtins through `builtin BUILTIN`, or if the user or another framework replaces the shell functions, the behavior is undefined.

# 1 Usage

Expand Down
30 changes: 15 additions & 15 deletions archive/getopt-test.sh
Expand Up @@ -2,11 +2,11 @@

# # usage
#
# declare "${ble_getopt_locals[@]}"
# declare "${_ble_getopt_locals[@]}"
# ble/getopt.init "$0" "$@"
#
# while ble/getopt.next; do
# case "$OPTION" in
# case "$option" in
# (-a|--hoge)
# echo hoge ;;
# esac
Expand All @@ -20,49 +20,49 @@
source getopt.sh

function command1 {
builtin eval -- "$ble_getopt_prologue"
builtin eval -- "$_ble_getopt_prologue"
ble/getopt.init "$0" "$@"

while ble/getopt.next; do
case "$OPTION" in
case "$option" in
(-b|--bytes) ble/util/print bytes ;;
(-s|--spaces) ble/util/print spaces ;;
(-w|--width)
if ! ble/getopt.get-optarg; then
ble/getopt.print-argument-message "missing an option argument for $OPTION"
_opterror=1
ble/getopt.print-argument-message "missing an option argument for $option"
getopt_error=1
continue
fi
ble/util/print "width=$OPTARG" ;;
ble/util/print "width=$optarg" ;;
(--char-width|--tab-width|--indent-type)
if ! ble/getopt.get-optarg; then
ble/getopt.print-argument-message "missing an option argument for $OPTION"
_opterror=1
ble/getopt.print-argument-message "missing an option argument for $option"
getopt_error=1
continue
fi
ble/util/print "${OPTION#--} = $OPTARG" ;;
ble/util/print "${option#--} = $optarg" ;;
(--continue)
if ble/getopt.has-optarg; then
ble/getopt.get-optarg
ble/util/print "continue = $OPTARG"
ble/util/print "continue = $optarg"
else
ble/util/print "continue"
fi ;;
(-i|--indent)
if ble/getopt.has-optarg; then
ble/getopt.get-optarg
ble/util/print "indent = $OPTARG"
ble/util/print "indent = $optarg"
else
ble/util/print "indent"
fi ;;
(--text-justify|--no-text-justify)
ble/util/print "${OPTION#--}" ;;
ble/util/print "${option#--}" ;;
(-[^-]*|--?*)
ble/getopt.print-argument-message "unknown option."
_opterror=1 ;;
getopt_error=1 ;;
(*)
ble/getopt.print-argument-message "unknown argument."
_opterror=1 ;;
getopt_error=1 ;;
esac
done

Expand Down
80 changes: 41 additions & 39 deletions archive/getopt.sh
Expand Up @@ -5,10 +5,10 @@
## function myfunc {
## local flagVersion= flagHelp= flagEnd=
##
## builtin eval -- "$ble_getopt_prologue"
## builtin eval -- "$_ble_getopt_prologue"
## ble/getopt.init myfunc "$@"
## while ble/getopt.next; do
## case "$OPTION" in
## case "$option" in
##
## #
## # --version, --help (引数を取らないオプション)
Expand All @@ -22,11 +22,11 @@
## (-w|--width)
## if ble/getopt.get-optarg; then
## # 引数が見付かった場合
## process "$OPTARG"
## process "$optarg"
## else
## # 引数が見付からなかった場合
## ble/getopt.print-argument-message "missing an option argument for $OPTION"
## _opterror=1
## ble/getopt.print-argument-message "missing an option argument for $option"
## getopt_error=1
## fi
##
## #
Expand All @@ -37,7 +37,7 @@
## if ble/getopt.has-optarg; then
## # 形式 --continue=... (直接引数あり) の場合
## ble/getopt.get-optarg
## process "$OPTARG"
## process "$optarg"
## else
## # 形式 --continue (直接引数なし) の場合
## fi
Expand All @@ -47,14 +47,14 @@
## #
## (--)
## # 残りの引数を処理
## process "${@:OPTIND}"
## process "${@:optind}"
## break ;;
##
## (-*)
## ble/getopt.print-argument-message "unknown option."
## _opterror=1 ;;
## getopt_error=1 ;;
## (*)
## process "$OPTION" ;;
## process "$option" ;;
## esac
## done
##
Expand All @@ -73,37 +73,39 @@
## fi
## }

ble_getopt_locals=(_optargs _optchars _optarg _opterror OPTIND OPTION OPTARG)
ble_getopt_prologue='declare "${ble_getopt_locals[@]}"'
_ble_getopt_locals=(getopt_args getopt_chars getopt_arg getopt_error optind option optarg)
_ble_getopt_prologue='declare "${_ble_getopt_locals[@]}"'
function ble/getopt.init {
_optargs=("$@")
_optchars= _optarg= _opterror=
OPTIND=1 OPTION= OPTARG=
getopt_args=("$@")
getopt_chars=
getopt_arg=
getopt_error=
optind=1 option= optarg=
}
function ble/getopt.print-argument-message {
local IFS=$_ble_term_IFS
local index=$((OPTIND-1))
ble/util/print "${_optargs[0]##*/} (argument#$index \`${_optargs[index]}'): $*" >&2
local index=$((optind-1))
ble/util/print "${getopt_args[0]##*/} (argument#$index \`${getopt_args[index]}'): $*" >&2
}
function ble/getopt.print-message {
local IFS=$_ble_term_IFS
local index=$((OPTIND-1))
ble/util/print "${_optargs[0]##*/} (arguments): $*" >&2
local index=$((optind-1))
ble/util/print "${getopt_args[0]##*/} (arguments): $*" >&2
}

function ble/getopt.next {
ble/getopt/.check-optarg-cleared
if ((${#_optchars})); then
OPTION=-${_optchars::1}
_optchars=${_optchars:1}
elif ((OPTIND<${#_optargs[@]})); then
OPTION=${_optargs[OPTIND++]}
if [[ $OPTION == -[^-]* ]]; then
_optchars=${OPTION:2}
OPTION=${OPTION::2}
elif [[ $OPTION == --*=* ]]; then
_optarg==${OPTION#--*=}
OPTION=${OPTION%%=*}
if ((${#getopt_chars})); then
option=-${getopt_chars::1}
getopt_chars=${getopt_chars:1}
elif ((optind<${#getopt_args[@]})); then
option=${getopt_args[optind++]}
if [[ $option == -[^-]* ]]; then
getopt_chars=${option:2}
option=${option::2}
elif [[ $option == --*=* ]]; then
getopt_arg==${option#--*=}
option=${option%%=*}
fi
else
return 1
Expand All @@ -112,28 +114,28 @@ function ble/getopt.next {

# optarg
function ble/getopt.get-optarg {
if [[ $_optarg ]]; then
OPTARG=${_optarg:1}
_optarg=
elif ((OPTIND<${#_optargs[@]})); then
OPTARG=${_optargs[OPTIND++]}
if [[ $getopt_arg ]]; then
optarg=${getopt_arg:1}
getopt_arg=
elif ((optind<${#getopt_args[@]})); then
optarg=${getopt_args[optind++]}
else
return 1
fi
}
function ble/getopt.has-optarg {
[[ $_optarg ]]
[[ $getopt_arg ]]
}
function ble/getopt/.check-optarg-cleared {
if [[ $_optarg ]]; then
ble/getopt.print-argument-message "the option argument \`${_optarg:1}' is not processed ">&2
_opterror=1 _optarg=
if [[ $getopt_arg ]]; then
ble/getopt.print-argument-message "the option argument \`${getopt_arg:1}' is not processed ">&2
getopt_error=1 getopt_arg=
fi
}

function ble/getopt.finalize {
ble/getopt/.check-optarg-cleared

[[ ! $_opterror ]]
[[ ! $getopt_error ]]
}

0 comments on commit 8683c84

Please sign in to comment.