xref: /freebsd/libexec/rc/hooks.sh (revision 5c4d1c85847d84bd86e6b12408f9b5f846094f39)
1:
2# NAME:
3#	hooks.sh - provide hooks for customization
4#
5# SYNOPSIS:
6#	hooks_add_all HOOKS [--first] func [...]
7#	hooks_add_once HOOKS [--first] func [...]
8#	hooks_add_default_set {all,once}
9#	hooks_add HOOKS func [...]
10#	hooks_get [--lifo] HOOKS
11#	hooks_run [--lifo] HOOKS ["args"]
12#	hooks_run_all [--lifo] HOOKS ["args"]
13#	hooks_has HOOKS func
14#
15#	add_hooks HOOKS [--first] func [...]
16#	run_hooks HOOKS [LIFO] ["args"]
17#	run_hooks_all HOOKS [LIFO] ["args"]
18#
19# DESCRIPTION:
20#	The functions add_hooks and run_hooks are retained for
21#	backwards compatibility.  They are aliases for hooks_add and
22#	hooks_run.
23#
24#	hooks_add_all simply adds the "func"s to the list "HOOKS".
25#
26#	If the first arg is '--first' "func"s are added to the start
27#	of the list.
28#
29#	hooks_add_once does the same but only if "func" is not in "HOOKS".
30#	hooks_add uses one of the above based on "option", '--all' (default)
31#	or '--once'.
32#
33#	hooks_add_default_set sets the default behavior of hooks_add
34#
35#	hooks_get simply returns the named list of functions.
36#
37#	hooks_has indicates whether "func" in in "HOOKS".
38#
39#	hooks_run runs each "func" in $HOOKS and stops if any of them
40#	return a bad status.
41#
42#	hooks_run_all does the same but does not stop on error.
43#
44#	If run_hooks or run_hooks_all is given a flag of '--lifo' or
45#	2nd argument of LIFO the hooks are run in the reverse order of
46#	calls to hooks_add.
47#	Any "args" specified are passed to each hook function.
48#
49
50# RCSid:
51#	$Id: hooks.sh,v 1.26 2025/08/07 21:59:54 sjg Exp $
52#
53#	@(#)Copyright (c) 2000-2024 Simon J. Gerraty
54#
55#	SPDX-License-Identifier: BSD-2-Clause
56#
57#	Please send copies of changes and bug-fixes to:
58#	sjg@crufty.net
59#
60
61# avoid multiple inclusion
62_HOOKS_SH=:
63
64# does local *actually* work?
65local_works() {
66    local _fu
67}
68
69if local_works > /dev/null 2>&1; then
70    _local=local
71else
72    _local=:
73fi
74# for backwards compatability
75local=$_local
76
77
78##
79# hooks_add_all list func ...
80#
81# add "func"s to "list" regardless
82#
83hooks_add_all() {
84    eval $_local __h
85    __h=$1; shift
86    case "$1" in
87    --first)
88        shift
89        eval "$__h=\"$* \$$__h\""
90        ;;
91    *)  eval "$__h=\"\$$__h $*\"";;
92    esac
93}
94
95##
96# hooks_add_once list func ...
97#
98# add "func"s to "list" if not already there
99#
100hooks_add_once() {
101    eval $_local __h __hh __first
102    __h=$1; shift
103    case "$1" in
104    --first) shift; __first=:;;
105    *) __first=;;
106    esac
107    eval "__hh=\$$__h"
108    while [ $# -gt 0 ]
109    do
110        : __hh="$__hh" 1="$1"
111        case "$__first $__hh " in
112        *" $1 "*) ;;    # dupe
113        :*) __hh="$1 $__hh";;
114        *) __hh="$__hh $1";;
115        esac
116        shift
117    done
118    eval "$__h=\"$__hh\""
119}
120
121##
122# hooks_add_default_set [--]{all,once}
123#
124# change the default method of hooks_add
125#
126hooks_add_default_set() {
127    case "$1" in
128    once|--once) HOOKS_ADD_DEFAULT=once;;
129    *) HOOKS_ADD_DEFAULT=all;;
130    esac
131}
132
133##
134# hooks_add [--{all,once}] list func ...
135#
136# add "func"s to "list"
137#
138# If '--once' use hooks_add_once,
139# default is hooks_add_all.
140#
141hooks_add() {
142    case "$1" in
143    --all) shift; hooks_add_all "$@";;
144    --once) shift; hooks_add_once "$@";;
145    *) hooks_add_${HOOKS_ADD_DEFAULT:-all} "$@";;
146    esac
147}
148
149##
150# hooks_get [--lifo] list [LIFO]
151#
152# return $list
153#
154hooks_get() {
155    eval $_local __h __h2 e __l
156    case "$1" in
157    --lifo) __l=LIFO; shift;;
158    esac
159    eval "__h=\$$1"
160    case "$__l$2" in
161    LIFO*)
162        __h2="$__h"
163        __h=
164        for e in $__h2
165        do
166            __h="$e $__h"
167        done
168        ;;
169    esac
170    echo "$__h"
171}
172
173##
174# hooks_has list func
175#
176# is func in $list ?
177#
178hooks_has() {
179    eval $_local __h
180    eval "__h=\$$1"
181    case " $__h " in
182    *" $1 "*) return 0;;
183    esac
184    return 1
185}
186
187##
188# hooks_run [--all] [--lifo] list [LIFO] [args]
189#
190# pass "args" to each function in "list"
191# Without '--all'; if any return non-zero return that immediately
192#
193hooks_run() {
194    eval $_local __a e __h __hl __h2 __l
195    __a=return
196    __l=
197
198    while :
199    do
200        case "$1" in
201        --all) __a=:; shift;;
202        --lifo) __l=$1; shift;;
203        *) break;;
204        esac
205    done
206    __hl=$1; shift
207    case "$1" in
208    LIFO) __l=--lifo; shift;;
209    esac
210    __h=`hooks_get $__l $__hl`
211    for e in $__h
212    do
213        $e "$@" || $__a $?
214    done
215}
216
217##
218# hooks_run_all [--lifo] list [LIFO] [args]
219#
220# pass "args" to each function in "list"
221#
222hooks_run_all() {
223    hooks_run --all "$@"
224}
225
226##
227# add_hooks,run_hooks[_all] aliases
228#
229add_hooks() {
230    hooks_add "$@"
231}
232
233run_hooks() {
234    hooks_run "$@"
235}
236
237run_hooks_all() {
238    hooks_run --all "$@"
239}
240
241
242case /$0 in
243*/hooks.sh)
244    # simple unit-test
245    list=HOOKS
246    flags=
247    while :
248    do
249        : 1=$1
250        case "$1" in
251        HOOKS|*hooks) list=$1; shift;;
252        --*) flags="$flags $1"; shift;;
253        *) break;;
254        esac
255    done
256    for f in "$@"
257    do
258        : f=$f
259        case "$f" in
260        LIFO) ;;
261        false|true) ;;
262        *) eval "$f() { echo This is $f; }";;
263        esac
264    done
265    echo hooks_add $flags $list "$@"
266    hooks_add $flags $list "$@"
267    echo hooks_run $list
268    hooks_run $list
269    echo hooks_run --all --lifo $list
270    hooks_run --all --lifo $list
271    echo hooks_run $list LIFO
272    hooks_run $list LIFO
273    ;;
274esac
275