• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CyberShadow / aconfmgr / 363

02 Nov 2023 08:36AM UTC coverage: 80.574% (-13.1%) from 93.666%
363

push

github

CyberShadow-Renovate
Update dependency Arch Linux Base to v2023.11.01

3202 of 3974 relevant lines covered (80.57%)

138.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.89
/src/apply.bash
1
# apply.bash
2

3
# This file contains the implementation of aconfmgr's 'apply' command.
4

5
function AconfApply() {
6
        local modified=n
41✔
7

8
        function PrintFileProperty() {
9
                local kind="$1"
68✔
10
                local value="$2"
68✔
11
                local file="$3"
68✔
12

13
                local value_text
68✔
14
                if [[ -z "$value" ]]
68✔
15
                then
16
                        local default_value
6✔
17
                        default_value="$(AconfDefaultFileProp "$file" "$kind")"
12✔
18
                        value_text="$(printf '%s (default value)' "$(Color G "%s" "$default_value")")"
18✔
19
                else
20
                        value_text="$(Color G "%s" "$value")"
124✔
21
                fi
22

23
                Log 'Setting %s of %s to %s\n'        \
204✔
24
                        "$(Color Y "%s" "$kind")"        \
204✔
25
                        "$(Color C "%q" "$file")"        \
204✔
26
                        "$value_text"
204✔
27
        }
28

29
        function ApplyFileProperty() {
30
                local kind="$1"
56✔
31
                local value="$2"
56✔
32
                local file="$3"
56✔
33

34
                PrintFileProperty "$kind" "$value" "$file"
56✔
35

36
                if [[ -z "$value" ]]
56✔
37
                then
38
                        value="$(AconfDefaultFileProp "$file" "$kind")"
6✔
39
                fi
40

41
                case "$kind" in
56✔
42
                        mode)
43
                                sudo chmod "$value" "$file"
16✔
44
                                ;;
45
                        owner)
46
                                sudo chown --no-dereference "$value" "$file"
20✔
47
                                ;;
48
                        group)
49
                                sudo chgrp --no-dereference "$value" "$file"
20✔
50
                                ;;
51
                        *)
52
                                Log 'Unknown property %s with value %s for file %s\n' \
×
53
                                        "$(Color Y "%q" "$kind" )" \
×
54
                                        "$(Color G "%q" "$value")" \
×
55
                                        "$(Color C "%q" "$file" )"
×
56
                                Exit 1
×
57
                                ;;
58
                esac
59
        }
60

61
        function ApplyFileProps() {
62
                local file="$1"
38✔
63
                local prop
38✔
64

65
                for prop in "${all_file_property_kinds[@]}"
122✔
66
                do
67
                        local key="$file:$prop"
122✔
68
                        if [[ -n "${output_file_props[$key]+x}" ]]
122✔
69
                        then
70
                                local value="${output_file_props[$key]}"
44✔
71
                                ApplyFileProperty "$prop" "$value" "$file"
44✔
72
                                unset "output_file_props[\$key]"
44✔
73
                                unset "system_file_props[\$key]"
44✔
74

75
                                printf '%s\t%s\t%q\n' "$prop" "$value" "$file" >> "$system_dir"/file-props.txt
44✔
76
                        fi
77
                done
78
        }
79

