-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrconfig.shi
180 lines (161 loc) · 5.27 KB
/
rconfig.shi
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
# vim: set filetype=sh :
# Internal library for reading INI/key-value configuration
# files in the Copacabana build system.
# Meant to be used with Korn Shell 93.
#
# Copyright (c) 2023-2024 Pindorama
# Luiz Antônio Rangel
# SPDX-Licence-Identifier: NCSA
# Reads INI configuration files, now a boilerplate function
# to dotini/inicompat --- and, in case of using
# GNU's Broken-Again Shell, it does a coarse conversion of
# the INI file to a simple Shell-based configuration file
# format.
function rconfig {
file="$1"
if [[ -z $BASH && -n $KSH_VERSION ]]; then
# Load the .ini configuration file as
# a compound variable to the memory.
# Simple as that.
eval conf=$(dotini "$file")
inicompat conf
elif [[ -n $BASH ]]; then # BASH
cat "$1" |
for ((;;)); do
if read line; then
if [[ "$line" =~ \[.*\] ]] || [[ "$line" =~ ^$ ]] ||
[[ "$line" =~ '^[#;].*$' ]]; then
continue
else
keyval="$(printf '%s' "$line" |
sed 's/^\(.*\)[#;].*/\1/;')"
identifier="${keyval%%=*}"
value="${keyval##*=}"
[[ "$keyval" =~ ^$ ]] && continue
eval $(printf '%s='\''%s'\''' $identifier "$value")
fi
else
break
fi
done
fi
}
# This function parses .INI files into a compound variable that can be used
# inside the script via eval.
# For example:
#
# eval private_config=$(dotini conf.ini)
# Then you could just access like so:
# printf '%s\n' "${private_config[SectName][KeyName]}"
# It does not outsource record() or map(), so there's more independence
# and flexibility on choosing identifiers for your INI file configuration.
function dotini {
file="$1"
# The function shall fail before "allocating" anything in memory.
# This may sound like a worry that a C programmer might have, but
# it's just a formality when the topic are ksh-ish languages such
# as GNU's Bash or even ksh93 itself.
if [[ ! -e "$file" ]]; then
panic '%s: file %s does not exist.\n' $0 "$file"
fi
# Allocate an array for recording the file and declarate
# a integer that represents the number of the line.
typeset -a inibuf confline[2]
integer nl l s
# First of all, we shall remove comments.
# I know this could be done manually using a for-loop,
# but I think sed can be more reliable.
(sed 's/^\(.*\)[#;].*/\1/; /^[#;].*$/d; /^$/d' "$file") |
for ((nl=0;; nl++)); do
if read line; then
inibuf[$nl]="$line"
else
break
fi
done
# Open compound variable syntax.
printf '%c' '('
# Parse each line.
for ((l=0; l < ${#inibuf[@]}; l++)); do
line="${inibuf[l]}"
# This gets the INI section.
if section_title="$(rematch "$line" '\[\(.*\)\]')"; then
# The formatted section title.
formatted_section="$(printf '%s' "$section_title" |
nawk '{ gsub(" ", "_"); print(tolower($0)) }')"
# We can procceed to the next line.
continue
fi
# A value declaration in INI follows the format:
# identifier = "value"
# Or
# identifier=value
# confline[1]=identifier
# confline[2]=value
confline[0]="${line%%=*}"
# Bullet-proofing against subshells.
confline[1]="$(echo -n "${line#*=}" | sed 's/\$/\\\$/g; s/\`/\\`/g')"
# If there is not a section, it will only declare the INI
# variable and its value on the compound variable.
if [[ ! -n $formatted_section ]]; then
# Using a apostrophe instead of quotations marks
# because, apart from the fact that it works as a
# micro-optimization --- since the shell will be
# treating its contents as vulgar strings instead of
# trying to interpret what is inside them ---, also
# prevents globbing of strings.
# The only thing that it doesn't prevents,
# unfortunately, is the abuse of subshells or malicious
# substitutions, but I do not think this can be a
# problem here.
printf '[%s]=%s ' \
"${confline[0]}" "${confline[1]}"
# Continue to the next line.
continue
fi
# If it there's a section and the value declaration was
# already parsed, return it as a associative array declaration.
printf '[%s]+=([%s]=%s) ' \
$formatted_section "${confline[0]}" "${confline[1]}"
# Clean confline[] array.
unset confline[0] confline[1]
done
# Close compound variable.
printf '%c' ')'
# Remove the section name from the memory,
# since it can cause fuss later.
unset formatted_section
}
# This function "converts" a compound variable generated from a .ini file via
# dotini() to the format "IDENTIFIER=VALUE".
# It can be useful if the program had just implemented .ini files as a way of
# configuration and developers doesn't want to mess with the entire source code.
# It takes the compound variable identifier as a input, for example:
#
# inicompat conf
function inicompat {
nameref inirecord=$1
integer s k
typeset -a sections
fmt='%s=%q'
sections=(${!inirecord[@]})
for ((s=0; s < ${#sections[@]}; s++)); do
typeset -a confkeys
section=${sections[s]}
confkeys=( ${!inirecord[$section][@]} )
case "${confkeys[0]}" in
0) # If dealing with section-less file.
eval $(printf "$fmt" \
$section "${inirecord[$section]}") ;;
*) # Default... Or dealing with sections.
for ((k=0; k < ${#confkeys[@]}; k++)); do
confkey=${confkeys[$k]}
confval="${inirecord[$section][$confkey]}"
eval $(printf "$fmt" \
$confkey "$confval")
unset confkey confval
done ;;
esac
unset section confkeys confkey confval
done
}