Blame common/tool.c

Packit Service 3749ba
/*
Packit Service 3749ba
 * Copyright (c) 2011, Collabora Ltd.
Packit Service 3749ba
 *
Packit Service 3749ba
 * Redistribution and use in source and binary forms, with or without
Packit Service 3749ba
 * modification, are permitted provided that the following conditions
Packit Service 3749ba
 * are met:
Packit Service 3749ba
 *
Packit Service 3749ba
 *     * Redistributions of source code must retain the above
Packit Service 3749ba
 *       copyright notice, this list of conditions and the
Packit Service 3749ba
 *       following disclaimer.
Packit Service 3749ba
 *     * Redistributions in binary form must reproduce the
Packit Service 3749ba
 *       above copyright notice, this list of conditions and
Packit Service 3749ba
 *       the following disclaimer in the documentation and/or
Packit Service 3749ba
 *       other materials provided with the distribution.
Packit Service 3749ba
 *     * The names of contributors to this software may not be
Packit Service 3749ba
 *       used to endorse or promote products derived from this
Packit Service 3749ba
 *       software without specific prior written permission.
Packit Service 3749ba
 *
Packit Service 3749ba
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit Service 3749ba
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit Service 3749ba
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
Packit Service 3749ba
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
Packit Service 3749ba
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit Service 3749ba
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
Packit Service 3749ba
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
Packit Service 3749ba
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
Packit Service 3749ba
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
Packit Service 3749ba
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
Packit Service 3749ba
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
Packit Service 3749ba
 * DAMAGE.
Packit Service 3749ba
 *
Packit Service 3749ba
 * Author: Stef Walter <stefw@collabora.co.uk>
Packit Service 3749ba
 */
Packit Service 3749ba
Packit Service 3749ba
#include "config.h"
Packit Service 3749ba
Packit Service 3749ba
#include "buffer.h"
Packit Service 3749ba
#include "compat.h"
Packit Service 3749ba
#include "debug.h"
Packit Service 3749ba
#include "message.h"
Packit Service 3749ba
#include "path.h"
Packit Service 3749ba
Packit Service 3749ba
#include <assert.h>
Packit Service 3749ba
#include <ctype.h>
Packit Service 3749ba
#include <getopt.h>
Packit Service 3749ba
#include <string.h>
Packit Service 3749ba
#include <stdio.h>
Packit Service 3749ba
#include <stdlib.h>
Packit Service 3749ba
#include <unistd.h>
Packit Service 3749ba
Packit Service 3749ba
#include "tool.h"
Packit Service 3749ba
Packit Service 3749ba
static char
Packit Service 3749ba
short_option (int opt)
Packit Service 3749ba
{
Packit Service 3749ba
	if (isalpha (opt) || isdigit (opt))
Packit Service 3749ba
		return (char)opt;
Packit Service 3749ba
	return 0;
Packit Service 3749ba
}
Packit Service 3749ba
Packit Service 3749ba
static const struct option *
Packit Service 3749ba
find_option (const struct option *longopts,
Packit Service 3749ba
             int opt)
Packit Service 3749ba
{
Packit Service 3749ba
	int i;
Packit Service 3749ba
Packit Service 3749ba
	for (i = 0; longopts[i].name != NULL; i++) {
Packit Service 3749ba
		if (longopts[i].val == opt)
Packit Service 3749ba
			return longopts + i;
Packit Service 3749ba
	}
Packit Service 3749ba
Packit Service 3749ba
	return NULL;
Packit Service 3749ba
}
Packit Service 3749ba
Packit Service 3749ba
void
Packit Service 3749ba
p11_tool_usage (const p11_tool_desc *usages,
Packit Service 3749ba
                const struct option *longopts)