80
        function InstallFile() {
81
                local file="$1"
38✔
82
                local source="$output_dir"/files/"$file"
38✔
83

84
                # system
85

86
                local target="$file".aconfmgr-new
38✔
87
                sudo rm --force --dir "$target"
38✔
88

89
                # Is the target a directory?
90
                if sudo test ! -h "$file" -a -d "$file"
38✔
91
                then
92
                        # Is the source a directory?
93
                        if test ! -h "$source" -a -d "$source"
6✔
94
                        then
95
                                # Update the target's properties in-place.
96
                                target=$file
4✔
97
                        else
98
                                # Delete the existing directory.
99
                                # install/mv can't replace a directory with a non-directory.
100
                                sudo rm --force --dir "$file"
2✔
101
                        fi
102
                else
103
                        # Is the source a directory?
104
                        if test ! -h "$source" -a -d "$source"
32✔
105
                        then
106
                                # Delete the existing non-directory.
107
                                # install/mv can't replace a non-directory with a directory either.
108
                                sudo rm --force "$file"
7✔
109
                        fi
110
                fi
111

112
                sudo mkdir --parents "$(dirname "$target")"
76✔
113
                if [[ -h "$source" ]]
38✔
114
                then
115
                        sudo cp --no-dereference "$source" "$target"
6✔
116
                        sudo chown --no-dereference root:root "$target"
6✔
117
                elif [[ -d "$source" ]]
32✔
118
                then
119
                        sudo install -d \
11✔
120
                                 --mode="${output_file_props[$file:mode]:-755}" \
11✔
121
                                 --owner="${output_file_props[$file:owner]:-0}" \
11✔
122
                                 --group="${output_file_props[$file:group]:-0}" \
11✔
123
                                 "$target"
11✔
124
                else
125
                        sudo install \
21✔
126
                                 --mode="${output_file_props[$file:mode]:-$default_file_mode}" \
21✔
127
                                 --owner="${output_file_props[$file:owner]:-0}" \
21✔
128
                                 --group="${output_file_props[$file:group]:-0}" \
21✔
129
                                 "$source" "$target"
21✔
130
                fi
131

132
                if [[ "$target" != "$file" ]]
38✔
133
                then
134
                        sudo mv --force --no-target-directory "$target" "$file"
34✔
135
                fi
136

137
                # $system_dir
138

139
                local system_file="$system_dir"/files/"$file"
38✔
140
                if ! { test ! -h "$source" -a -d "$source" && test ! -h "$system_file" -a -d "$system_file" ; }
49✔
141
                then
142
                        rm --force --dir "$system_file"
36✔
143
                fi
144

145
                mkdir --parents "$(dirname "$system_file")"
76✔
146
                if [[ -h "$source" ]]
38✔
147
                then
148
                        cp --no-dereference "$source" "$system_file"
6✔
149
                elif [[ -d "$source" ]]
32✔
150
                then
151
                        mkdir --parents "$system_file"
11✔
152
                else
153
                        cp --no-dereference "$source" "$system_file"
21✔
154
                fi
155

156
                ApplyFileProps "$file"
38✔
157

158
                if [[ -h "$source" ]]
38✔
159
                then
160
                        # ApplyFileProps will apply and unset owner/group. For
161
                        # symlinks, we need to avoid attempting to restore the
162
                        # mode, so unset it here.
163
                        unset "system_file_props[\$file:mode]"
6✔
164
                        printf '%s\t%s\t%q\n' mode '' "$file" >> "$system_dir"/file-props.txt
6✔
165
                fi
166
        }
167

168
        AconfCompile
41✔
169

170
        LogEnter 'Applying configuration...\n'
41✔
171

172
        #
173
        # Priority files
174
        #
175

176
        LogEnter 'Installing priority files...\n'
41✔
177

178
        function Details_DiffFile() {
179
                if sudo test -d "$file"
16✔
180
                then
181
                        Log '%s (old) is a directory.\n' "$(Color C "%q" "$file")"
4✔
182
                elif test -d "$output_dir"/files/"$file"
14✔
183
                then
184
                        Log '%s (new) is a directory.\n' "$(Color C "%q" "$file")"
10✔
185
                else
186
                        AconfNeedProgram diff diffutils n
9✔
187
                        sudo "${diff_opts[@]}" --unified --no-dereference --report-identical-files "$file" "$output_dir"/files/"$file" || true
18✔
188
                fi
189
        }
190

191
        local file
41✔
192
        comm -12 --zero-terminated \
246✔
193
                 <(  Print0Array priority_files                                 | sort --zero-terminated ) \
×
194
                 <( (Print0Array config_only_files ; Print0Array changed_files) | sort --zero-terminated ) | \
×
195
                while read -r -d $'\0' file
48✔
196
                do
197
                        LogEnter 'Installing %s...\n' "$(Color C %q "$file")"
14✔
198

199
                        if sudo test -e "$file"
7✔
200
                        then
201
                                Confirm Details_DiffFile
4✔
202
                        else
203
                                Confirm ''
3✔
204
                        fi
205

206
                        InstallFile "$file"
7✔
207
                        LogLeave
