From b632b56ef19cb5e0a6683a141cde9396e6024c79 Mon Sep 17 00:00:00 2001 From: Kamil Cukrowski Date: Fri, 30 Jan 2026 19:34:50 +0100 Subject: [PATCH 1/6] L_getopts_in: handle -n2+ and better parse --- bin/L_lib.sh | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/bin/L_lib.sh b/bin/L_lib.sh index c54198e..eac6a3e 100755 --- a/bin/L_lib.sh +++ b/bin/L_lib.sh @@ -967,38 +967,38 @@ L_getopts_in() { ${_L_es[@]:+printf} ${_L_es[@]:+-v_L_tmp} ${_L_es[@]:+"%d"} ${_L_es[@]:+"'$_L_opt"} ${_L_es[@]:+${_L_es[_L_tmp]:+eval}} ${_L_es[@]:+${_L_es[_L_tmp]:+"${_L_es[_L_tmp]}"}} # Parse the command. - if [[ "$_L_spec" == *"$_L_opt::"* ]]; then - L_array_append "$_L_prefix$_L_opt" "$OPTARG" - elif [[ "$_L_spec" == *"$_L_opt:"* ]]; then - "${_L_local[@]}" "$_L_prefix$_L_opt=$OPTARG" - elif [[ "$_L_spec" == *"$_L_opt"* ]]; then - printf -v "$_L_prefix$_L_opt" "%s" "$(( ${_L_prefix}${_L_opt} + 1 ))" - elif [[ "$_L_spec" == h ]]; then - L_func_usage "$_L_up" - return 0 - else - L_func_usage_error "$_L_up" - return 2 - fi + case "$_L_spec" in + *"$_L_opt::"*) L_array_append "$_L_prefix$_L_opt" "$OPTARG" ;; + *"$_L_opt:"*) "${_L_local[@]}" "$_L_prefix$_L_opt=$OPTARG" ;; + *"$_L_opt"*) printf -v "$_L_prefix$_L_opt" "%s" "$(( ${_L_prefix}${_L_opt} + 1 ))" ;; + h) L_func_usage "$_L_up"; return 0 ;; + *) L_func_usage_error "$_L_up"; return 2 ;; + esac done shift "$((OPTIND-1))" # case "$_L_nargs" in '*') ;; '?') - if (($# > 1)); then + if (( $# > 1 )); then L_func_usage_error "Wrong number of arguments. At most 1 argument expected but received $#" "$_L_up" return 2 fi ;; '+') - if (($# == 0)); then + if (( $# == 0 )); then L_func_usage_error "Missing positional argument" "$_L_up" return 2 fi ;; + [0-9]*'+') + if (( $# < ${_L_nargs%%+} )); then + L_func_usage_error "Wrong number of arguments. Expected at least ${_L_nargs%%+} but received $#" "$_L_up" + return 2 + fi + ;; [0-9]*) - if (($# != _L_nargs)); then + if (( $# != _L_nargs )); then L_func_usage_error "Wrong number of arguments. Expected $_L_nargs but received $#" "$_L_up" return 2 fi @@ -1007,7 +1007,7 @@ L_getopts_in() { esac # # L_array_assign "${_L_prefix}args" "$@" - if ((_L_eval)); then + if (( _L_eval )); then eval "$_L_cmd \"\$@\"" else "$_L_cmd" "$@" From 139a12f68d507a558ad84e2e0eabf0168971ef55 Mon Sep 17 00:00:00 2001 From: Kamil Cukrowski Date: Fri, 30 Jan 2026 19:35:26 +0100 Subject: [PATCH 2/6] L_handle_v_*: handle -vL_v properly --- bin/L_lib.sh | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/bin/L_lib.sh b/bin/L_lib.sh index eac6a3e..7fe51a6 100755 --- a/bin/L_lib.sh +++ b/bin/L_lib.sh @@ -1386,6 +1386,13 @@ else # L_HAS_NAMEREF L_handle_v_scalar() { case "${1:-}" in + -vL_v) + if [[ "${2:-}" == -- ]]; then + "${FUNCNAME[1]}"_v "${@:3}" + else + "${FUNCNAME[1]}"_v "${@:2}" + fi + ;; -v?*) local -n L_v="${1##-v}" || return 2 if [[ "${2:-}" == -- ]]; then @@ -1395,7 +1402,9 @@ else # L_HAS_NAMEREF fi ;; -v) - local -n L_v="$2" || return 2 + if [[ "$2" != L_v ]]; then + local -n L_v="$2" || return 2 + fi if [[ "${3:-}" == -- ]]; then "${FUNCNAME[1]}"_v "${@:4}" else @@ -1427,6 +1436,13 @@ else # L_HAS_NAMEREF L_handle_v_array() { case "${1:-}" in + -vL_v) + if [[ "${2:-}" == -- ]]; then + "${FUNCNAME[1]}"_v "${@:3}" + else + "${FUNCNAME[1]}"_v "${@:2}" + fi + ;; -v?*) local -n L_v="${1##-v}" || return 2 if [[ "${2:-}" == -- ]]; then @@ -1436,7 +1452,9 @@ else # L_HAS_NAMEREF fi ;; -v) - local -n L_v="$2" || return 2 + if [[ "$2" != L_v ]]; then + local -n L_v="$2" || return 2 + fi if [[ "${3:-}" == -- ]]; then "${FUNCNAME[1]}"_v "${@:4}" else From e22d024b0090f456ceef5f9d6353d713af8b7a82 Mon Sep 17 00:00:00 2001 From: Kamil Cukrowski Date: Fri, 30 Jan 2026 19:35:44 +0100 Subject: [PATCH 3/6] add L_array_keys and refactory L_array_contains L_args_contain --- bin/L_lib.sh | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/bin/L_lib.sh b/bin/L_lib.sh index 7fe51a6..329ca24 100755 --- a/bin/L_lib.sh +++ b/bin/L_lib.sh @@ -3381,10 +3381,18 @@ L_json_create_v() { # @example L_array_len arr L_array_len() { L_handle_v_scalar "$@"; } +# @description Get array keys +# @option -v Store the output in variable instead of printing it. +# @option -h Print this help and return 0. +# @arg $1 array nameref # @arg $@ elements to set @@ -3439,6 +3447,7 @@ L_array_copy() { else # L_HAS_NAMEREF L_array_len_v() { L_is_valid_variable_name "$1" && eval "L_v=\${#$1[@]}"; } + L_array_keys_v() { L_is_valid_variable_anme "$1" && eval "L_v=(\"\${!$1[@]}\")"; } L_array_assign() { L_is_valid_variable_name "$1" && eval "$1=(\"\${@:2}\")"; } L_array_set() { L_is_valid_variable_name "$1" && eval "$1[\"\$2\"]=\"\$3\""; } L_array_append() { L_is_valid_variable_name "$1" && eval "$1+=(\"\${@:2}\")"; } @@ -3597,9 +3606,20 @@ L_array_pipe() { # arr=("Hello" "World") # L_array_contains arr "Hello" # echo $? # prints 0 +# @see L_args_contain L_array_contains() { - local _L_arr="$1[@]" - L_args_contain "$2" ${!_L_arr:+"${!_L_arr}"} + local _L_arr="$1[*]" IFS=$'\x1D' + if [[ "${!_L_arr:+${!_L_arr//"$IFS"}}" == "${!_L_arr:+${!_L_arr}}" ]]; then + [[ "$IFS${!_L_arr:+${!_L_arr}}$IFS" == *"$IFS$2$IFS"* ]] + else + local _L_arr="$1[@]" i + for i in "${!_L_arr}"; do + if [[ "$i" == "$2" ]]; then + return 0 + fi + done + return 1 + fi } # @description Remove elements from array for which expression evaluates to failure. @@ -3629,17 +3649,16 @@ L_array_filter_eval() { # @arg $2 element to find L_array_index() { L_handle_v_scalar "$@"; } L_array_index_v() { - local _L_i="$1[@]" - (( ${!_L_i:+1}+0 )) && { - eval "local _L_i=(\"\${!$1[@]}\")" - for L_v in "${_L_i[@]}"; do - _L_i="$1[$L_v]" - if [[ "$2" == "${!_L_i}" ]]; then - return 0 - fi - done - return 1 - } + local _L_i _L_k + L_array_keys_v "$1" || return 2 + for _L_k in ${L_v[@]+"${L_v[@]}"}; do + _L_i="$1[$_L_k]" + if [[ "$2" == "${!_L_i}" ]]; then + L_v=$_L_k + return 0 + fi + done + return 1 } # @description Join array elements separated with the second argument. From 237cbec4a74a868a5438d77ad439336c308263a5 Mon Sep 17 00:00:00 2001 From: Kamil Cukrowski Date: Fri, 30 Jan 2026 19:35:50 +0100 Subject: [PATCH 4/6] ad more tests --- AGENTS.md | 5 ++ GEMINI.md | 1 + tests/array_index_tests.sh | 109 +++++++++++++++++++++++++++++++++++++ tests/test.sh | 2 + 4 files changed, 117 insertions(+) create mode 120000 GEMINI.md create mode 100644 tests/array_index_tests.sh diff --git a/AGENTS.md b/AGENTS.md index 2448c2c..af027b4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -65,3 +65,8 @@ The project adheres to strict conventions to maintain consistency and readabilit * `124`: Timeout. * **Shell Options:** Scripts and the library itself operate with `set -euo pipefail` to ensure robust error handling and predictable behavior. * **Testing Practices:** Unit tests are organized into functions prefixed with `_L_test_` within `tests/test.sh` and are executed by `L_unittest_main`. + +## Agent Interaction Rules + +* **Task Management:** Always first create a todo list with a todo write tools and then execute it. +* **Communication Style:** Be as concise and short as possible in communications, sacrificing grammatical structure and correctness. diff --git a/GEMINI.md b/GEMINI.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/tests/array_index_tests.sh b/tests/array_index_tests.sh new file mode 100644 index 0000000..4713757 --- /dev/null +++ b/tests/array_index_tests.sh @@ -0,0 +1,109 @@ +_L_test_array_index() { + # Test indexed array + local -a arr=(1 2 3) + local idx + L_array_index -v idx arr 1 + L_unittest_eq "$idx" "0" + + L_array_index -v idx arr 2 + L_unittest_eq "$idx" "1" + + L_array_index -v idx arr 3 + L_unittest_eq "$idx" "2" + + # Test failure + +arr=(1 2 3) + if L_array_index arr 4; then + echo "L_array_index should have failed" + return 1 + fi + + # Test associative array + if (( L_HAS_ASSOCIATIVE_ARRAY )); then + local -A assoc=([one]=1 [two]=2 [three]=3) + local key + L_array_index -v key assoc 2 + L_unittest_eq "$key" "two" + fi +} + +_L_test_array_keys() { + local keys + + # Empty array + local -a empty_arr=() + L_array_keys -v keys empty_arr + L_unittest_eq "${#keys[@]}" "0" + + # Normal array + local -a arr=(a b c) + L_array_keys -v keys arr + L_unittest_eq "${#keys[@]}" "3" + # keys should be 0 1 2 + L_unittest_eq "${keys[*]}" "0 1 2" + + if (( L_HAS_ASSOCIATIVE_ARRAY )); then + # Empty associative array + local -A empty_assoc=() + L_array_keys -v keys empty_assoc + L_unittest_eq "${#keys[@]}" "0" + + # Associative array + local -A assoc=([a]=1 [b]=2) + L_array_keys -v keys assoc + L_unittest_eq "${#keys[@]}" "2" + + # Sort keys for consistent comparison + local sorted_keys + # shellcheck disable=SC2207 + sorted_keys=($(printf "%s\n" "${keys[@]}" | sort)) + L_unittest_eq "${sorted_keys[*]}" "a b" + fi +} + +_L_test_args_index() { + local idx + L_args_index -v idx "World" "Hello" "World" + L_unittest_eq "$idx" "1" + + L_args_index -v idx "Hello" "Hello" "World" + L_unittest_eq "$idx" "0" + + if L_args_index -v idx "Missing" "Hello" "World"; then + echo "L_args_index should have failed" + return 1 + fi +} + +_L_test_args() { + # L_args_contain tests + L_unittest_checkexit 0 L_args_contain 1 0 1 2 + L_unittest_checkexit 0 L_args_contain 1 2 1 + L_unittest_checkexit 0 L_args_contain 1 1 0 + L_unittest_checkexit 0 L_args_contain 1 1 + L_unittest_checkexit 1 L_args_contain 0 1 + L_unittest_checkexit 1 L_args_contain 0 +} + +_L_test_array_contains() { + local arr + +arr=(0 1 2) + L_unittest_checkexit 0 L_array_contains arr 1 + +arr=(2 1) + L_unittest_checkexit 0 L_array_contains arr 1 + +arr=(1 0) + L_unittest_checkexit 0 L_array_contains arr 1 + +arr=(1) + L_unittest_checkexit 0 L_array_contains arr 1 + +arr=(1) + L_unittest_checkexit 1 L_array_contains arr 0 + +arr=() + L_unittest_checkexit 1 L_array_contains arr 0 +} diff --git a/tests/test.sh b/tests/test.sh index 5db6420..ac4c3ad 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -22,6 +22,8 @@ USR2_CNT=0 ############################################################################### +. "$L_DIR"/array_index_tests.sh + _L_test_color() { { L_color_enable From 216702a24c99022b6cc9c64ddf7cf9edfc332fdb Mon Sep 17 00:00:00 2001 From: Kamil Cukrowski Date: Fri, 6 Feb 2026 12:07:49 +0100 Subject: [PATCH 5/6] fix typo --- bin/L_lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/L_lib.sh b/bin/L_lib.sh index 329ca24..41cd55d 100755 --- a/bin/L_lib.sh +++ b/bin/L_lib.sh @@ -3447,7 +3447,7 @@ L_array_copy() { else # L_HAS_NAMEREF L_array_len_v() { L_is_valid_variable_name "$1" && eval "L_v=\${#$1[@]}"; } - L_array_keys_v() { L_is_valid_variable_anme "$1" && eval "L_v=(\"\${!$1[@]}\")"; } + L_array_keys_v() { L_is_valid_variable_name "$1" && eval "L_v=(\"\${!$1[@]}\")"; } L_array_assign() { L_is_valid_variable_name "$1" && eval "$1=(\"\${@:2}\")"; } L_array_set() { L_is_valid_variable_name "$1" && eval "$1[\"\$2\"]=\"\$3\""; } L_array_append() { L_is_valid_variable_name "$1" && eval "$1+=(\"\${@:2}\")"; } From db347c62938d81adfb485adce9807a6525185e97 Mon Sep 17 00:00:00 2001 From: Kamil Cukrowski Date: Fri, 6 Feb 2026 12:28:53 +0100 Subject: [PATCH 6/6] fix tests --- bin/L_lib.sh | 4 ++-- tests/array_index_tests.sh | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/L_lib.sh b/bin/L_lib.sh index 41cd55d..53d10dc 100755 --- a/bin/L_lib.sh +++ b/bin/L_lib.sh @@ -3385,7 +3385,7 @@ L_array_len() { L_handle_v_scalar "$@"; } # @option -v Store the output in variable instead of printing it. # @option -h Print this help and return 0. # @arg $1