Packit Service 3749ba
{
Packit Service 3749ba
	const struct option *longopt;
Packit Service 3749ba
	const int indent = 22;
Packit Service 3749ba
	const char *long_name;
Packit Service 3749ba
	const char *description;
Packit Service 3749ba
	const char *next;
Packit Service 3749ba
	char short_name;
Packit Service 3749ba
	int spaces;
Packit Service 3749ba
	int len;
Packit Service 3749ba
	int i;
Packit Service 3749ba
Packit Service 3749ba
	for (i = 0; usages[i].text != NULL; i++) {
Packit Service 3749ba
Packit Service 3749ba
		/* If no option, then this is a heading */
Packit Service 3749ba
		if (!usages[i].option) {
Packit Service 3749ba
			printf ("%s\n\n", usages[i].text);
Packit Service 3749ba
			continue;
Packit Service 3749ba
		}
Packit Service 3749ba
Packit Service 3749ba
		longopt = find_option (longopts, usages[i].option);
Packit Service 3749ba
		long_name = longopt ? longopt->name : NULL;
Packit Service 3749ba
		short_name = short_option (usages[i].option);
Packit Service 3749ba
		description = usages[i].text;
Packit Service 3749ba
Packit Service 3749ba
		if (short_name && long_name)
Packit Service 3749ba
			len = printf ("  -%c, --%s", (int)short_name, long_name);
Packit Service 3749ba
		else if (long_name)
Packit Service 3749ba
			len = printf ("  --%s", long_name);
Packit Service 3749ba
		else
Packit Service 3749ba
			len = printf ("  -%c", (int)short_name);
Packit Service 3749ba
		if (longopt && longopt->has_arg)
Packit Service 3749ba
			len += printf ("%s<%s>",
Packit Service 3749ba
			               long_name ? "=" : " ",
Packit Service 3749ba
			               usages[i].arg ? usages[i].arg : "...");
Packit Service 3749ba
		if (len < indent) {
Packit Service 3749ba
			spaces = indent - len;
Packit Service 3749ba
		} else {
Packit Service 3749ba
			printf ("\n");
Packit Service 3749ba
			spaces = indent;
Packit Service 3749ba
		}
Packit Service 3749ba
		while (description) {
Packit Service 3749ba
			while (spaces-- > 0)
Packit Service 3749ba
				fputc (' ', stdout);
Packit Service 3749ba
			next = strchr (description, '\n');
Packit Service 3749ba
			if (next) {
Packit Service 3749ba
				next += 1;
Packit Service 3749ba
				printf ("%.*s", (int)(next - description), description);
Packit Service 3749ba
				description = next;
Packit Service 3749ba
				spaces = indent;
Packit Service 3749ba
			} else {
Packit Service 3749ba
				printf ("%s\n", description);
Packit Service 3749ba
				break;
Packit Service 3749ba
			}
Packit Service 3749ba
		}
Packit Service 3749ba
Packit Service 3749ba
	}
Packit Service 3749ba
}
Packit Service 3749ba
Packit Service 3749ba
int
Packit Service 3749ba
p11_tool_getopt (int argc,
Packit Service 3749ba
                 char *argv[],
Packit Service 3749ba
                 const struct option *longopts)