7✔
208
                        modified=y
7✔
209
                done
210
        comm -23 --zero-terminated <(Print0Array config_only_files | sort --zero-terminated) <(Print0Array priority_files | sort --zero-terminated) | mapfile -d $'\0' config_only_files
246✔
211
        comm -23 --zero-terminated <(Print0Array changed_files     | sort --zero-terminated) <(Print0Array priority_files | sort --zero-terminated) | mapfile -d $'\0' changed_files
246✔
212

213
        LogLeave # Installing priority files
41✔
214

215
        #
216
        # Apply packages
217
        #
218

219
        LogEnter 'Configuring packages...\n'
41✔
220

221
        #        in                in                in-
222
        #        config        system        stalled foreign        action
223
        #        ----------------------------------------
224
        #        no                no                no                no                nothing
225
        #        no                no                no                yes                nothing
226
        #        no                no                yes                no                (prune)
227
        #        no                no                yes                yes                (prune)
228
        #        no                yes                no                no                impossible
229
        #        no                yes                no                yes                impossible
230
        #        no                yes                yes                no                unpin (and prune)
231
        #        no                yes                yes                yes                unpin (and prune)
232
        #        yes                no                no                no                install via pacman
233
        #        yes                no                no                yes                install via makepkg
234
        #        yes                no                yes                no                pin
235
        #        yes                no                yes                yes                pin
236
        #        yes                yes                no                no                impossible
237
        #        yes                yes                no                yes                impossible
238
        #        yes                yes                yes                no                nothing
239
        #        yes                yes                yes                yes                nothing
240

241
        # Unknown packages (native and foreign packages that are explicitly installed but not listed)
242
        local -a unknown_packages
41✔
243
        comm -13                                                                               \
287✔
244
                 <((PrintArray           packages ; PrintArray           foreign_packages) | sort) \
×
245
                 <((PrintArray installed_packages ; PrintArray installed_foreign_packages) | sort) \
×
246
                 | mapfile -t unknown_packages
41✔
247

