From 807b66e246abe3b4578fda744d3483d3fb206f5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Dosko=C4=8Dil?= <jan.doskocil@nic.cz>
Date: Wed, 8 Jan 2025 12:48:04 +0100
Subject: [PATCH] knotc: conf-(import|export) path tab completion

---
 src/utils/knotc/commands.c    | 53 +----------------------------------
 src/utils/knotc/commands.h    | 53 ++++++++++++++++++++++++++++++++++-
 src/utils/knotc/interactive.c | 24 ++++++++++------
 3 files changed, 68 insertions(+), 62 deletions(-)

diff --git a/src/utils/knotc/commands.c b/src/utils/knotc/commands.c
index 146e3efb32..5fe17cf526 100644
--- a/src/utils/knotc/commands.c
+++ b/src/utils/knotc/commands.c
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2025 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -34,57 +34,6 @@
 #include "contrib/openbsd/strlcat.h"
 #include "utils/knotc/commands.h"
 
-#define CMD_EXIT		"exit"
-
-#define CMD_STATUS		"status"
-#define CMD_STOP		"stop"
-#define CMD_RELOAD		"reload"
-#define CMD_STATS		"stats"
-
-#define CMD_ZONE_CHECK		"zone-check"
-#define CMD_ZONE_STATUS		"zone-status"
-#define CMD_ZONE_RELOAD		"zone-reload"
-#define CMD_ZONE_REFRESH	"zone-refresh"
-#define CMD_ZONE_RETRANSFER	"zone-retransfer"
-#define CMD_ZONE_NOTIFY		"zone-notify"
-#define CMD_ZONE_FLUSH		"zone-flush"
-#define CMD_ZONE_BACKUP		"zone-backup"
-#define CMD_ZONE_RESTORE	"zone-restore"
-#define CMD_ZONE_SIGN		"zone-sign"
-#define CMD_ZONE_VALIDATE	"zone-validate"
-#define CMD_ZONE_KEYS_LOAD	"zone-keys-load"
-#define CMD_ZONE_KEY_ROLL	"zone-key-rollover"
-#define CMD_ZONE_KSK_SBM	"zone-ksk-submitted"
-#define CMD_ZONE_FREEZE		"zone-freeze"
-#define CMD_ZONE_THAW		"zone-thaw"
-#define CMD_ZONE_XFR_FREEZE	"zone-xfr-freeze"
-#define CMD_ZONE_XFR_THAW	"zone-xfr-thaw"
-
-#define CMD_ZONE_READ		"zone-read"
-#define CMD_ZONE_BEGIN		"zone-begin"
-#define CMD_ZONE_COMMIT		"zone-commit"
-#define CMD_ZONE_ABORT		"zone-abort"
-#define CMD_ZONE_DIFF		"zone-diff"
-#define CMD_ZONE_GET		"zone-get"
-#define CMD_ZONE_SET		"zone-set"
-#define CMD_ZONE_UNSET		"zone-unset"
-#define CMD_ZONE_PURGE		"zone-purge"
-#define CMD_ZONE_STATS		"zone-stats"
-
-#define CMD_CONF_INIT		"conf-init"
-#define CMD_CONF_CHECK		"conf-check"
-#define CMD_CONF_IMPORT		"conf-import"
-#define CMD_CONF_EXPORT		"conf-export"
-#define CMD_CONF_LIST		"conf-list"
-#define CMD_CONF_READ		"conf-read"
-#define CMD_CONF_BEGIN		"conf-begin"
-#define CMD_CONF_COMMIT		"conf-commit"
-#define CMD_CONF_ABORT		"conf-abort"
-#define CMD_CONF_DIFF		"conf-diff"
-#define CMD_CONF_GET		"conf-get"
-#define CMD_CONF_SET		"conf-set"
-#define CMD_CONF_UNSET		"conf-unset"
-
 #define CTL_LOG_STR		"failed to control"
 
 #define CTL_SEND(type, data) \
diff --git a/src/utils/knotc/commands.h b/src/utils/knotc/commands.h
index fd6f00635a..3464ca4ef5 100644
--- a/src/utils/knotc/commands.h
+++ b/src/utils/knotc/commands.h
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2025 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -19,6 +19,57 @@
 #include "libknot/control/control.h"
 #include "knot/ctl/commands.h"
 
