From 977a6f7fad8d2985e8e898bd8dc7d3d376164e9d Mon Sep 17 00:00:00 2001
From: Amitay Isaacs <amitay@gmail.com>
Date: Mon, 11 Nov 2019 17:29:26 +1100
Subject: [PATCH] ctdb-common: Change cmdline implementation to support
 multiple sections

Signed-off-by: Amitay Isaacs <amitay@gmail.com>
Reviewed-by: Martin Schwenke <martin@meltin.net>
---
 ctdb/common/cmdline.c | 134 +++++++++++++++++++++++++++++++-----------
 1 file changed, 101 insertions(+), 33 deletions(-)

diff --git a/ctdb/common/cmdline.c b/ctdb/common/cmdline.c
index eb5361cf61ab..0ce49b8311a3 100644
--- a/ctdb/common/cmdline.c
+++ b/ctdb/common/cmdline.c
@@ -29,11 +29,16 @@
 
 #define CMDLINE_MAX_LEN		80
 
+struct cmdline_section {
+	const char *name;
+	struct cmdline_command *commands;
+};
+
 struct cmdline_context {
 	const char *prog;
 	struct poptOption *options;
-	const char *section;
-	struct cmdline_command *commands;
+	struct cmdline_section *section;
+	int num_sections;
 	int max_len;
 	poptContext pc;
 	int argc, arg0;
@@ -207,16 +212,51 @@ static bool cmdline_commands_check(struct cmdline_command *commands,
 
 static int cmdline_context_destructor(struct cmdline_context *cmdline);
 
+static int cmdline_section_add(struct cmdline_context *cmdline,
+			       const char *name,
+			       struct cmdline_command *commands)
+{
+	struct cmdline_section *section;
+	size_t max_len = 0;
+	bool ok;
+
+	ok = cmdline_commands_check(commands, &max_len);
+	if (!ok) {
+		return EINVAL;
+	}
+
+	section = talloc_realloc(cmdline,
+				 cmdline->section,
+				 struct cmdline_section,
+				 cmdline->num_sections + 1);
+	if (section == NULL) {
+		return ENOMEM;
+	}
+
+	section[cmdline->num_sections] = (struct cmdline_section) {
+		.name = name,
+		.commands = commands,
+	};
+
+	if (max_len > cmdline->max_len) {
+		cmdline->max_len = max_len;
+	}
+
+	cmdline->section = section;
+	cmdline->num_sections += 1;
+
+	return 0;
+}
+
 int cmdline_init(TALLOC_CTX *mem_ctx,
 		 const char *prog,
 		 struct poptOption *options,
-		 const char *section,
+		 const char *name,
 		 struct cmdline_command *commands,
 		 struct cmdline_context **result)
 {
 	struct cmdline_context *cmdline;
 	int ret;
-	size_t max_len = 0;
 	bool ok;
 
 	if (prog == NULL) {
@@ -228,11 +268,6 @@ int cmdline_init(TALLOC_CTX *mem_ctx,
 		return EINVAL;
 	}
 
-	ok = cmdline_commands_check(commands, &max_len);
-	if (!ok) {
-		return EINVAL;
-	}
-
 	cmdline = talloc_zero(mem_ctx, struct  cmdline_context);
 	if (cmdline == NULL) {
 		return ENOMEM;
@@ -249,9 +284,12 @@ int cmdline_init(TALLOC_CTX *mem_ctx,
 		talloc_free(cmdline);
 		return ret;
 	}
-	cmdline->section = section;
-	cmdline->commands = commands;
-	cmdline->max_len = max_len;
+
+	ret = cmdline_section_add(cmdline, name, commands);
+	if (ret != 0) {
+		talloc_free(cmdline);
+		return ret;
+	}
 
 	cmdline->argc = 1;
 	cmdline->argv = talloc_array(cmdline, const char *, 2);
@@ -326,16 +364,12 @@ static int cmdline_parse_options(struct cmdline_context *cmdline,
 	return 0;
 }
 
-static int cmdline_match(struct cmdline_context *cmdline)
+static int cmdline_match_section(struct cmdline_context *cmdline,
+				 struct cmdline_section *section)
 {
 	int i;
 
-	if (cmdline->argc == 0 || cmdline->argv == NULL) {
-		cmdline->match_cmd = NULL;
-		return EINVAL;
-	}
-
-	for (i=0; cmdline->commands[i].name != NULL; i++) {
+	for (i=0; section->commands[i].name != NULL; i++) {
 		struct cmdline_command *cmd;
 		char name[CMDLINE_MAX_LEN+1];
 		size_t len;
@@ -343,7 +377,7 @@ static int cmdline_match(struct cmdline_context *cmdline)
 		int n = 0;
 		bool match = false;
 
-		cmd = &cmdline->commands[i];
+		cmd = &section->commands[i];
 		len = strlcpy(name, cmd->name, sizeof(name));
 		if (len >= sizeof(name)) {
 			D_ERR("Skipping long command '%s'\n", cmd->name);
@@ -382,6 +416,25 @@ static int cmdline_match(struct cmdline_context *cmdline)
 	return ENOENT;
 }
 
+static int cmdline_match(struct cmdline_context *cmdline)
+{
+	int i, ret = ENOENT;
+
+	if (cmdline->argc == 0 || cmdline->argv == NULL) {
+		cmdline->match_cmd = NULL;
+		return EINVAL;
+	}
+
+	for (i=0; i<cmdline->num_sections; i++) {
+		ret = cmdline_match_section(cmdline, &cmdline->section[i]);
+		if (ret == 0) {
+			break;
+		}
+	}
+
+	return ret;
+}
+
 int cmdline_parse(struct cmdline_context *cmdline,
 		  int argc,
 		  const char **argv,
@@ -445,38 +498,53 @@ static void cmdline_usage_command(struct cmdline_context *cmdline,
 	printf("     %s\n", cmd->msg_help);
 }
 
-static void cmdline_usage_full(struct cmdline_context *cmdline)
+static void cmdline_usage_section(struct cmdline_context *cmdline,
+				  struct cmdline_section *section)
 {
 	int i;
 
-	poptSetOtherOptionHelp(cmdline->pc, "[<options>] <command> [<args>]");
-	poptPrintHelp(cmdline->pc, stdout, 0);
-
 	printf("\n");
-	if (cmdline->section != NULL) {
-		printf("%s ", cmdline->section);
+
+	if (section->name != NULL) {
+		printf("%s ", section->name);
 	}
 	printf("Commands:\n");
-	for (i=0; cmdline->commands[i].name != NULL; i++) {
-		cmdline_usage_command(cmdline, &cmdline->commands[i], true);
+	for (i=0; section->commands[i].name != NULL; i++) {
+		cmdline_usage_command(cmdline, &section->commands[i], true);
 
 	}
 }
 
+static void cmdline_usage_full(struct cmdline_context *cmdline)
+{
+	int i;
+
+	poptSetOtherOptionHelp(cmdline->pc, "[<options>] <command> [<args>]");
+	poptPrintHelp(cmdline->pc, stdout, 0);
+
+	for (i=0; i<cmdline->num_sections; i++) {
+		cmdline_usage_section(cmdline, &cmdline->section[i]);
+	}
+}
+
 void cmdline_usage(struct cmdline_context *cmdline, const char *cmd_name)
 {
 	struct cmdline_command *cmd = NULL;
-	int i;
+	int i, j;
 
 	if (cmd_name == NULL) {
 		cmdline_usage_full(cmdline);
 		return;
 	}
 
-	for (i=0; cmdline->commands[i].name != NULL; i++) {
-		if (strcmp(cmdline->commands[i].name, cmd_name) == 0) {
-			cmd = &cmdline->commands[i];
-			break;
+	for (j=0; j<cmdline->num_sections; j++) {
+		struct cmdline_section *section = &cmdline->section[j];
+
+		for (i=0; section->commands[i].name != NULL; i++) {
+			if (strcmp(section->commands[i].name, cmd_name) == 0) {
+				cmd = &section->commands[i];
+				break;
+			}
 		}
 	}