-
Notifications
You must be signed in to change notification settings - Fork 89
/
kwio.sh
259 lines (230 loc) · 7.76 KB
/
kwio.sh
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# NOTE: src/kw_config_loader.sh must be included before this file
declare -gr BLUECOLOR='\033[1;34;49m%s\033[m'
declare -gr REDCOLOR='\033[1;31;49m%s\033[m'
declare -gr YELLOWCOLOR='\033[1;33;49m%s\033[m'
declare -gr GREENCOLOR='\033[1;32;49m%s\033[m'
declare -gr SEPARATOR='========================================================='
# Alerts command completion to the user.
#
# @COMMAND First argument should be the kw command string which the user wants
# to get notified about. It can be printed in visual notification if
# ${configurations[visual_alert_command]} uses it.
# @ALERT_OPT Second argument is the string with the "--alert=" option or "" if
# no alert option was given by the user.
function alert_completion()
{
local COMMAND=$1
local ALERT_OPT=$2
local opts
if [[ $# -gt 1 && "$ALERT_OPT" =~ ^--alert= ]]; then
opts="$(printf '%s\n' "$ALERT_OPT" | sed s/--alert=//)"
else
opts="${configurations[alert]}"
fi
while read -rN 1 option; do
if [ "$option" == 'v' ]; then
if command_exists "${configurations[visual_alert_command]}"; then
eval "${configurations[visual_alert_command]} &"
else
warning 'The following command set in the visual_alert_command variable could not be run:'
warning "${configurations[visual_alert_command]}"
warning 'Check if the necessary packages are installed.'
fi
elif [ "$option" == 's' ]; then
if command_exists "${configurations[sound_alert_command]}"; then
eval "${configurations[sound_alert_command]} &"
else
warning 'The following command set in the sound_alert_command variable could not be run:'
warning "${configurations[sound_alert_command]}"
warning 'Check if the necessary packages are installed.'
fi
fi
done <<< "$opts"
}
# Print colored message. This function verifies if stdout
# is open and print it with color, otherwise print it without color.
#
# @param $1 [${@:2}] [-n ${@:3}] it receives the variable defining
# the color to be used and two optional params:
# - the option '-n', to not output the trailing newline
# - text message to be printed
#shellcheck disable=SC2059
function colored_print()
{
local message="${*:2}"
local colored_format="${!1}"
if [[ $# -ge 2 && $2 = '-n' ]]; then
message="${*:3}"
if [ -t 1 ]; then
printf "$colored_format" "$message"
else
printf '%s' "$message"
fi
else
if [ -t 1 ]; then
printf "$colored_format\n" "$message"
else
printf '%s\n' "$message"
fi
fi
}
# Print normal message (e.g info messages).
function say()
{
colored_print BLUECOLOR "$@"
}
# Print error message.
function complain()
{
colored_print REDCOLOR "$@"
}
# Warning error message.
function warning()
{
colored_print YELLOWCOLOR "$@"
}
# Print success message.
function success()
{
colored_print GREENCOLOR "$@"
}
# Allows the user to select a given question with yes or no answers. Shows a
# default option (as a capitalized letter). If the user inserts anything
# different from a "y" or a "yes" (capitalization doesn't matter here) it will
# be treated as a "no".
#
# @message A string with the message to be displayed for the user.
# @default_option A string (simply y or n) to set the default answer for the
# user. If this parameter is not given, or if it is invalid, 'n'
# will be used.
#
# Return:
# Return "1" if the user accept the question, otherwise, return "0"
# Note: ask_yN return the string '1' and '0', you have to handle it by
# yourself in the code.
function ask_yN()
{
local message="$1"
local default_option="${2:-'n'}"
local yes='y'
local no='N'
if [[ "$default_option" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
yes='Y'
no='n'
fi
message="$message [$yes/$no]"
response=$(ask_with_default "$message" "$default_option" false)
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
printf '%s\n' '1'
else
printf '%s\n' '0'
fi
}
# Asks for user input
#
# @message A string with the message to be displayed to the user.
# @default_option String with default answer to be used if no input is given.
# @show_default A string that defines if the default value will be printed to
# the user. Any non-empty value causes this behavior to be false.
# @flag String to mark special conditions in this function. To activate the test
# mode, set this to 'TEST_MODE'.
#
# Return:
# The user answer, guaranteed not to be empty.
# Note: this function does not verify the given answer. You have to handle this
# somewhere else.
function ask_with_default()
{
local message="$1"
local default_option="$2"
local show_default="$3"
local flag="$4"
local value
if [[ -z "$show_default" ]]; then
message+=" ($default_option)"
fi
message+=': '
[[ "$flag" == 'TEST_MODE' ]] && printf '%s\n' "$message"
read -r -p "$message" response
if [[ "$?" -ne 0 || -z "$response" ]]; then
printf '%s\n' "$default_option"
return
fi
printf '%s\n' "$response"
}
# Load text from a file into a dictionary. The file that will be read must have
# a key before a text body to name that particular text as part of the key, for
# example:
#
# [KEY]:
# text
#
# [KEY] must be non-empty, alphanumeric and between square brackets followed by
# a colon. The global array string_file can then be queried by key as in
# ${string_file[<key>]} to obtain the string. <key> should be named according
# to its respective module for compatibility. That is, if we have modules A and
# B, name A's keys as [KEY_A] and B's keys as [KEY_B]. This makes it so the
# module's keys are compatible with each other
#
# @text_file_to_be_loaded_path The full path of the text file to be loaded as
# the first argument.
#
# Return:
# SUCCESS : In case of success.
# EKEYREJECTED : If an invalid key is found, prints the line with a bad key.
# ENOKEY : If a key is not found.
# ENOENT : If @text_file_to_be_loaded_path is invalid, or is not a text file.
# ENODATA : If the file given in @text_file_to_be_loaded_path is empty.
function load_module_text()
{
local text_file_to_be_loaded_path="$1"
local key=''
local line_counter=0
local error=0 # SUCCESS
local key_set=0
local first_line=0
# Associative array to read strings from files
unset module_text_dictionary
declare -gA module_text_dictionary
if [[ ! -f "$text_file_to_be_loaded_path" ]]; then
complain "[ERROR]:$text_file_to_be_loaded_path: Does not exist or is not a text file."
return 2 # ENOENT
fi
if [[ ! -s "$text_file_to_be_loaded_path" ]]; then
complain "[ERROR]:$text_file_to_be_loaded_path: File is empty."
return 61 # ENODATA
fi
while read -r line; do
((line_counter++))
# Match [VALUE]:
if [[ "$line" =~ ^\[(.*)\]:$ ]]; then
key=''
# Match to check if VALUE is composed of alphanumeric and underscores only
[[ "${BASH_REMATCH[1]}" =~ (^[A-Za-z0-9_][A-Za-z0-9_]*$) ]] && key="${BASH_REMATCH[1]}"
if [[ -z "$key" ]]; then
error=129 # EKEYREJECTED
complain "[ERROR]:$text_file_to_be_loaded_path:$line_counter: Keys should be alphanum chars."
continue
fi
if [[ -n "${module_text_dictionary[$key]}" ]]; then
warning "[WARNING]:$text_file_to_be_loaded_path:$line_counter: Overwriting '$key' key."
fi
key_set=1
first_line=1
module_text_dictionary["$key"]=''
# If we are inside a text block, collect the current line
elif [[ -n "$key" ]]; then
if [[ "$first_line" -eq 1 ]]; then
first_line=0
else
module_text_dictionary["$key"]+=$'\n'
fi
module_text_dictionary["$key"]+="$line"
fi
done < "$text_file_to_be_loaded_path"
if [[ "$key_set" -eq 0 ]]; then
error=126 # ENOKEY
complain "[ERROR]:$text_file_to_be_loaded_path: No key found."
fi
return "$error"
}