diff options
| author | Přemysl Janouch <p.janouch@gmail.com> | 2015-02-11 02:07:52 +0100 | 
|---|---|---|
| committer | Přemysl Janouch <p.janouch@gmail.com> | 2015-02-11 02:07:52 +0100 | 
| commit | cd1a55a0d1f77aedb078ab9fef96dac94b41c86b (patch) | |
| tree | 8618c6f854a0f19c87590bdb93f96a4c83566c62 | |
| parent | 59b1c5c056269a5151ef7a17cdedf8a3caccfc61 (diff) | |
| download | xK-cd1a55a0d1f77aedb078ab9fef96dac94b41c86b.tar.gz xK-cd1a55a0d1f77aedb078ab9fef96dac94b41c86b.tar.xz xK-cd1a55a0d1f77aedb078ab9fef96dac94b41c86b.zip | |
Import option handler from ponymap
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | common.c | 149 | ||||
| -rw-r--r-- | kike.c | 82 | ||||
| -rw-r--r-- | zyklonb.c | 81 | 
4 files changed, 216 insertions, 98 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index 077b109..7b0c049 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,7 +69,7 @@ foreach (page zyklonb kike)  	set (page_output "${PROJECT_BINARY_DIR}/${page}.1")  	list (APPEND project_MAN_PAGES "${page_output}")  	add_custom_command (OUTPUT ${page_output} -		COMMAND ${HELP2MAN_EXECUTABLE} -N --no-discard-stderr # FIXME +		COMMAND ${HELP2MAN_EXECUTABLE} -N  			"${PROJECT_BINARY_DIR}/${page}" -o ${page_output}  		DEPENDS ${page}  		COMMENT "Generating man page for ${page}" VERBATIM) @@ -1995,3 +1995,152 @@ call_write_default_config (const char *hint, const struct config_item *table)  	print_status ("configuration written to `%s'", filename);  	free (filename);  } + +// --- Option handler ---------------------------------------------------------- + +// Simple wrapper for the getopt_long API to make it easier to use and maintain. + +#define OPT_USAGE_ALIGNMENT_COLUMN 30   ///< Alignment for option descriptions + +enum +{ +	OPT_OPTIONAL_ARG  = (1 << 0),       ///< The argument is optional +	OPT_LONG_ONLY     = (1 << 1)        ///< Ignore the short name in opt_string +}; + +// All options need to have both a short name, and a long name.  The short name +// is what is returned from opt_handler_get().  It is possible to define a value +// completely out of the character range combined with the OPT_LONG_ONLY flag. +// +// When `arg_hint' is defined, the option is assumed to have an argument. + +struct opt +{ +	int short_name;                     ///< The single-letter name +	const char *long_name;              ///< The long name +	const char *arg_hint;               ///< Option argument hint +	int flags;                          ///< Option flags +	const char *description;            ///< Option description +}; + +struct opt_handler +{ +	int argc;                           ///< The number of program arguments +	char **argv;                        ///< Program arguments + +	const char *arg_hint;               ///< Program arguments hint +	const char *description;            ///< Description of the program + +	const struct opt *opts;             ///< The list of options +	size_t opts_len;                    ///< The length of the option array + +	struct option *options;             ///< The list of options for getopt +	char *opt_string;                   ///< The `optstring' for getopt +}; + +static void +opt_handler_free (struct opt_handler *self) +{ +	free (self->options); +	free (self->opt_string); +} + +static void +opt_handler_init (struct opt_handler *self, int argc, char **argv, +	const struct opt *opts, const char *arg_hint, const char *description) +{ +	memset (self, 0, sizeof *self); +	self->argc = argc; +	self->argv = argv; +	self->arg_hint = arg_hint; +	self->description = description; + +	size_t len = 0; +	for (const struct opt *iter = opts; iter->long_name; iter++) +		len++; + +	self->opts = opts; +	self->opts_len = len; +	self->options = xcalloc (len + 1, sizeof *self->options); + +	struct str opt_string; +	str_init (&opt_string); + +	for (size_t i = 0; i < len; i++) +	{ +		const struct opt *opt = opts + i; +		struct option *mapped = self->options + i; + +		mapped->name = opt->long_name; +		if (!opt->arg_hint) +			mapped->has_arg = no_argument; +		else if (opt->flags & OPT_OPTIONAL_ARG) +			mapped->has_arg = optional_argument; +		else +			mapped->has_arg = required_argument; +		mapped->val = opt->short_name; + +		if (opt->flags & OPT_LONG_ONLY) +			continue; + +		str_append_c (&opt_string, opt->short_name); +		if (opt->arg_hint) +		{ +			str_append_c (&opt_string, ':'); +			if (opt->flags & OPT_OPTIONAL_ARG) +				str_append_c (&opt_string, ':'); +		} +	} + +	self->opt_string = str_steal (&opt_string); +} + +static void +opt_handler_usage (struct opt_handler *self, FILE *stream) +{ +	struct str usage; +	str_init (&usage); + +	str_append_printf (&usage, "Usage: %s [OPTION]... %s\n", +		self->argv[0], self->arg_hint ? self->arg_hint : ""); +	str_append_printf (&usage, "%s\n\n", self->description); + +	for (size_t i = 0; i < self->opts_len; i++) +	{ +		struct str row; +		str_init (&row); + +		const struct opt *opt = self->opts + i; +		if (!(opt->flags & OPT_LONG_ONLY)) +			str_append_printf (&row, "  -%c, ", opt->short_name); +		else +			str_append (&row, "      "); +		str_append_printf (&row, "--%s", opt->long_name); +		if (opt->arg_hint) +			str_append_printf (&row, (opt->flags & OPT_OPTIONAL_ARG) +				? " [%s]" : " %s", opt->arg_hint); + +		// TODO: keep the indent if there are multiple lines +		if (row.len + 2 <= OPT_USAGE_ALIGNMENT_COLUMN) +		{ +			str_append (&row, "  "); +			str_append_printf (&usage, "%-*s%s\n", +				OPT_USAGE_ALIGNMENT_COLUMN, row.str, opt->description); +		} +		else +			str_append_printf (&usage, "%s\n%-*s%s\n", row.str, +				OPT_USAGE_ALIGNMENT_COLUMN, "", opt->description); + +		str_free (&row); +	} + +	fputs (usage.str, stream); +	str_free (&usage); +} + +static int +opt_handler_get (struct opt_handler *self) +{ +	return getopt_long (self->argc, self->argv, +		self->opt_string, self->options, NULL); +} @@ -1,7 +1,7 @@  /*   * kike.c: the experimental IRC daemon   * - * Copyright (c) 2014, Přemysl Janouch <p.janouch@gmail.com> + * Copyright (c) 2014 - 2015, Přemysl Janouch <p.janouch@gmail.com>   * All rights reserved.   *   * Permission to use, copy, modify, and/or distribute this software for any @@ -3072,63 +3072,47 @@ daemonize (void)  		exit_fatal ("failed to reopen FD's: %s", strerror (errno));  } -static void -print_usage (const char *program_name) -{ -	fprintf (stderr, -		"Usage: %s [OPTION]...\n" -		"Experimental IRC server.\n" -		"\n" -		"  -d, --debug     run in debug mode (do not daemonize)\n" -		"  -h, --help      display this help and exit\n" -		"  -V, --version   output version information and exit\n" -		"  --write-default-cfg [filename]\n" -		"                  write a default configuration file and exit\n", -		program_name); -} -  int  main (int argc, char *argv[])  { -	const char *invocation_name = argv[0]; - -	static struct option opts[] = +	static const struct opt opts[] =  	{ -		{ "debug",             no_argument,       NULL, 'd' }, -		{ "help",              no_argument,       NULL, 'h' }, -		{ "version",           no_argument,       NULL, 'V' }, -		{ "write-default-cfg", optional_argument, NULL, 'w' }, -		{ NULL,                0,                 NULL,  0  } +		{ 'd', "debug", NULL, 0, "run in debug mode (do not daemonize)" }, +		{ 'h', "help", NULL, 0, "display this help and exit" }, +		{ 'V', "version", NULL, 0, "output version information and exit" }, +		{ 'w', "write-default-cfg", "FILENAME", +		  OPT_OPTIONAL_ARG | OPT_LONG_ONLY, +		  "write a default configuration file and exit" }, +		{ 0, NULL, NULL, 0, NULL }  	}; -	while (1) -	{ -		int c, opt_index; +	struct opt_handler oh; +	opt_handler_init (&oh, argc, argv, opts, NULL, "Experimental IRC daemon."); -		c = getopt_long (argc, argv, "dhV", opts, &opt_index); -		if (c == -1) -			break; - -		switch (c) -		{ -		case 'd': -			g_debug_mode = true; -			break; -		case 'h': -			print_usage (invocation_name); -			exit (EXIT_SUCCESS); -		case 'V': -			printf (PROGRAM_NAME " " PROGRAM_VERSION "\n"); -			exit (EXIT_SUCCESS); -		case 'w': -			call_write_default_config (optarg, g_config_table); -			exit (EXIT_SUCCESS); -		default: -			print_error ("wrong options"); -			exit (EXIT_FAILURE); -		} +	int c; +	while ((c = opt_handler_get (&oh)) != -1) +	switch (c) +	{ +	case 'd': +		g_debug_mode = true; +		break; +	case 'h': +		opt_handler_usage (&oh, stdout); +		exit (EXIT_SUCCESS); +	case 'V': +		printf (PROGRAM_NAME " " PROGRAM_VERSION "\n"); +		exit (EXIT_SUCCESS); +	case 'w': +		call_write_default_config (optarg, g_config_table); +		exit (EXIT_SUCCESS); +	default: +		print_error ("wrong options"); +		opt_handler_usage (&oh, stderr); +		exit (EXIT_FAILURE);  	} +	opt_handler_free (&oh); +  	print_status (PROGRAM_NAME " " PROGRAM_VERSION " starting");  	setup_signal_handlers (); @@ -1,7 +1,7 @@  /*   * zyklonb.c: the experimental IRC bot   * - * Copyright (c) 2014, Přemysl Janouch <p.janouch@gmail.com> + * Copyright (c) 2014 - 2015, Přemysl Janouch <p.janouch@gmail.com>   * All rights reserved.   *   * Permission to use, copy, modify, and/or distribute this software for any @@ -2150,65 +2150,50 @@ on_signal_pipe_readable (const struct pollfd *fd, struct bot_context *ctx)  	}  } -static void -print_usage (const char *program_name) -{ -	fprintf (stderr, -		"Usage: %s [OPTION]...\n" -		"Experimental IRC bot.\n" -		"\n" -		"  -d, --debug     run in debug mode\n" -		"  -h, --help      display this help and exit\n" -		"  -V, --version   output version information and exit\n" -		"  --write-default-cfg [filename]\n" -		"                  write a default configuration file and exit\n", -		program_name); -} -  int  main (int argc, char *argv[])  { -	const char *invocation_name = argv[0];  	str_vector_init (&g_original_argv);  	str_vector_add_vector (&g_original_argv, argv); -	static struct option opts[] = +	static const struct opt opts[] =  	{ -		{ "debug",             no_argument,       NULL, 'd' }, -		{ "help",              no_argument,       NULL, 'h' }, -		{ "version",           no_argument,       NULL, 'V' }, -		{ "write-default-cfg", optional_argument, NULL, 'w' }, -		{ NULL,                0,                 NULL,  0  } +		{ 'd', "debug", NULL, 0, "run in debug mode" }, +		{ 'h', "help", NULL, 0, "display this help and exit" }, +		{ 'V', "version", NULL, 0, "output version information and exit" }, +		{ 'w', "write-default-cfg", "FILENAME", +		  OPT_OPTIONAL_ARG | OPT_LONG_ONLY, +		  "write a default configuration file and exit" }, +		{ 0, NULL, NULL, 0, NULL }  	}; -	while (1) -	{ -		int c, opt_index; - -		c = getopt_long (argc, argv, "dhV", opts, &opt_index); -		if (c == -1) -			break; +	struct opt_handler oh; +	opt_handler_init (&oh, argc, argv, opts, NULL, "Experimental IRC bot."); -		switch (c) -		{ -		case 'd': -			g_debug_mode = true; -			break; -		case 'h': -			print_usage (invocation_name); -			exit (EXIT_SUCCESS); -		case 'V': -			printf (PROGRAM_NAME " " PROGRAM_VERSION "\n"); -			exit (EXIT_SUCCESS); -		case 'w': -			call_write_default_config (optarg, g_config_table); -			exit (EXIT_SUCCESS); -		default: -			print_error ("wrong options"); -			exit (EXIT_FAILURE); -		} +	int c; +	while ((c = opt_handler_get (&oh)) != -1) +	switch (c) +	{ +	case 'd': +		g_debug_mode = true; +		break; +	case 'h': +		opt_handler_usage (&oh, stdout); +		exit (EXIT_SUCCESS); +	case 'V': +		printf (PROGRAM_NAME " " PROGRAM_VERSION "\n"); +		exit (EXIT_SUCCESS); +	case 'w': +		call_write_default_config (optarg, g_config_table); +		exit (EXIT_SUCCESS); +	default: +		print_error ("wrong options"); +		opt_handler_usage (&oh, stderr); +		exit (EXIT_FAILURE);  	} +	opt_handler_free (&oh); +  	print_status (PROGRAM_NAME " " PROGRAM_VERSION " starting");  	setup_signal_handlers (); | 
