commit c6b7b9067faf1d65cda0d0d8a273d0c9766f6865
parent 8a0fd0b6a5df5ec969fcf1d6301d089f3766727b
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Fri, 16 Aug 2024 13:52:50 +0800
reg-dump: purge command
Purge all registry entries that match the pattern.
Signed-off-by: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Diffstat:
| M | main.go | | | 111 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
1 file changed, 109 insertions(+), 2 deletions(-)
diff --git a/main.go b/main.go
@@ -34,6 +34,8 @@ func main() {
Search(args)
case "show":
Show(args)
+ case "purge":
+ Purge(args)
}
}
@@ -102,7 +104,53 @@ func Search(args []string) {
Pattern: exp,
}
- flowmatic.ManageTasks(-1, walk, manager.Manage, root)
+ flowmatic.ManageTasks(-1, walk, manager.Search, root)
+}
+
+// Purge the registry of all entries that match the pattern.
+func Purge(args []string) {
+ var (
+ log string
+ pattern string
+ root string
+ )
+
+ flags := flag.NewFlagSet("search", flag.ExitOnError)
+ flags.StringVar(&log, "log", "", "log file")
+ flags.StringVar(&pattern, "pattern", "", "regex pattern to match against")
+ flags.StringVar(&root, "path", "", "root search path")
+ flags.Parse(args)
+
+ if pattern == "" {
+ fmt.Println("-pattern is required")
+ os.Exit(1)
+ }
+
+ var exp *regexp.Regexp
+
+ if pattern != "" {
+ exp = regexp.MustCompile(pattern)
+ }
+
+ var output io.Writer
+
+ if log != "" {
+ f, err := os.OpenFile(log, os.O_CREATE|os.O_WRONLY, 0o644)
+ if err != nil {
+ panic(fmt.Errorf("opening log file: %w", err))
+ }
+ defer f.Close()
+ output = f
+ } else {
+ output = os.Stderr
+ }
+
+ manager := Manager{
+ Logger: slog.New(slog.NewTextHandler(output, nil)),
+ Pattern: exp,
+ }
+
+ flowmatic.ManageTasks(-1, walk, manager.Purge, root)
}
type Manager struct {
@@ -115,7 +163,8 @@ type Manager struct {
seen []uint64
}
-func (m *Manager) Manage(path string, subkeys []string, err error) ([]string, bool) {
+// Search outputs matched registry entries.
+func (m *Manager) Search(path string, subkeys []string, err error) ([]string, bool) {
if err != nil {
m.Error("task", path, err)
}
@@ -140,6 +189,64 @@ func (m *Manager) Manage(path string, subkeys []string, err error) ([]string, bo
return subkeys, true
}
+// Purge deletes matched registry entries.
+func (m *Manager) Purge(path string, subkeys []string, err error) ([]string, bool) {
+ if err != nil {
+ m.Error("task", path, err)
+ }
+
+ if m.redundant(path) {
+ m.Warn("skipping", "path", path)
+ return nil, true
+ }
+
+ if m.match(path) && err == nil {
+ if err := m.purge(path); err != nil {
+ m.Error("purge", path, err)
+ }
+ return nil, true
+ }
+
+ for ii, subkey := range subkeys {
+ subkeys[ii] = filepath.Join(path, subkey)
+ }
+
+ return subkeys, true
+}
+
+// purge the given path.
+// If the path contains the pattern, delete the key.
+// Else if the key's values contain the pattern in name or value, delete them.
+func (m *Manager) purge(path string) error {
+ if m.Pattern.MatchString(path) {
+ if err := registry.DeleteKey(registry.CURRENT_USER, path); err != nil {
+ return fmt.Errorf("deleting key: %w", err)
+ }
+ return nil
+ }
+
+ key, err := registry.OpenKey(registry.CURRENT_USER, path, registry.ALL_ACCESS)
+ if err != nil {
+ return fmt.Errorf("opening key with write permissions: %w", err)
+ }
+
+ defer key.Close()
+
+ forEachValue(key, func(name, value string, err error) bool {
+ if err != nil {
+ m.Error("iterating", fmt.Sprintf("%s.%s", path, name), err)
+ }
+ if m.Pattern.MatchString(name) || m.Pattern.MatchString(value) {
+ if err := key.DeleteValue(name); err != nil {
+ m.Error("deleting", fmt.Sprintf("%s.%s", path, name), err)
+ }
+ }
+ return true
+ })
+
+ return nil
+}
+
// redundant is true if the path has already been seen.
//
// The check is done using a binary search over sorted hashes to avoid retaining