summaryrefslogtreecommitdiff
path: root/src/lib/messages.sh
blob: d98946994b01bc916f573db62753a31bb27cfb00 (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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#!/usr/bin/env bash
# This may be included with or without `set -euE`

# Copyright (C) 2011 Joshua Ismael Haase Hernández (xihh) <hahj87@gmail.com>
# Copyright (C) 2012 Nicolás Reynolds <fauno@parabola.nu>
# Copyright (C) 2012-2014, 2016-2017 Luke Shumaker <lukeshu@sbcglobal.net>
#
# For just the setup_traps() function:
#   Copyright (C) 2002-2006 Judd Vinet <jvinet@zeroflux.org>
#   Copyright (C) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org>
#   Copyright (C) 2005 Aurelien Foret <orelien@chez.com>
#   Copyright (C) 2005 Christian Hamar <krics@linuxforum.hu>
#   Copyright (C) 2006 Alex Smith <alex@alex-smith.me.uk>
#   Copyright (C) 2006 Andras Voroskoi <voroskoi@frugalware.org>
#   Copyright (C) 2006 Miklos Vajna <vmiklos@frugalware.org>
#
# License: GNU GPLv2+
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

[[ -z ${_INCLUDE_MESSAGES_SH:-} ]] || return 0
_INCLUDE_MESSAGES_SH=true

################################################################################
# Inherit most functions from devtools                                         #
################################################################################

. "$(librelib common)"

################################################################################
# Own functions                                                                #
################################################################################

declare -rgi EXIT_TRUE=0
declare -rgi EXIT_FALSE=1

declare -rgi EXIT_SUCCESS=0
declare -rgi EXIT_FAILURE=1 # generic/unspecified error
declare -rgi EXIT_INVALIDARGUMENT=2
declare -rgi EXIT_NOTIMPLEMENTED=3
declare -rgi EXIT_NOPERMISSION=4
declare -rgi EXIT_NOTINSTALLED=5
declare -rgi EXIT_NOTCONFIGURED=6

# Usage: panic
#
# For programming errors, bails immediately with little fanfare.
panic() {
	_l _ 'panic: malformed call to internal function' >&2
	exit 1
}

# Usage: print MESG [ARGS...]
#
# Like printf, but gettext-aware, and prints a trailing newline
print() {
	[[ $# -ge 1 ]] || panic
	local mesg; mesg="$(_ "$1")"; shift
	printf -- "$mesg\n" "$@"
}

# Usage: whitespace_collapse <<<STRING
#
# Collapses whitespace on stadard I/O, similar to HTML whitespace
# collapsing, with the exception that it puts two spaces between
# sentences.  It considers newline, tab, and space to be whitespace.
whitespace_collapse() {
	[[ $# == 0 ]] || panic

	tr '\n' '\r' | sed -r \
			   -e 's/\r/  /g' -e 's/\t/ /g' \
			   -e 's/(^|[^.!? ]) +/\1 /g' -e 's/([.!?])  +/\1  /g'
}


# Usage: prose MESG [ARGS...]
#
# Do HTML-style whitespace collapsing on the first argument, translate
# it (gettext), then word-wrap it to 75 columns.  This is useful for
# printing a paragraph of prose in --help text.
prose() {
	[[ $# -ge 1 ]] || panic
	local mesg; mesg="$(_ "$(whitespace_collapse <<<"$1")")"; shift
	printf -- "$mesg" "$@" | fmt -u
}

# Usage: bullet MESG [ARGS...]
# Like prose, but print a bullet "-" before the first line, and indent the
# remaining lines.
bullet() {
	[[ $# -ge 1 ]] || panic
	local mesg; mesg="$(_ "$(whitespace_collapse <<<"$1")")"; shift
	# Wrap the text to 71 columns; 75 (the default) minus a 4 column indent
	printf -- "$mesg" "$@" | fmt -u -w 71 | sed -e '1s/^/  - /' -e '2,$s/^/    /'
}

# Usage: flag [FLAG DESCRIPTION|HEADING:]...
#
# Print a flag and description formatted for --help text.
#
#   ex: flag '-C <FILE>' 'Use this file instead of pacman.conf'
#
# The descriptions and headings are fed through gettext, the flags ar
# not, so if part of a flag needs to be translated, you must do that
# yourself:
#
#   ex: flag "-C <$(_ FILE)>" 'Use this file instead of pacman.conf'
#
# If you want to line-break the description in the source, so it isn't
# crazy-long, feel free, it is reflowed/wrapped the same way as prose
# and bullet.  If you pass in multiple flag/description pairs at once,
# the descriptions are all alligned together.
#
# A heading MUST end with a colon (':'), this is how it knows that it
# is a heading.  Similarly, a flag MUST NOT end with a colon.
flag() {
	local args=("$@")

	declare -i flaglen=0
	while [[ $# -gt 0 ]]; do
		if [[ $1 == *: ]]; then
			shift 1
		else
			if [[ ${#1} -gt $flaglen ]]; then
				flaglen=${#1}
			fi
			shift 2
		fi
	done
	set -- "${args[@]}"

	# Unless the $flaglen is extra-wide, the $desc should start at
	# column 16 (that is, two literal-tabs).  If $flaglen is wide,
	# this should be increased in increments of 8 (that is, a
	# literal-tab).  Everything should be wrapped to 75 columns.

	# The printf-format we use has 4 spaces built into it (two at
	# the beginning, and two for a seperator).  Therefore, the
	# default indent is 16-4=12 columns.  And the width left for
	# $desc is (75-4)-indent = 71-indent.

	declare -i indent=12
	while [[ $indent -lt $flaglen ]]; do
		indent+=8
	done
	local fmt2 fmt1
	fmt2="  %-${indent}s  %s\n"
	printf -v fmt1 "  %-${indent}s  %%s\n" ''

	while [[ $# -gt 0 ]]; do
		if [[ $1 == *: ]]; then
			printf -- ' %s\n' "$(_ "$1")"
			shift
		else
			[[ $# -gt 1 ]] || panic
			local flag=$1
			local desc; desc="$(_ "$(whitespace_collapse <<<"$2")")"
			shift 2

			local lines
			IFS=$'\n' lines=($(fmt -u -w $((71-indent)) <<<"$desc"))
			printf -- "$fmt2" "$flag" "${lines[0]}"
			[[ ${#lines[@]} -lt 2 ]] || printf -- "$fmt1" "${lines[@]:1}"
		fi
	done
}

# Usage: term_title MESG [ARGS...]
#
# Sets the terminal title.
term_title() {
	[[ $# -ge 1 ]] || panic
	local fmt=''
	case "$TERM" in
		screen|tmux)
			# shellcheck disable=1003
			fmt='\ek%s\e\\';;
		xterm*|rxvt*)
			fmt='\e]0;%s\a';;
	esac
	printf "$fmt" "$(printf -- "$@")"
}

# Usage: setup_traps [handler]
#
# Sets up traps on TERM, HUP, QUIT and INT signals, as well as the ERR
# event, similar to makepkg.
#
# If `handler` is specified, instead of using the default handler
# (which is good for most purposes), it will call the command handler
# with the arguments:
#
#   ${handler} SIGNAL_NAME MESSAGE_FMT [MESSAGE_PARAMS...]
#
# where MESSAGE_* are printf-like stuff.
#
# This function is based on code from pacman:makepkg
setup_traps() {
	[[ $# -le 1 ]] || panic
	if [[ $# == 1 ]]; then
		eval "_libremessages_trap_exit() { $1 \"\$@\"; }"
	else
		_libremessages_trap_exit() {
			local signal=$1; shift
			echo >&2
			error "$@"
			trap -- "$signal"
			kill "-$signal" "$$"
		}
	fi
	set -E
	for signal in TERM HUP QUIT; do
		trap "_libremessages_trap_exit $signal '%s signal caught. Exiting...' $signal" $signal
	done
	trap '_libremessages_trap_exit INT "Aborted by user! Exiting..."' INT
	trap '_libremessages_trap_exit USR1 "An unknown error has occurred. Exiting..."' ERR
}