-
Notifications
You must be signed in to change notification settings - Fork 40
/
save.bash
277 lines (234 loc) · 7.71 KB
/
save.bash
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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
# save.bash
# This file contains the implementation of aconfmgr's 'save' command.
function AconfSave() {
local config_save_target=$config_dir/99-unsorted.sh
local modified=n
AconfCompile
LogEnter 'Saving configuration...\n'
#
# Packages
#
LogEnter 'Examining packages...\n'
# Unknown native packages (installed but not listed)
local -a unknown_packages
comm -13 <(PrintArray packages) <(PrintArray installed_packages) | mapfile -t unknown_packages
if [[ ${#unknown_packages[@]} != 0 ]]
then
LogEnter 'Found %s unknown packages. Registering...\n' "$(Color G ${#unknown_packages[@]})"
printf '\n\n# %s - Unknown packages\n\n\n' "$(date)" >> "$config_save_target"
local package
for package in "${unknown_packages[@]}"
do
Log '%s...\r' "$(Color M "%q" "$package")"
local description
description="$(LC_ALL=C "$PACMAN" --query --info "$package" | grep '^Description' | cut -d ':' -f 2)"
printf 'AddPackage %q #%s\n' "$package" "$description" >> "$config_save_target"
done
modified=y
LogLeave
fi
# Missing native packages (listed but not installed on current system)
local -a missing_packages
comm -23 <(PrintArray packages) <(PrintArray installed_packages) | mapfile -t missing_packages
if [[ ${#missing_packages[@]} != 0 ]]
then
LogEnter 'Found %s missing packages. Un-registering.\n' "$(Color G ${#missing_packages[@]})"
printf '\n\n# %s - Missing packages\n\n\n' "$(date)" >> "$config_save_target"
local package
for package in "${missing_packages[@]}"
do
printf 'RemovePackage %q\n' "$package" >> "$config_save_target"
done
modified=y
LogLeave
fi
# Unknown foreign packages (installed but not listed)
local -a unknown_foreign_packages
comm -13 <(PrintArray foreign_packages) <(PrintArray installed_foreign_packages) | mapfile -t unknown_foreign_packages
if [[ ${#unknown_foreign_packages[@]} != 0 ]]
then
LogEnter 'Found %s unknown foreign packages. Registering...\n' "$(Color G ${#unknown_foreign_packages[@]})"
printf '\n\n# %s - Unknown foreign packages\n\n\n' "$(date)" >> "$config_save_target"
local package
for package in "${unknown_foreign_packages[@]}"
do
Log '%s...\r' "$(Color M "%q" "$package")"
local description
description="$(LC_ALL=C "$PACMAN" --query --info "$package" | grep '^Description' | cut -d ':' -f 2)"
printf 'AddPackage --foreign %q #%s\n' "$package" "$description" >> "$config_save_target"
done
modified=y
LogLeave
fi
# Missing foreign packages (listed but not installed on current system)
local -a missing_foreign_packages
comm -23 <(PrintArray foreign_packages) <(PrintArray installed_foreign_packages) | mapfile -t missing_foreign_packages
if [[ ${#missing_foreign_packages[@]} != 0 ]]
then
LogEnter 'Found %s missing foreign packages. Un-registering.\n' "$(Color G ${#missing_foreign_packages[@]})"
printf '\n\n# %s - Missing foreign packages\n\n\n' "$(date)" >> "$config_save_target"
local package
for package in "${missing_foreign_packages[@]}"
do
printf 'RemovePackage --foreign %q\n' "$package" >> "$config_save_target"
done
modified=y
LogLeave
fi
LogLeave # Examining packages
#
# Emit files
#
LogEnter 'Registering files...\n'
# Don't emit redundant CreateDir lines
local -A skip_dirs
local file
( Print0Array system_only_files ; Print0Array changed_files ) | \
while read -r -d $'\0' file
do
local path=${file%/*}
while [[ -n "$path" ]]
do
skip_dirs[$path]=y
path=${path%/*}
done
done
if [[ ${#config_only_files[@]} != 0 ]]
then
LogEnter 'Found %s extra files.\n' "$(Color G ${#config_only_files[@]})"
printf '\n\n# %s - Extra files\n\n\n' "$(date)" >> "$config_save_target"
local i
for ((i=${#config_only_files[@]}-1; i>=0; i--))
do
file=${config_only_files[$i]}
printf 'RemoveFile %q\n' "$file" >> "$config_save_target"
done
modified=y
LogLeave
fi
if [[ ${#system_only_files[@]} != 0 || ${#changed_files[@]} != 0 ]]
then
LogEnter 'Found %s new and %s changed files.\n' "$(Color G ${#system_only_files[@]})" "$(Color G ${#changed_files[@]})"
printf '\n\n# %s - New / changed files\n\n\n' "$(date)" >> "$config_save_target"
( Print0Array system_only_files ; Print0Array changed_files ) | \
while read -r -d $'\0' file
do
if [[ -n ${skip_dirs[$file]+x} ]]
then
continue
fi
local dir
dir="$(dirname "$file")"
mkdir --parents "$config_dir"/files/"$dir"
local func args props suffix=''
local output_file="$output_dir"/files/"$file"
local system_file="$system_dir"/files/"$file"
local need_remove
if ! [[ -h "$output_file" || -e "$output_file" ]]
then
need_remove=false # don't need RemoveFile if it doesn't exist
elif [[ -h "$output_file" || -h "$system_file" ]]
then
need_remove=true # always need RemoveFile for symlinks
elif [[ ( -d "$output_file" && -d "$system_file" ) || ( -f "$output_file" && -f "$system_file" ) ]]
then
need_remove=false # don't need RemoveFile if both are files or both are directories
else
need_remove=true
fi
if $need_remove
then
printf 'RemoveFile %q # Replacing %s with %s\n' "$file" \
"$(LC_ALL=C stat --format=%F "$output_file")" \
"$(LC_ALL=C stat --format=%F "$system_file")" \
>> "$config_save_target"
fi
if [[ -h "$system_file" ]]
then
func=CreateLink
args=("$file" "$(readlink "$system_file")")
props=(owner group)
elif [[ -d "$system_file" ]]
then
func=CreateDir
args=("$file")
props=(mode owner group)
else
local size
size=$(LC_ALL=C stat --format=%s "$system_file")
if [[ $size -eq 0 ]]
then
func=CreateFile
suffix=' > /dev/null'
if [[ -h "$output_file" || -e "$output_file" ]]
then
printf 'RemoveFile %q\n' "$file" >> "$config_save_target"
fi
else
cp "$system_file" "$config_dir"/files/"$file"
func=CopyFile
fi
args=("$file")
props=(mode owner group)
fi
# Calculate the optional function parameters
local prop
for prop in "${props[@]}"
do
local key="$file:$prop"
if [[ -n "${system_file_props[$key]+x}" ]]
then
args+=("${system_file_props[$key]}")
unset "output_file_props[\$key]"
unset "system_file_props[\$key]"
else
args+=('')
fi
done
# Trim redundant blank parameters
while [[ -z "${args[-1]}" ]]
do
unset 'args[${#args[@]}-1]'
done
printf '%s%s%s\n' "$func" "$(printf ' %q' "${args[@]}")" "$suffix" >> "$config_save_target"
done
modified=y
LogLeave
fi
LogLeave # Emit files
#
# Emit remaining file properties
#
LogEnter 'Registering file properties...\n'
AconfCompareFileProps # Update data after above unsets
if [[ ${#system_only_file_props[@]} != 0 || ${#changed_file_props[@]} != 0 ]]
then
printf '\n\n# %s - New file properties\n\n\n' "$(date)" >> "$config_save_target"
local key
( ( Print0Array system_only_file_props ; Print0Array changed_file_props ) | sort --zero-terminated ) | \
while read -r -d $'\0' key
do
printf 'SetFileProperty %q %q %q\n' "${key%:*}" "${key##*:}" "${system_file_props[$key]}" >> "$config_save_target"
done
modified=y
fi
if [[ ${#config_only_file_props[@]} != 0 ]]
then
printf '\n\n# %s - Extra file properties\n\n\n' "$(date)" >> "$config_save_target"
local key
( Print0Array config_only_file_props | sort --zero-terminated ) | \
while read -r -d $'\0' key
do
printf 'SetFileProperty %q %q %q\n' "${key%:*}" "${key##*:}" '' >> "$config_save_target"
done
modified=y
fi
LogLeave # Registering file properties
if [[ $modified == n ]]
then
LogLeave 'Done (%s).\n' "$(Color G "configuration unchanged")"
else
LogLeave 'Done (%s).\n' "$(Color Y "configuration changed")"
fi
}
: # include in coverage