248
        if [[ ${#unknown_packages[@]} != 0 ]]
41✔
249
        then
250
                LogEnter 'Unpinning %s unknown packages.\n' "$(Color G ${#unknown_packages[@]})"
6✔
251

252
                function Details() { Log 'Unpinning (setting install reason to '\''as dependency'\'') the following packages:%s\n' "$(Color M " %q" "${unknown_packages[@]}")" ; }
6✔
253
                Confirm Details
3✔
254

255
                Print0Array unknown_packages | sudo xargs -0 "$PACMAN" --database --asdeps
6✔
256

257
                modified=y
3✔
258
                LogLeave
3✔
259
        fi
260

261
        # Missing packages (native and foreign packages that are listed in the configuration, but not marked as explicitly installed)
262
        local -a missing_packages
41✔
263
        comm -23                                                                                                                                                           \
287✔
264
                 <((PrintArray           packages ; PrintArray           foreign_packages) | sort) \
×
265
                 <((PrintArray installed_packages ; PrintArray installed_foreign_packages) | sort) \
×
266
                 | mapfile -t missing_packages
41✔
267

268
        # Missing installed/unpinned packages (native and foreign packages that are implicitly installed,
269
        # and listed in the configuration, but not marked as explicitly installed)
270
        local -a missing_unpinned_packages
41✔
271
        comm -12 <(PrintArray missing_packages) <(("$PACMAN" --query --quiet || true) | sort) | mapfile -t missing_unpinned_packages
229✔
272

273
        if [[ ${#missing_unpinned_packages[@]} != 0 ]]
41✔
274
        then
275
                LogEnter 'Pinning %s unknown packages.\n' "$(Color G ${#missing_unpinned_packages[@]})"
4✔
276

277
                function Details() { Log 'Pinning (setting install reason to '\''explicitly installed'\'') the following packages:%s\n' "$(Color M " %q" "${missing_unpinned_packages[@]}")" ; }
4✔
278
                Confirm Details
2✔
279

280
                Print0Array missing_unpinned_packages | sudo xargs -0 "$PACMAN" --database --asexplicit
4✔
281

282
                modified=y
2✔
283
                LogLeave
2✔
284
        fi
285

286
        # Orphan packages
287

288
        local -a files_in_deleted_packages=()
82✔
289

290
        if "$PACMAN" --query --unrequired --unrequired --deps --quiet > /dev/null
41✔
291
        then
292
                LogEnter 'Pruning orphan packages...\n'
5✔
293

294
                # We have to loop, since pacman's dependency scanning doesn't seem to be recursive
295
                local iter=1
5✔
296
                while true
10✔
297
                do
298
                        LogEnter 'Iteration %s:\n' "$(Color G "$iter")"
20✔
299

300
                        LogEnter 'Querying orphan packages...\n'
10✔
301
                        local -a orphan_packages
10✔
302
                        ( "$PACMAN" --query --unrequired --unrequired --deps --quiet || true ) | mapfile -t orphan_packages
25✔
303
                        LogLeave
10✔
304

305
                        if [[ ${#orphan_packages[@]} != 0 ]]
10✔
306
                        then
307
                                LogEnter 'Pruning %s orphan packages.\n' "$(Color G ${#orphan_packages[@]})"
10✔
308

309
                                function Details() { Log 'Removing the following orphan packages:%s\n' "$(Color M " %q" "${orphan_packages[@]}")" ; }
10✔
310
                                ParanoidConfirm Details
5✔
311

312
                                local -a deleted_files=()
10✔
313
                                "$PACMAN" --query --list --quiet "${orphan_packages[@]}" | sed 's#^\(.*\)/$#\1#' | mapfile -t deleted_files
15✔
314
                                files_in_deleted_packages+=("${deleted_files[@]}")
5✔
315

316
                                sudo "${pacman_opts[@]}" --remove "${orphan_packages[@]}"
5✔
317

318
                                LogLeave
5✔
319
                        fi
320

321
                        iter=$((iter+1))
10✔
322

323
                        LogLeave # Iteration
10✔
324

325
                        if [[ ${#orphan_packages[@]} == 0 ]]
10✔
326
                        then
327
                                break
5✔
328
                        fi
329
                done
330

331
                modified=y
5✔
332
                LogLeave # Removing orphan packages
5✔
333
        fi
334

335

336
        # Missing native packages (native packages that are listed in the configuration, but not installed)
337
        local -a missing_native_packages
41✔
338
        comm -23 <(PrintArray packages) <(("$PACMAN" --query --quiet || true) | sort) | mapfile -t missing_native_packages
230✔
339

340
        if [[ ${#missing_native_packages[@]} != 0 ]]
41✔
341
        then
342
                LogEnter 'Installing %s missing native packages.\n' "$(Color G ${#missing_native_packages[@]})"
6✔
343

344
                function Details() { Log 'Installing the following native packages:%s\n' "$(Color M " %q" "${missing_native_packages[@]}")" ; }
6✔
345
                ParanoidConfirm Details
3✔
346

347
                AconfInstallNative "${missing_native_packages[@]}"
3✔
348

349
                modified=y
3✔
350
                LogLeave
3✔
351
        fi
352

353
        # Missing foreign packages (foreign packages that are listed in the configuration, but not installed)
354
        local -a missing_foreign_packages
41✔
355
        comm -23 <(PrintArray foreign_packages) <(("$PACMAN" --query --quiet || true) | sort) | mapfile -t missing_foreign_packages
228✔
356

357
        if [[ ${#missing_foreign_packages[@]} != 0 ]]
41✔
358
        then
359
                LogEnter 'Installing %s missing foreign packages.\n' "$(Color G ${#missing_foreign_packages[@]})"
×
360

361
                function Details() { Log 'Installing the following foreign packages:%s\n' "$(Color M " %q" "${missing_foreign_packages[@]}")" ; }
362
                Confirm Details
×
363

364
                # If an AUR helper is present in the list of packages to be installed,
365
                # install it first, then use it to install the rest of the foreign packages.
366
                function InstallAurHelper() {
367
                        local package helper
×
368
                        for package in "${missing_foreign_packages[@]}"
×
369
                        do
370
                                for helper in "${aur_helpers[@]}"
×
371
                                do
372
                                        if [[ "$package" == "$helper" ]]
×
373
                                        then
374
                                                LogEnter 'Installing AUR helper %s...\n' "$(Color M %q "$helper")"
×
375
                                                ParanoidConfirm ''
×
376
                                                AconfInstallForeign "$package"
×
377
                                                aur_helper="$package"
×
378
                                                LogLeave
×
379
                                                return
×
380
                                        fi
381
                                done
382
                        done
383
                }
384
                if [[ $EUID != 0 ]]
×
385
                then
386
                        InstallAurHelper
×
387
                fi
388

389
                AconfInstallForeign "${missing_foreign_packages[@]}"
×
390

391
                modified=y
×
392
                LogLeave
×
393
        fi
394

395
        LogLeave # Configuring packages
41✔
396

397
        # Read file owners
398
        local -a modified_files_in_deleted_packages
41✔
399
        (
400
                cat "$tmp_dir"/output-files "$tmp_dir"/system-files
41✔
401
                local kind value file
41✔
402
                cat "$output_dir"/file-props.txt "$system_dir"/file-props.txt \
41✔
403
                        | \
×
404
                        while IFS=$'\t' read -r kind value file
510✔
405
                        do
406
                                eval "printf '%s\\0' $file" # Unescape
428✔
407
                        done
408
        ) \
×
409
                | sort --zero-terminated --unique \
41✔
410
                | comm --zero-terminated -12 <(Print0Array files_in_deleted_packages | sort --zero-terminated --unique) /dev/stdin \
123✔
411
                | tac -s $'\0' \
41✔
412
                | mapfile -t -d $'\0' modified_files_in_deleted_packages
41✔
413

414
        if [[ "${#modified_files_in_deleted_packages[@]}" -gt 0 ]]
41✔
415
        then
416
                LogEnter 'Detected %s modified files in pruned packages.\n' \
2✔
417
                                 "$(Color G %s "${#modified_files_in_deleted_packages[@]}")"
2✔
418

419
                LogEnter 'Updating system file data...\n'
1✔
420
                local file
1✔
421
                for file in "${modified_files_in_deleted_packages[@]}"
7✔
422
                do
423
                        local system_file="$system_dir"/files"$file"
7✔
424
                        if [[ -h "$system_file" || -f "$system_file" ]]
12✔
425
                        then
426
                                rm --force "$system_file"
3✔
427
                        elif [[ -d "$system_file" ]]
4✔
428
                        then
429
                                rmdir --ignore-fail-on-non-empty "$system_file"
3✔
430
                        elif [[ -e "$system_file" ]]
1✔
431
                        then
432
                                FatalError '%s exists, but is neither file or directory or link?\n' \
×
433
                                                   "$(Color C "%q" "$file")"
×
434
                        fi
435

436
                        local prop
7✔
437
                        for prop in "${all_file_property_kinds[@]}"
28✔
438
                        do
439
                                printf '%s\t%s\t%q\n' "$prop" '' "$file" >> "$system_dir"/file-props.txt
28✔
440
                        done
441
                done
442
                LogLeave
1✔
443

444
                LogEnter 'Updating owned file list...\n'
1✔
445
                ( "$PACMAN" --query --list --quiet || true ) | sed 's#\/$##' | sort --unique > "$tmp_dir"/owned-files
3✔
446
                LogLeave
1✔
447

448
                LogEnter 'Rescanning...\n'
1✔
449
                AconfAnalyzeFiles
1✔
450
                LogLeave
1✔
451
                LogLeave
1✔
452
        fi
453

454
        #
455
        # Copy files
456
        #
457

458
        LogEnter 'Configuring files...\n'
41✔
459

460
        if [[ ${#changed_files[@]} != 0 ]]
41✔
461
        then
462
                LogEnter 'Overwriting %s changed files.\n' "$(Color G ${#changed_files[@]})"
16✔
463

464
                # shellcheck disable=2059
465
                function Details() {
466
                        Log 'Overwriting the following changed files:\n'
8✔
467
                        printf "$(Color W "*") $(Color C "%s" "%s")\\n" "${changed_files[@]}"
24✔
468
                }
469
                Confirm Details
8✔
470

471
                for file in "${changed_files[@]}"
12✔
472
                do
473
                        LogEnter 'Overwriting %s...\n' "$(Color C "%q" "$file")"
24✔
474
                        ParanoidConfirm Details_DiffFile
12✔
475
                        InstallFile "$file"
12✔
476
                        LogLeave ''
12✔
477
                done
478

479
                modified=y
8✔
480
                LogLeave
8✔
481
        fi
482

483
        if [[ ${#config_only_files[@]} != 0 ]]
41✔
484
        then
485
                LogEnter 'Installing %s new files.\n' "$(Color G ${#config_only_files[@]})"
20✔
486

487
                # shellcheck disable=2059
488
                function Details() {
489
                        Log 'Installing the following new files:\n'
10✔
490
                        printf "$(Color W "*") $(Color C "%s" "%s")\\n" "${config_only_files[@]}"
30✔
491
                }
492
                Confirm Details
10✔
493

494
                for file in "${config_only_files[@]}"
19✔
495
                do
496
                        LogEnter 'Installing %s...\n' "$(Color C "%q" "$file")"
38✔
497
                        ParanoidConfirm ''
19✔
498
                        InstallFile "$file"
19✔
499
                        LogLeave ''
19✔
500
                done
501

502
                modified=y
10✔
503
                LogLeave
10✔
504
        fi
505

506
        local -a files_to_delete=()
82✔
507
        local -a files_to_restore=()
82✔
508

509
        if [[ ${#system_only_files[@]} != 0 ]]
41✔
510
        then
511
                LogEnter 'Processing system-only files...\n'
6✔
512

513
                # Delete unknown stray files (files not present in config and belonging to no package)
514

515
                LogEnter 'Filtering system-only stray files...\n'
6✔
516
                local system_only_stray_files=0
6✔
517
                tr '\n' '\0' < "$tmp_dir"/owned-files > "$tmp_dir"/owned-files-0
6✔
518
                comm -13 --zero-terminated "$tmp_dir"/owned-files-0 <(Print0Array system_only_files) | \
12✔
519
                        while read -r -d $'\0' file
15✔
520
                        do
521
                                files_to_delete+=("$file")
9✔
522
                                system_only_stray_files=$((system_only_stray_files+1))
9✔
523
                        done
524
                LogLeave 'Done (%s system-only stray files).\n' "$(Color G %s $system_only_stray_files)"
12✔
525

526
                # Restore unknown owned files (files not present in config and belonging to a package)
527

528
                LogEnter 'Filtering system-only owned files...\n'
6✔
529
                local system_only_owned_files=0
6✔
530
                comm -12 --zero-terminated "$tmp_dir"/owned-files-0 <(Print0Array system_only_files) | \
12✔
531
                        while read -r -d $'\0' file
13✔
532
                        do
533
                                if [[ "${output_file_props[$file:deleted]:-}" == y ]]
7✔
534
                                then
535
                                        continue # Don't restore files that the user wants deleted
1✔
536
                                fi
537

538
                                files_to_restore+=("$file")
6✔
539
                                system_only_owned_files=$((system_only_owned_files+1))
6✔
540
                        done
541
                LogLeave 'Done (%s system-only owned files).\n' "$(Color G %s $system_only_owned_files)"
12✔
542

543
                LogLeave # Processing system-only files
6✔
544
        fi
545

546
        LogEnter 'Processing deleted files...\n'
41✔
547

548
        if [[ ${#config_only_file_props[@]} != 0 ]]
41✔
549
        then
550
                local key
6✔
551
                for key in "${config_only_file_props[@]}"
33✔
552
                do
553
                        if [[ "$key" == *:deleted ]]
33✔
554
                        then
555
                                local file="${key%:*}"
6✔
556
                                files_to_delete+=("$file")
6✔
557
                                unset "output_file_props[\$key]"
6✔
558
                        fi
559
                done
560
        fi
561

562
        if [[ ${#system_only_file_props[@]} != 0 ]]
41✔
563
        then
564
                local key
3✔
565
                for key in "${system_only_file_props[@]}"
9✔
566
                do
567
                        if [[ "$key" == *:deleted ]]
9✔
568
                        then
569
                                local file="${key%:*}"
3✔
570

571
                                if [[ -h "$output_dir"/files/"$file" || -e "$output_dir"/files/"$file" ]]
5✔
572
                                then
573
                                        # If we are going to replace a deleted file with
574
                                        # one from the configuration, do not attempt to
575
                                        # restore it.
576
                                        :
2✔
577
                                else
578
                                        files_to_restore+=("$file")
1✔
579
                                fi
580

581
                                unset "system_file_props[\$key]"
3✔
582
                        fi
583
                done
584
        fi
585

586
        LogLeave # Processing deleted files
41✔
587

588
        if [[ ${#files_to_delete[@]} != 0 ]]
41✔
589
        then
590
                LogEnter 'Deleting %s files.\n' "$(Color G ${#files_to_delete[@]})"
12✔
591
                printf '%s\0' "${files_to_delete[@]}" | sort --zero-terminated | mapfile -d $'\0' files_to_delete
18✔
592

593
                # shellcheck disable=2059
594
                function Details() {
595
                        Log 'Deleting the following files:\n'
6✔
596
                        printf "$(Color W "*") $(Color C "%s" "%s")\\n" "${files_to_delete[@]}"
18✔
597
                }
598
                Confirm Details
6✔
599

600
                local -A parents=()
12✔
601

602
                # Iterate backwards, so that inner files/directories are
603
                # deleted before their parent ones.
604
                local i
6✔
605
                for (( i=${#files_to_delete[@]}-1 ; i >= 0 ; i-- ))
42✔
606
                do
607
                        local file="${files_to_delete[$i]}"
15✔
608

609
                        if [[ -n "${parents[$file]+x}" && -n "$(sudo find "$file" -maxdepth 0 -type d -not -empty 2>/dev/null)" ]]
29✔
610
                        then
611
                                # Ignoring paths under a directory can cause us to
612
                                # want to remove a directory which will in fact not be
613
                                # empty, and actually contain ignored files. So, skip
614
                                # deleting empty directories which are parents of
615
                                # previously-deleted objects.
616
                                LogEnter 'Skipping non-empty directory %s.\n' "$(Color C "%q" "$file")"
2✔
617
                        else
618
                                LogEnter 'Deleting %s...\n' "$(Color C "%q" "$file")"
28✔
619
                                ParanoidConfirm ''
14✔
620
                                sudo rm --dir "$file"
14✔
621
                        fi
622

623
                        local prop
15✔
624
                        for prop in "${all_file_property_kinds[@]}"
20✔
625
                        do
626
                                local key="$file:$prop"
20✔
627
                                unset "system_file_props[\$key]"
20✔
628
                        done
629

630
                        parents["$(dirname "$file")"]=y
30✔
631

632
                        LogLeave ''
15✔
633
                done
634

635
                modified=y
6✔
636
                LogLeave
6✔
637
        fi
638

639
        if [[ ${#files_to_restore[@]} != 0 ]]
41✔
640
        then
641
                LogEnter 'Restoring %s files.\n' "$(Color G ${#files_to_restore[@]})"
6✔
642
                printf '%s\0' "${files_to_restore[@]}" | sort --zero-terminated | mapfile -d $'\0' files_to_restore
9✔
643

644
                # shellcheck disable=2059
645
                function Details() {
646
                        Log 'Restoring the following files:\n'
3✔
647
                        printf "$(Color W "*") $(Color C "%s" "%s")\\n" "${files_to_restore[@]}"
9✔
648
                }
649
                Confirm Details
3✔
650

651
                # Read file owners
652
                local -A file_owners
3✔
653
                local file
3✔
654
                while read -r -d $'\0' file
79✔
655
                do
656
                        local package
76✔
657
                        read -r -d $'\0' package
76✔
658
                        file_owners[$file]=$package
76✔
659
                done < "$tmp_dir"/file-owners
×
660

661
                for file in "${files_to_restore[@]}"
7✔
662
                do
663
                        local package
7✔
664

665
                        if [[ -n "${file_owners[$file]+x}" ]]
7✔
666
                        then
667
                                package=${file_owners[$file]}
6✔
668
                        else
669
                                package="$( ("$PACMAN" --query --owns --quiet "$file" || true) | head -n 1)"
3✔
670

671
                                if [[ -z "$package" ]]
1✔
672
                                then
673
                                        Log 'Can'\''t find package owning file %s\n' "$(Color C "%q" "$file")"
×
674
                                        Exit 1
×
675
                                fi
676
                        fi
677

678
                        LogEnter 'Restoring %s file %s...\n' "$(Color M "%q" "$package")" "$(Color C "%q" "$file")"
21✔
679
                        function Details() {
680
                                AconfNeedProgram diff diffutils n
1✔
681
                                AconfGetPackageOriginalFile "$package" "$file" | ( "${diff_opts[@]}" --unified <(SuperCat "$file") - || true )
4✔
682
                        }
683
                        if sudo test -f "$file"
7✔
684
                        then
685
                                ParanoidConfirm Details
1✔
686
                        else
687
                                ParanoidConfirm ''
6✔
688
                        fi
689

690
                        AconfRestoreFile "$package" "$file"
7✔
691

692
                        # The file was restored with all of its original properties.
693
                        local prop
7✔
694
                        for prop in owner group mode
21✔
695
                        do
696
                                unset "system_file_props[\$file:\$prop]"
21✔
697
                        done
698

699
                        LogLeave ''
7✔
700
                done
701

702
                modified=y
3✔
703
                LogLeave
3✔
704
        fi
705

706
        #
707
        # Apply remaining file properties
708
        #
709

710
        LogEnter 'Configuring file properties...\n'
41✔
711

712
        AconfCompareFileProps # Update data after ApplyFileProps' unsets
41✔
713

714
        if [[ ${#config_only_file_props[@]} != 0 || ${#changed_file_props[@]} != 0 || ${#system_only_file_props[@]} != 0 ]]
119✔
715
        then
716
                LogEnter 'Found %s new, %s changed, and %s extra files properties.\n'        \
12✔
717
                                 "$(Color G ${#config_only_file_props[@]})"                                                \
12✔
718
                                 "$(Color G ${#changed_file_props[@]})"                                                 \
12✔
719
                                 "$(Color G ${#system_only_file_props[@]})"
12✔
720

12✔
721
                function LogFileProps() {
722
                        local verb="$1"
9✔
723
                        local first=y
9✔
724
                        local key
9✔
725

726
                        while read -r -d $'\0' key
21✔
727
                        do
728
                                if [[ $first == y ]]
12✔
729
                                then
730
                                        LogEnter '%s the following file properties:\n' "$verb"
3✔
731
                                        first=n
3✔
732
                                fi
733

734
                                local kind="${key##*:}"
12✔
735
                                local file="${key%:*}"
12✔
736
                                local value="${output_file_props[$key]:-}"
12✔
737
                                PrintFileProperty "$kind" "$value" "$file"
12✔
738
                        done
739

740
                        if [[ $first == n ]]
9✔
741
                        then
742
                                LogLeave ''
3✔
743
                        fi
744
                }
745

746
                # shellcheck disable=2059
747
                function Details() {
748
                        Print0Array config_only_file_props | LogFileProps "Setting"
6✔
749
                        Print0Array changed_file_props     | LogFileProps "Updating"
6✔
750
                        Print0Array system_only_file_props | LogFileProps "Clearing"
6✔
751
                }
752
                Confirm Details
3✔
753

754
                local key
3✔
755
                ( Print0Array config_only_file_props ; Print0Array changed_file_props ; Print0Array system_only_file_props ) | \
9✔
756
                        while read -r -d $'\0' key
15✔
757
                        do
758
                                local kind="${key##*:}"
12✔
759
                                local file="${key%:*}"
12✔
760
                                local value="${output_file_props[$key]:-}"
12✔
761

762
                                ApplyFileProperty "$kind" "$value" "$file"
12✔
763
                        done
764

765
                modified=y
3✔
766
                LogLeave
3✔
767
        fi
768

769
        LogLeave # Configuring file properties
41✔
770

771
        LogLeave # Configuring files
41✔
772

773
        if [[ $modified == n ]]
41✔
774
        then
775
                LogLeave 'Done (%s).\n' "$(Color G "system state unchanged")"
16✔
776
        else
777
                LogLeave 'Done (%s).\n' "$(Color Y "system state changed")"
66✔
778
        fi
779
}
780

781
: # include in coverage
88✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc