-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmap_pchunk
154 lines (138 loc) · 5.42 KB
/
map_pchunk
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#! .desc:
# Process each cumulative path segment with a function
#! .params:
# <"$1"> - path
# <["$2"]> - LTR mask
# <["$3"]> - RTL mask
# <$4> - function name
# ["$5"]+ - function arguments
#! .sets:
# <map_pchunk_> ();
#! .rc:
# (0) success
# (255) bad input / error
#! .rc.fn:
# ($4) (*) error
#! .caveats:
# > `INT_MAX`.
#! .desc.ext:
# The path mask(s), a mandatory argument that can be empty, specifies the
# exclusion of cumulative path segments that match exactly against the given
# direction of a mask. It is possible for a mask to match the entire path,
# hence resulting in a NOP.
#
# For a mask to apply, each segment it contains must sequentially match exactly
# against the path in the given direction of a mask. Consequently, the entire
# mask is discarded if its last segment, like any preceding ones, matches only
# partially. Multiple consecutive slashes between the segments are significant,
# while the matching logic remains unaffected by any leading/trailing path or
# mask slashes.
#
# For example, with a path `/foo/bar/baz` and an LTR mask `foo`, the function
# will receive only the cumulative path segments `/foo/bar` and `/foo/bar/baz`,
# respectively. With an RTL mask `bar/baz`, the function will receive only
# `/foo`. Note: Multiple consecutive slashes between the segments remain
# significant.
#
# If root (`/`) is solely given as the path, nothing will be processed and
# success will be returned, as there are no segments to process. Note that a
# semantically different specification of root, such as `/.`, will be
# processed.
#.
map_pchunk() {
map_pchunk_() { _pchunk="$2" && shift 2 && "$@" "$_pchunk"; }
[ "$#" -ge 4 ] || return 255
# Determine whether an existing LTR path mask applies. A valid LTR path
# mask shall not end with trailing slashes.
#
# If no valid LTR path mask is present, the mask is removed (emptied).
{
# Strip any trailing slashes in the path and save the result into $_a.
_a="$1"
_a="${_a%"${_a##*[!/]}"}"
# Strip any trailing slashes in the LTR mask, swap any leading slashes
# with the path's, and save the result into $_b. The order of operation
# matters.
_b="$2"
_b="${_b%"${_b##*[!/]}"}"
_b="${1%%[!/]*}${_b#"${_b%%[!/]*}"}"
# Assert whether $_b (the mask) sequentially matches exactly against
# $_a (the path) left-to-right (LTR).
case "$_a/" in
"$_b/"*) # The mask matches.
# Set the valid asserted LTR mask.
_path="$1"; _ltr_mask="$_b"; shift 2
set -- "$_path" "$_ltr_mask" "$@"
;;
*) # The mask does not match.
# Remove (empty) the mask.
_path="$1"; _ltr_mask=; shift 2
set -- "$_path" "$_ltr_mask" "$@"
;;
esac
}
# Determine whether an existing RTL path mask applies. A valid RTL path
# mask shall begin with leading slashes. A valid RTL path mask strips the
# path RTL and discards the mask.
#
# If no valid RTL path mask is present, the mask is simply discarded.
{
# Strip any leading slashes in the path and save the result into $_a.
_a="$1"
_a="${_a#"${_a%%[!/]*}"}"
# Strip any leading slashes in the RTL mask, swap any trailing slashes
# with the path's, and save the result into $_b. The order of operation
# matters.
_b="$3"
_b="${_b#"${_b%%[!/]*}"}"
_b="${_b%"${_b##*[!/]}"}${1##*[!/]}"
# Assert whether $_b (the mask) sequentially matches exactly against
# $_a (the path) right-to-left (RTL).
case "/$_a" in
*"/$_b") # The mask matches.
# Extract any leading slashes of the first path mask segment
# into $_a.
_a="$1"
_a="${_a%"$_b"}"
_a="${_a##*[!/]}"
# Strip the path RTL and discard the mask.
_path="$1"; _ltr_mask="$2"; shift 3
set -- "${_path%"$_a$_b"}" "$_ltr_mask" "$@"
;;
*) # The mask does not match.
# Discard the mask.
_path="$1"; _ltr_mask="$2"; shift 3
set -- "$_path" "$_ltr_mask" "$@"
;;
esac
}
# Process the path using parameter expansion and the custom concept of an
# LTR path mask.
#
# For more information, refer to:
# > "POSIX.1-2024, Volume: Shell & Utilities, Section: Shell Command
# Language, Subsection: Parameter Expansion".
while :; do
# Strip the path LTR using the path mask and save the result into $_a.
_a="${1#"$2"}"
# Extract the leading slash(es) into $_b.
_b="${_a%%[!/]*}"
# Extract the first LTR path segment into $_c.
_c="${_a#"$_b"}"
_c="${_c%%/*}"
# Break successfully if no path segment is present.
[ "$_c" ] || break
# To properly handle the last path segment, set the whole path as the
# path mask.
if [ "$2$_b$_c" = "${1%"${1##*[!/]}"}" ]; then
_path="$1"; _ltr_mask="$1"; _b=; _c=; shift 2
set -- "$_path" "$_ltr_mask" "$@"
fi
# Append the extracted slash(es) and the path segment to the path mask.
_path="$1"; _ltr_mask="$2$_b$_c"; shift 2
set -- "$_path" "$_ltr_mask" "$@"
# Process the path chunk (path mask).
map_pchunk_ "$@" || return "$?"
done
return 0
}