+#define CMD_EXIT		"exit"
+
+#define CMD_STATUS		"status"
+#define CMD_STOP		"stop"
+#define CMD_RELOAD		"reload"
+#define CMD_STATS		"stats"
+
+#define CMD_ZONE_CHECK		"zone-check"
+#define CMD_ZONE_STATUS		"zone-status"
+#define CMD_ZONE_RELOAD		"zone-reload"
+#define CMD_ZONE_REFRESH	"zone-refresh"
+#define CMD_ZONE_RETRANSFER	"zone-retransfer"
+#define CMD_ZONE_NOTIFY		"zone-notify"
+#define CMD_ZONE_FLUSH		"zone-flush"
+#define CMD_ZONE_BACKUP		"zone-backup"
+#define CMD_ZONE_RESTORE	"zone-restore"
+#define CMD_ZONE_SIGN		"zone-sign"
+#define CMD_ZONE_VALIDATE	"zone-validate"
+#define CMD_ZONE_KEYS_LOAD	"zone-keys-load"
+#define CMD_ZONE_KEY_ROLL	"zone-key-rollover"
+#define CMD_ZONE_KSK_SBM	"zone-ksk-submitted"
+#define CMD_ZONE_FREEZE		"zone-freeze"
+#define CMD_ZONE_THAW		"zone-thaw"
+#define CMD_ZONE_XFR_FREEZE	"zone-xfr-freeze"
+#define CMD_ZONE_XFR_THAW	"zone-xfr-thaw"
+
+#define CMD_ZONE_READ		"zone-read"
+#define CMD_ZONE_BEGIN		"zone-begin"
+#define CMD_ZONE_COMMIT		"zone-commit"
+#define CMD_ZONE_ABORT		"zone-abort"
+#define CMD_ZONE_DIFF		"zone-diff"
+#define CMD_ZONE_GET		"zone-get"
+#define CMD_ZONE_SET		"zone-set"
+#define CMD_ZONE_UNSET		"zone-unset"
+#define CMD_ZONE_PURGE		"zone-purge"
+#define CMD_ZONE_STATS		"zone-stats"
+
+#define CMD_CONF_INIT		"conf-init"
+#define CMD_CONF_CHECK		"conf-check"
+#define CMD_CONF_IMPORT		"conf-import"
+#define CMD_CONF_EXPORT		"conf-export"
+#define CMD_CONF_LIST		"conf-list"
+#define CMD_CONF_READ		"conf-read"
+#define CMD_CONF_BEGIN		"conf-begin"
+#define CMD_CONF_COMMIT		"conf-commit"
+#define CMD_CONF_ABORT		"conf-abort"
+#define CMD_CONF_DIFF		"conf-diff"
+#define CMD_CONF_GET		"conf-get"
+#define CMD_CONF_SET		"conf-set"
+#define CMD_CONF_UNSET		"conf-unset"
+
 /*! \brief Command condition flags. */
 typedef enum {
 	CMD_FNONE        = 0,       /*!< Empty flag. */
diff --git a/src/utils/knotc/interactive.c b/src/utils/knotc/interactive.c
index 1ca0c743f0..3fb2ca3126 100644
--- a/src/utils/knotc/interactive.c
+++ b/src/utils/knotc/interactive.c
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2024 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2025 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
 
     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -344,7 +344,7 @@ cmds_lookup_finish:
 	lookup_deinit(&lookup);
 }
 
-static void path_lookup(EditLine *el, const char *str) {
+static void path_lookup(EditLine *el, const char *str, bool dirsonly) {
 	if (str == NULL || *str == '\0') {
 		str = "./";
 	}
@@ -376,11 +376,11 @@ static void path_lookup(EditLine *el, const char *str) {
 
 	for (int i = 0; i < nnames; ++i) {
 		const struct dirent *it = namelist[i];
-		// WARNING: dirent.d_type is not POSIX/SuSv4 standardized, but is present on most
-		// systems. Problematic?
-		if (it->d_type == DT_DIR && strcmp(it->d_name, ".") && strcmp(it->d_name, "..")) {
+		if ((!dirsonly || it->d_type == DT_DIR)
+		    && strcmp(it->d_name, ".")
+		    && strcmp(it->d_name, "..")) {
 			char buf[PATH_MAX + 1];
-			snprintf(buf, PATH_MAX, "%s/", it->d_name);
+			snprintf(buf, PATH_MAX, (it->d_type == DT_DIR) ? "%s/" : "%s", it->d_name);
 			ret = lookup_insert(&lookup, buf, NULL);
 			if (ret != KNOT_EOK) {
 				goto finish1;
@@ -445,8 +445,14 @@ static unsigned char complete(EditLine *el, int ch)
 		goto complete_exit;
 	}
 
-	// Complete filters.
+	// Complete filters and path arguments.
 	if ((desc->flags & CMD_FOPT_FILTER) && token > 0) {
+		if ((!strcmp(CMD_CONF_IMPORT, argv[0]) || !strcmp(CMD_CONF_EXPORT, argv[0]))
+		    && token == 1) {
+			path_lookup(el, argv[token], false);
+			goto complete_exit;
+		}
+
 		if (token < argc && *argv[token] == '+') {
 			dup_check_ctx_t ctx = { &argv[1], token - 1, false };
 			filter_lookup(el, argv[token], desc, &ctx);
@@ -456,14 +462,14 @@ static unsigned char complete(EditLine *el, int ch)
 		switch (desc->cmd) {
 		case CTL_ZONE_FLUSH:
 			if (!strcmp("+outdir", argv[token - 1])) {
-				path_lookup(el, argv[token]);
+				path_lookup(el, argv[token], true);
 				goto complete_exit;
 			}
 			break;
 		case CTL_ZONE_BACKUP:
 		case CTL_ZONE_RESTORE:
 			if (!strcmp("+backupdir", argv[token - 1])) {
-				path_lookup(el, argv[token]);
+				path_lookup(el, argv[token], true);
 				goto complete_exit;
 			}
 			break;
-- 
GitLab