-
Notifications
You must be signed in to change notification settings - Fork 82
Expand file tree
/
Copy pathargs.sh
More file actions
133 lines (120 loc) · 3.19 KB
/
args.sh
File metadata and controls
133 lines (120 loc) · 3.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env bash
# Shared argument parser for gtr commands
# Depends on: ui.sh (log_error, show_command_help)
#
# Usage:
# parse_args "<spec>" "$@"
#
# Spec format (one flag per line):
# --force # boolean flag → _arg_force=1
# --from: value # value flag → _arg_from="<val>"
# --dry-run|-n # aliases → _arg_dry_run=1
# --editor|-e # short alias → _arg_editor=1
#
# Output variables:
# _pa_positional[] — positional arguments (array)
# _pa_passthrough[] — arguments after -- (array)
# _arg_<name> — one per declared flag (hyphens → underscores)
declare -a _pa_positional _pa_passthrough
# Try to match a flag against the spec. Sets _arg_<name> and _pa_shift_count.
# Returns 0 if matched, 1 if no match.
_pa_match_flag() {
local spec="$1" flag="$2" next_val="${3:-}"
_pa_shift_count=0
local line
while IFS= read -r line; do
[ -z "$line" ] && continue
# Check if spec line ends with ": value"
local has_value=0
case "$line" in
*": value")
has_value=1
line="${line%: value}"
;;
esac
# Check if $flag matches any alternative in the pattern
local alt matched=0
local IFS="|"
for alt in $line; do
if [ "$flag" = "$alt" ]; then
matched=1
break
fi
done
if [ "$matched" = "1" ]; then
# Derive variable name from the first (canonical) pattern
local canonical="${line%%|*}"
canonical="${canonical#--}"
canonical="${canonical#-}"
canonical="${canonical//-/_}"
if [ "$has_value" = "1" ]; then
if [ -z "$next_val" ]; then
log_error "$flag requires a value"
exit 1
fi
eval "_arg_${canonical}=\"\$next_val\""
_pa_shift_count=2
else
eval "_arg_${canonical}=1"
_pa_shift_count=1
fi
return 0
fi
done <<EOF
$spec
EOF
return 1
}
# Main parser. Call from command functions as: parse_args "<spec>" "$@"
parse_args() {
local _pa_spec="$1"
shift
_pa_positional=()
_pa_passthrough=()
# Reset all _arg_ variables from the spec to empty
local _pa_line
while IFS= read -r _pa_line; do
[ -z "$_pa_line" ] && continue
local _pa_clean="${_pa_line%: value}"
local _pa_canonical="${_pa_clean%%|*}"
_pa_canonical="${_pa_canonical#--}"
_pa_canonical="${_pa_canonical#-}"
_pa_canonical="${_pa_canonical//-/_}"
eval "_arg_${_pa_canonical}=''"
done <<EOF
$_pa_spec
EOF
while [ $# -gt 0 ]; do
case "$1" in
-h|--help)
show_command_help
;;
--)
shift
_pa_passthrough=("$@")
break
;;
-*)
if _pa_match_flag "$_pa_spec" "$@"; then
shift "$_pa_shift_count"
else
log_error "Unknown flag: $1"
exit 1
fi
;;
*)
_pa_positional+=("$1")
shift
;;
esac
done
}
# Validate that at least N positional arguments were provided.
# Call after parse_args. Exits with error message if insufficient args.
# Usage: require_args <min_count> "<usage message>"
require_args() {
if [ "${#_pa_positional[@]}" -lt "$1" ]; then
log_error "$2"
exit 1
fi
}