Packit Service 3749ba
{
Packit Service 3749ba
	p11_buffer buf;
Packit Service 3749ba
	int ret;
Packit Service 3749ba
	char opt;
Packit Service 3749ba
	int i;
Packit Service 3749ba
Packit Service 3749ba
	if (!p11_buffer_init_null (&buf, 64))
Packit Service 3749ba
		return_val_if_reached (-1);
Packit Service 3749ba
Packit Service 3749ba
	for (i = 0; longopts[i].name != NULL; i++) {
Packit Service 3749ba
		opt = short_option (longopts[i].val);
Packit Service 3749ba
		if (opt != 0) {
Packit Service 3749ba
			p11_buffer_add (&buf, &opt, 1);
Packit Service 3749ba
			assert (longopts[i].has_arg != optional_argument);
Packit Service 3749ba
			if (longopts[i].has_arg == required_argument)
Packit Service 3749ba
				p11_buffer_add (&buf, ":", 1);
Packit Service 3749ba
		}
Packit Service 3749ba
	}
Packit Service 3749ba
Packit Service 3749ba
	ret = getopt_long (argc, argv, buf.data, longopts, NULL);
Packit Service 3749ba
Packit Service 3749ba
	p11_buffer_uninit (&buf;;
Packit Service 3749ba
Packit Service 3749ba
	return ret;
Packit Service 3749ba
}
Packit Service 3749ba
Packit Service 3749ba
static void
Packit Service 3749ba
command_usage (const p11_tool_command *commands)
Packit Service 3749ba
{
Packit Service 3749ba
	const char *progname;
Packit Service 3749ba
	int i;
Packit Service 3749ba
Packit Service 3749ba
	progname = getprogname ();
Packit Service 3749ba
	printf ("usage: %s command <args>...\n", progname);
Packit Service 3749ba
	printf ("\nCommon %s commands are:\n", progname);
Packit Service 3749ba
	for (i = 0; commands[i].name != NULL; i++) {
Packit Service 3749ba
		if (strcmp (commands[i].name, P11_TOOL_FALLBACK) != 0)
Packit Service 3749ba
			printf ("  %-15s  %s\n", commands[i].name, commands[i].text);
Packit Service 3749ba
	}
Packit Service 3749ba
	printf ("\nSee '%s <command> --help' for more information\n", progname);
Packit Service 3749ba
}
Packit Service 3749ba
Packit Service 3749ba
static void
Packit Service 3749ba
verbose_arg (void)
Packit Service 3749ba
{
Packit Service 3749ba
	setenv ("P11_KIT_DEBUG", "tool", 0);
Packit Service 3749ba
	p11_message_loud ();
Packit Service 3749ba
}
Packit Service 3749ba
Packit Service 3749ba
static void
Packit Service 3749ba
quiet_arg (void)
Packit Service 3749ba
{
Packit Service 3749ba
	setenv ("P11_KIT_DEBUG", "", 1);
Packit Service 3749ba
	p11_message_quiet ();
Packit Service 3749ba
}
Packit Service 3749ba
Packit Service 3749ba
int
Packit Service 3749ba
p11_tool_main (int argc,
Packit Service 3749ba
               char *argv[],
Packit Service 3749ba
               const p11_tool_command *commands)
Packit Service 3749ba
{
Packit Service 3749ba
	const p11_tool_command *fallback = NULL;
Packit Service 3749ba
	char *command = NULL;
Packit Service 3749ba
	bool want_help = false;
Packit Service 3749ba
	bool skip;
Packit Service 3749ba
	int in, out;
Packit Service 3749ba
	int i;
Packit Service 3749ba
Packit Service 3749ba
	/* Print messages by default. */
Packit Service 3749ba
	p11_message_loud ();
Packit Service 3749ba
Packit Service 3749ba
	/*
Packit Service 3749ba
	 * Parse the global options. We rearrange the options as
Packit Service 3749ba
	 * necessary, in order to pass relevant options through
Packit Service 3749ba
	 * to the commands, but also have them take effect globally.
Packit Service 3749ba
	 */
Packit Service 3749ba
Packit Service 3749ba
	for (in = 1, out = 1; in < argc; in++, out++) {
Packit Service 3749ba
Packit Service 3749ba
		/* The non-option is the command, take it out of the arguments */
Packit Service 3749ba
		if (argv[in][0] != '-') {
Packit Service 3749ba
			if (!command) {
Packit Service 3749ba
				skip = true;
Packit Service 3749ba
				command = argv[in];
Packit Service 3749ba
			} else {
Packit Service 3749ba
				skip = false;
Packit Service 3749ba
			}
Packit Service 3749ba
Packit Service 3749ba
		/* The global long options */
Packit Service 3749ba
		} else if (argv[in][1] == '-') {
Packit Service 3749ba
			skip = false;
Packit Service 3749ba
Packit Service 3749ba
			if (strcmp (argv[in], "--") == 0) {
Packit Service 3749ba
				if (!command) {
Packit Service 3749ba
					p11_message ("no command specified");
Packit Service 3749ba
					return 2;
Packit Service 3749ba
				} else {
Packit Service 3749ba
					break;
Packit Service 3749ba
				}
Packit Service 3749ba
Packit Service 3749ba
			} else if (strcmp (argv[in], "--verbose") == 0) {
Packit Service 3749ba
				verbose_arg ();
Packit Service 3749ba
Packit Service 3749ba
			} else if (strcmp (argv[in], "--quiet") == 0) {
Packit Service 3749ba
				quiet_arg ();
Packit Service 3749ba
Packit Service 3749ba
			} else if (strcmp (argv[in], "--help") == 0) {
Packit Service 3749ba
				want_help = true;
Packit Service 3749ba
Packit Service 3749ba
			} else if (!command) {
Packit Service 3749ba
				p11_message ("unknown global option: %s", argv[in]);
Packit Service 3749ba
				return 2;
Packit Service 3749ba
			}
Packit Service 3749ba
Packit Service 3749ba
		/* The global short options */
Packit Service 3749ba
		} else {
Packit Service 3749ba
			skip = false;
Packit Service 3749ba
Packit Service 3749ba
			for (i = 1; argv[in][i] != '\0'; i++) {
Packit Service 3749ba
				switch (argv[in][i]) {
Packit Service 3749ba
				case 'h':
Packit Service 3749ba
					want_help = true;
Packit Service 3749ba
					break;
Packit Service 3749ba
Packit Service 3749ba
				/* Compatibility option */
Packit Service 3749ba
				case 'l':
Packit Service 3749ba
					command = "list-modules";
Packit Service 3749ba
					break;
Packit Service 3749ba
Packit Service 3749ba
				case 'v':
Packit Service 3749ba
					verbose_arg ();
Packit Service 3749ba
					break;
Packit Service 3749ba
Packit Service 3749ba
				case 'q':
Packit Service 3749ba
					quiet_arg ();
Packit Service 3749ba
					break;
Packit Service 3749ba
Packit Service 3749ba
				default:
Packit Service 3749ba
					if (!command) {
Packit Service 3749ba
						p11_message ("unknown global option: -%c", (int)argv[in][i]);
Packit Service 3749ba
						return 2;
Packit Service 3749ba
					}
Packit Service 3749ba
					break;
Packit Service 3749ba
				}
Packit Service 3749ba
			}
Packit Service 3749ba
		}
Packit Service 3749ba
Packit Service 3749ba
		/* Skipping this argument? */
Packit Service 3749ba
		if (skip)
Packit Service 3749ba
			out--;
Packit Service 3749ba
		else
Packit Service 3749ba
			argv[out] = argv[in];
Packit Service 3749ba
	}
Packit Service 3749ba
Packit Service 3749ba
	/* Initialize tool's debugging after setting env vars above */
Packit Service 3749ba
	p11_debug_init ();
Packit Service 3749ba
Packit Service 3749ba
	if (command == NULL) {
Packit Service 3749ba
		/* As a special favor if someone just typed the command, help them out */
Packit Service 3749ba
		if (argc == 1) {
Packit Service 3749ba
			command_usage (commands);
Packit Service 3749ba
			return 2;
Packit Service 3749ba
		} else if (want_help) {
Packit Service 3749ba
			command_usage (commands);
Packit Service 3749ba
			return 0;
Packit Service 3749ba
		} else {
Packit Service 3749ba
			p11_message ("no command specified");
Packit Service 3749ba
			return 2;
Packit Service 3749ba
		}
Packit Service 3749ba
	}
Packit Service 3749ba
Packit Service 3749ba
	argc = out;
Packit Service 3749ba
Packit Service 3749ba
	/* Look for the command */
Packit Service 3749ba
	for (i = 0; commands[i].name != NULL; i++) {
Packit Service 3749ba
		if (strcmp (commands[i].name, P11_TOOL_FALLBACK) == 0) {
Packit Service 3749ba
			fallback = commands + i;
Packit Service 3749ba
Packit Service 3749ba
		} else if (strcmp (commands[i].name, command) == 0) {
Packit Service 3749ba
			argv[0] = command;
Packit Service 3749ba
			return (commands[i].function) (argc, argv);
Packit Service 3749ba
		}
Packit Service 3749ba
	}
Packit Service 3749ba
Packit Service 3749ba
	/* Got here because no command matched */
Packit Service 3749ba
	if (fallback != NULL) {
Packit Service 3749ba
		argv[0] = command;
Packit Service 3749ba
		return (fallback->function) (argc, argv);
Packit Service 3749ba
	}
Packit Service 3749ba
Packit Service 3749ba
	/* At this point we have no command */
Packit Service 3749ba
	p11_message ("'%s' is not a valid command. See '%s --help'",
Packit Service 3749ba
	             command, getprogname ());
Packit Service 3749ba
	return 2;
Packit Service 3749ba
}