summaryrefslogtreecommitdiff
blob: 4d56cfa3503af3c806bcaaa6e2766adb05b13e58 (plain)
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
155
156
157
158
159
160
161
162
163
164
# Copyright 2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
# shellcheck shell=sh disable=3043

# This file contains functions considered experimental in nature. Any functions
# defined here may eventually be promoted to the core library or to a distinct
# module. They may also be dropped without warning, either because they were
# not considered as being sufficiently within the scope of gentoo-functions as
# a project or because they were deemed to be insufficiently useful. As such, it
# serves as a staging ground for new ideas. Note that GENFUN_API_LEVEL must
# never be incremented on account of any changes made to this module.

warn "sourcing the experimental module from gentoo-functions; no stability guarantee is provided"

#
# Expects three parameters, all of which must be integers, and determines
# whether the first is numerically greater than or equal to the second, and
# numerically lower than or equal to the third.
#
int_between()
{
	if [ "$#" -lt 3 ]; then
		warn "int_between: too few arguments (got $#, expected 3)"
		false
	elif ! is_int "$2" || ! is_int "$3"; then
		_warn_for_args int_between "$@"
		false
	else
		is_int "$1" && [ "$1" -ge "$2" ] && [ "$1" -le "$3" ]
	fi
}

#
# Returns 0 provided that two conditions hold. Firstly, that the standard input
# is connected to a tty. Secondly, that the standard output has not been closed.
# This technique is loosely based on the IO::Interactive::Tiny module from CPAN.
#
is_interactive()
{
	test -t 0 && { true 3>&1; } 2>/dev/null
}

#
# Continuously reads lines from the standard input, prepending each with a
# timestamp before printing to the standard output. Timestamps shall be in the
# format of "%FT%T%z", per strftime(3). Output buffering shall not be employed.
#
prepend_ts()
{
	if hash gawk 2>/dev/null; then
		prepend_ts()
		{
			gawk '{ print strftime("%FT%T%z"), $0; fflush(); }'
		}
	elif hash ts 2>/dev/null; then
		prepend_ts()
		{
			ts '%FT%T%z'
		}
	elif bash -c '(( BASH_VERSINFO >= 4 ))' 2>/dev/null; then
		prepend_ts()
		{
			bash -c 'while read -r; do printf "%(%FT%T%z)T %s\n" -1 "${REPLY}"; done'
		}
	else
		warn "prepend_ts: this function requires that either bash, gawk or moreutils be installed"
		return 1
	fi

	prepend_ts
}

#
# Expects three parameters and determines whether the first is lexicographically
# greater than or equal to the second, and lexicographically lower than or equal
# to the third. The effective system collation shall affect the results, given
# the involvement of the sort(1) utility.
#
str_between()
{
	local i

	if [ "$#" -ne 3 ]; then
		warn "str_between: wrong number of arguments (got $#, expected 3)"
		false
	else
		set -- "$2" "$1" "$3"
		i=0
		printf '%s\n' "$@" |
		sort |
		while IFS= read -r line; do
			eval "[ \"\${line}\" = \"\$$(( i += 1 ))\" ]" || ! break
		done
	fi
}

#
# Takes the first parameter as a string (s), the second parameter as a numerical
# position (m) and, optionally, the third parameter as a numerical length (n).
# It shall then print a <newline> terminated substring of s that is at most, n
# characters in length and which begins at position m, numbering from 1. If n is
# omitted, or if n specifies more characters than are left in the string, the
# length of the substring shall be limited by the length of s. The function
# shall return 0 provided that none of the parameters are invalid.
#
substr()
{
	local i str

	if [ "$#" -lt 2 ]; then
		warn "substr: too few arguments (got $#, expected at least 2)"
		return 1
	elif ! is_int "$2"; then
		_warn_for_args substr "$2"
		return 1
	elif [ "$#" -ge 3 ]; then
		if ! is_int "$3"; then
			_warn_for_args substr "$3"
			return 1
		elif [ "$3" -lt 0 ]; then
			set -- "$1" "$2" 0
		fi
	fi
	str=$1
	i=0
	while [ "$(( i += 1 ))" -lt "$2" ]; do
		str=${str#?}
	done
	i=0
	while [ "${#str}" -gt "${3-${#str}}" ]; do
		str=${str%?}
	done
	if [ "${#str}" -gt 0 ]; then
		printf '%s\n' "${str}"
	fi
}

#
# Takes the first parameter as either a relative pathname or an integer
# referring to a number of iterations. To be recognised as a pathname, the first
# four characters must form the special prefix, ".../". It recurses upwards from
# the current directory until either the relative pathname is found to exist,
# the specified number of iterations has occurred, or the root directory is
# encountered. In the event that the root directory is reached without either of
# the first two conditions being satisfied, the return value shall be 1.
# Otherwise, the value of PWD shall be printed to the standard output.
#
up()
{
	local i

	i=0
	while [ "${PWD}" != / ]; do
		chdir ../
		case $1 in
			.../*)
				test -e "${1#.../}"
				;;
			*)
				test "$(( i += 1 ))" -eq "$1"
		esac \
		&& pwd && return
	done
}