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