[PATCH nft 4/4] main: use one data-structure to initialize getopt_long(3) arguments and help.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



By generating the getopt_long(3) optstring and options, and the help
from one source, we reduce the chance that they may get out of sync.

Signed-off-by: Jeremy Sowden <jeremy@xxxxxxxxxx>
---
 src/main.c | 251 +++++++++++++++++++++++++++++------------------------
 1 file changed, 138 insertions(+), 113 deletions(-)

diff --git a/src/main.c b/src/main.c
index 4e9a2ed3bcec..1ee4d957a152 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,6 +24,19 @@
 
 static struct nft_ctx *nft;
 
+/*
+ * These options are grouped separately in the help, so we give them named
+ * indices for use there.
+ */
+enum opt_indices {
+	IDX_HELP,
+	IDX_VERSION,
+	IDX_VERSION_LONG,
+	IDX_CHECK,
+	IDX_FILE,
+	IDX_INTERACTIVE,
+};
+
 enum opt_vals {
 	OPT_HELP		= 'h',
 	OPT_VERSION		= 'v',
@@ -47,123 +60,133 @@ enum opt_vals {
 	OPT_TERSE		= 't',
 	OPT_INVALID		= '?',
 };
-#define OPTSTRING	"+hvVcf:iI:jnsNSd:aeuypTt"
 
-static const struct option options[] = {
-	{
-		.name		= "help",
-		.val		= OPT_HELP,
-	},
-	{
-		.name		= "version",
-		.val		= OPT_VERSION,
-	},
-	{
-		.name		= "check",
-		.val		= OPT_CHECK,
-	},
-	{
-		.name		= "file",
-		.val		= OPT_FILE,
-		.has_arg	= 1,
-	},
-	{
-		.name		= "interactive",
-		.val		= OPT_INTERACTIVE,
-	},
-	{
-		.name		= "numeric",
-		.val		= OPT_NUMERIC,
-	},
-	{
-		.name		= "stateless",
-		.val		= OPT_STATELESS,
-	},
-	{
-		.name		= "reversedns",
-		.val		= OPT_IP2NAME,
-	},
-	{
-		.name		= "service",
-		.val		= OPT_SERVICE,
-	},
-	{
-		.name		= "includepath",
-		.val		= OPT_INCLUDEPATH,
-		.has_arg	= 1,
-	},
-	{
-		.name		= "debug",
-		.val		= OPT_DEBUG,
-		.has_arg	= 1,
-	},
-	{
-		.name		= "handle",
-		.val		= OPT_HANDLE_OUTPUT,
-	},
-	{
-		.name		= "echo",
-		.val		= OPT_ECHO,
-	},
-	{
-		.name		= "json",
-		.val		= OPT_JSON,
-	},
-	{
-		.name		= "guid",
-		.val		= OPT_GUID,
-	},
-	{
-		.name		= "numeric-priority",
-		.val		= OPT_NUMERIC_PRIO,
-	},
-	{
-		.name		= "numeric-protocol",
-		.val		= OPT_NUMERIC_PROTO,
-	},
-	{
-		.name		= "numeric-time",
-		.val		= OPT_NUMERIC_TIME,
-	},
-	{
-		.name		= "terse",
-		.val		= OPT_TERSE,
-	},
-	{
-		.name		= NULL
-	}
+struct nft_opt {
+	const char    *name;
+	enum opt_vals  val;
+	const char    *arg;
+	const char    *help;
+};
+
+#define NFT_OPT(n, v, a, h) \
+	(struct nft_opt) { .name = n, .val = v, .arg = a, .help = h }
+
+static const struct nft_opt nft_options[] = {
+	NFT_OPT("help",			OPT_HELP,		NULL,
+		"Show this help"),
+	NFT_OPT("version",		OPT_VERSION,		NULL,
+		"Show version information"),
+	NFT_OPT(NULL,                   OPT_VERSION_LONG,       NULL,
+		"Show extended version information"),
+	NFT_OPT("check",		OPT_CHECK,		NULL,
+		"Check commands validity without actually applying the changes."),
+	NFT_OPT("file",			OPT_FILE,		"<filename>",
+		"Read input from <filename>"),
+	NFT_OPT("interactive",		OPT_INTERACTIVE,	NULL,
+		"Read input from interactive CLI"),
+	NFT_OPT("numeric",		OPT_NUMERIC,		NULL,
+		"Print fully numerical output."),
+	NFT_OPT("stateless",		OPT_STATELESS,		NULL,
+		"Omit stateful information of ruleset."),
+	NFT_OPT("reversedns",		OPT_IP2NAME,		NULL,
+		"Translate IP addresses to names."),
+	NFT_OPT("service",		OPT_SERVICE,		NULL,
+		"Translate ports to service names as described in /etc/services."),
+	NFT_OPT("includepath",		OPT_INCLUDEPATH,	"<directory>",
+		"Add <directory> to the paths searched for include files. Default is: " DEFAULT_INCLUDE_PATH),
+	NFT_OPT("debug",		OPT_DEBUG,		"<level [,level...]>",
+		"Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)"),
+	NFT_OPT("handle",		OPT_HANDLE_OUTPUT,	NULL,
+		"Output rule handle."),
+	NFT_OPT("echo",			OPT_ECHO,		NULL,
+		"Echo what has been added, inserted or replaced."),
+	NFT_OPT("json",			OPT_JSON,		NULL,
+		"Format output in JSON"),
+	NFT_OPT("guid",			OPT_GUID,		NULL,
+		"Print UID/GID as defined in /etc/passwd and /etc/group."),
+	NFT_OPT("numeric-priority",	OPT_NUMERIC_PRIO,	NULL,
+		"Print chain priority numerically."),
+	NFT_OPT("numeric-protocol",	OPT_NUMERIC_PROTO,	NULL,
+		"Print layer 4 protocols numerically."),
+	NFT_OPT("numeric-time",		OPT_NUMERIC_TIME,	NULL,
+		"Print time values numerically."),
+	NFT_OPT("terse",		OPT_TERSE,		NULL,
+		"Omit contents of sets."),
 };
 
+#define NR_NFT_OPTIONS (sizeof(nft_options) / sizeof(nft_options[0]))
+
+static const char *get_optstring(void)
+{
+	static char optstring[NR_NFT_OPTIONS + 2];
+
+	if (!optstring[0]) {
+		size_t i, j;
+
+		optstring[0] = '+';
+		for (i = 0, j = 1; i < NR_NFT_OPTIONS; i++)
+			j += sprintf(optstring + j, "%c%s",
+				     nft_options[i].val,
+				     nft_options[i].arg ? ":" : "");
+	}
+	return optstring;
+}
+
+static const struct option *get_options(void)
+{
+	static struct option options[NR_NFT_OPTIONS + 1];
+
+	if (!options[0].name) {
+		size_t i, j;
+
+		for (i = 0, j = 0; i < NR_NFT_OPTIONS; ++i) {
+			if (nft_options[i].name) {
+				options[j].name    = nft_options[i].name;
+				options[j].val     = nft_options[i].val;
+				options[j].has_arg = nft_options[i].arg != NULL;
+				j++;
+			}
+		}
+	}
+	return options;
+}
+
+static void print_option(const struct nft_opt *opt)
+{
+	char optbuf[33] = "";
+	int i;
+
+	i = snprintf(optbuf, sizeof(optbuf), "  -%c", opt->val);
+	if (opt->name)
+		i += snprintf(optbuf + i, sizeof(optbuf) - i, ", %s", opt->name);
+	if (opt->arg)
+		i += snprintf(optbuf + i, sizeof(optbuf) - i, " %s", opt->arg);
+
+	printf("%-32s%s\n", optbuf, opt->help);
+}
+
 static void show_help(const char *name)
 {
-	printf(
-"Usage: %s [ options ] [ cmds... ]\n"
-"\n"
-"Options:\n"
-"  -h, --help				Show this help\n"
-"  -v, --version				Show version information\n"
-"  -V					Show extended version information\n"
-"\n"
-"  -c, --check				Check commands validity without actually applying the changes.\n"
-"  -f, --file <filename>			Read input from <filename>\n"
-"  -i, --interactive			Read input from interactive CLI\n"
-"\n"
-"  -j, --json				Format output in JSON\n"
-"  -n, --numeric				Print fully numerical output.\n"
-"  -s, --stateless			Omit stateful information of ruleset.\n"
-"  -t, --terse				Omit contents of sets.\n"
-"  -u, --guid				Print UID/GID as defined in /etc/passwd and /etc/group.\n"
-"  -N, --reversedns			Translate IP addresses to names.\n"
-"  -S, --service				Translate ports to service names as described in /etc/services.\n"
-"  -p, --numeric-protocol		Print layer 4 protocols numerically.\n"
-"  -y, --numeric-priority		Print chain priority numerically.\n"
-"  -T, --numeric-time			Print time values numerically.\n"
-"  -a, --handle				Output rule handle.\n"
-"  -e, --echo				Echo what has been added, inserted or replaced.\n"
-"  -I, --includepath <directory>		Add <directory> to the paths searched for include files. Default is: " DEFAULT_INCLUDE_PATH "\n"
-"  -d, --debug <level [,level...]>	Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)\n"
-"\n",
-	name);
+	printf("Usage: %s [ options ] [ cmds... ]\n"
+	       "\n"
+	       "Options:\n", name);
+
+	print_option(&nft_options[IDX_HELP]);
+	print_option(&nft_options[IDX_VERSION]);
+	print_option(&nft_options[IDX_VERSION_LONG]);
+
+	fputs("\n", stdout);
+
+	print_option(&nft_options[IDX_CHECK]);
+	print_option(&nft_options[IDX_FILE]);
+	print_option(&nft_options[IDX_INTERACTIVE]);
+
+	fputs("\n", stdout);
+
+	for (size_t i = IDX_INTERACTIVE + 1; i < NR_NFT_OPTIONS; ++i)
+		print_option(&nft_options[i]);
+
+	fputs("\n", stdout);
 }
 
 static void show_version(void)
@@ -288,6 +311,8 @@ static bool nft_options_check(int argc, char * const argv[])
 
 int main(int argc, char * const *argv)
 {
+	const struct option *options = get_options();
+	const char *optstring = get_optstring();
 	char *buf = NULL, *filename = NULL;
 	unsigned int output_flags = 0;
 	bool interactive = false;
@@ -301,7 +326,7 @@ int main(int argc, char * const *argv)
 	nft = nft_ctx_new(NFT_CTX_DEFAULT);
 
 	while (1) {
-		val = getopt_long(argc, argv, OPTSTRING, options, NULL);
+		val = getopt_long(argc, argv, optstring, options, NULL);
 		if (val == -1)
 			break;
 
-- 
2.25.1




[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux