/* Demonstration of sub-command options in popt
 *
 * This program was written for demonstrational purposes
 * 2005-05-24 by Henrik Nordstrom and is placed in the public
 * domain. No righs reserved. You may use this program in any
 * way you may seem fit.
 *
 * The program is delivered as-is and comes with absolutely no
 * warranty of any kind. The author can not be held responsible
 * for any problems caused by using whole or parts of this
 * program for any purpose.
 */
#include <popt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char *program_name;

void
usage(poptContext optCon, int exitcode, char *error, char *addl)
{
    poptPrintUsage(optCon, stderr, 0);
    if (error)
	fprintf(stderr, "%s: %s\n", error, addl);
    exit(exitcode);
}

int
argv_count(const char **argv)
{
    int i;
    if (!argv)
	return 0;
    for (i = 0; *argv++; i++);
    return i;
}

/**** Global *****/

const char *global_1 = "default global 1";
const char *global_2 = "default global 2";

struct poptOption global_options[] =
{
    {"opt1", 0, POPT_ARG_STRING, &global_1, 0, "Global option 1", "String used for global option 2"},
    {"opt2", 0, POPT_ARG_STRING, &global_2, 0, "Global option 2", "String used for global option 2"},
    {NULL, 0, 0, NULL, 0, NULL, NULL}
};
#define GLOBAL_POPT_TABLE {NULL, 0, POPT_ARG_INCLUDE_TABLE, global_options, 0, "Global options:", NULL},

void
module_global(void)
{
    printf("global1: %s\n", global_1);
    printf("global2: %s\n", global_2);
}

/**** Module 1 *****/

const char *og1_1 = "default 1 1";

struct poptOption module1_options[] =
{
    {"og1_1", 0, POPT_ARG_STRING, &og1_1, 0, "Common option 1 1", "String used for option 1 1"},
    {NULL, 0, 0, NULL, 0, NULL, NULL}
};

#define MODULE1_POPT_TABLE {NULL, 0, POPT_ARG_INCLUDE_TABLE, module1_options, 0, "Module 1 options:", NULL},
void
module1(void)
{
    printf("og1_1: %s\n", og1_1);
}

/***** Module 2 ******/

const char *og2_1 = "default 2 1";

const char *module2_login = NULL;
const char *module2_password = NULL;

void
module2_parse_password(const struct poptOption *opt,
    const char *arg, void *data)
{
    char *login = strdup(arg);
    char *password = strchr(login, ':');
    if (!password) {
	fprintf(stderr, "ERROR: --%s requires a login:password\n", opt->longName);
	exit(1);
    }
    *password++ = '\0';
    module2_login = login;
    module2_password = password;
}

void
module2_parse(poptContext con,
    enum poptCallbackReason reason,
    const struct poptOption *opt,
    const char *arg,
    void *data)
{
    switch (opt->val) {
    case 2:			/* og2_2 */
	module2_parse_password(opt, arg, data);
	break;
    default:
	fprintf(stderr, "Unexpected callbal for %s\n", opt->longName);
	break;
    }
}

struct poptOption module2_options[] =
{
    {NULL, 0, POPT_ARG_CALLBACK, module2_parse, 0, NULL, NULL},
    MODULE1_POPT_TABLE
    {"og2_1", 0, POPT_ARG_STRING, &og2_1, 0, "Common option 2 1", "String used for option 2 1"},
    {"og2_2", 0, POPT_ARG_STRING, NULL, 2, "Common option 2 2", "login:password"},
    {NULL, 0, 0, NULL, 0, NULL, NULL}
};

#define MODULE2_POPT_TABLE {NULL, 0, POPT_ARG_INCLUDE_TABLE, module2_options, 0, "Module 2 options:", NULL},
void
module2(void)
{
    printf("og2_1: %s\n", og2_1);
    printf("login: %s\n", module2_login);
    printf("password: %s\n", module2_password);
}


/* Sub command 1. Uses Module 1 */

int
sub_command1(int argc, char *argv[])
{
    const char *test = "default";
    const char *recipient;
    int opt;

    struct poptOption options[] =
    {
	{"test", 0, POPT_ARG_STRING, &test, 0, "Test", "Message text"},
	GLOBAL_POPT_TABLE
	MODULE1_POPT_TABLE
	POPT_AUTOHELP
	{NULL, 0, 0, NULL, 0, NULL, NULL}
    };

    poptContext optCon = poptGetContext(NULL, argc, (const char **)argv, options, 0);
    poptSetOtherOptionHelp(optCon,"command1 [OPTION...] recipient...");

    while ((opt = poptGetNextOpt(optCon)) >= 0) {
	switch (opt) {
	default:
	    fprintf(stderr, "Unexpected option %d\n", opt);
	    exit(1);
	}
    }
    if (opt != -1) {
	fprintf(stderr, "%s: %s\n",
	    poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
	    poptStrerror(opt));
	poptPrintUsage(optCon, stderr, 0);
	exit(1);
    }
    if (poptPeekArg(optCon) == NULL) {
	poptPrintUsage(optCon, stderr, 0);
	exit(1);
    }
    module_global();
    module1();
    printf("Message: %s\n", test);

    while ((recipient = poptGetArg(optCon)) != NULL) {
	printf("Recipient: %s\n", recipient);
    }

    return 1;
}

/* Sub command 2. Uses Module 1 & 2 */

int
sub_command2(int argc, char *argv[])
{
    const char *test = "default";
    int opt;

    struct poptOption options[] =
    {
	{"test", 0, POPT_ARG_STRING, &test, 0, "Testing", "String"},
	GLOBAL_POPT_TABLE
	MODULE2_POPT_TABLE
	POPT_AUTOHELP
	{NULL, 0, 0, NULL, 0, NULL, NULL}
    };

    poptContext optCon = poptGetContext(NULL, argc, (const char **)argv, options, 0);
    poptSetOtherOptionHelp(optCon, "command2 [OPTION...]");

    while ((opt = poptGetNextOpt(optCon)) >= 0) {
	switch (opt) {
	default:
	    fprintf(stderr, "Unexpected option %d\n", opt);
	    exit(1);
	}
    }
    if (opt != -1) {
	fprintf(stderr, "%s: %s\n",
	    poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
	    poptStrerror(opt));
	poptPrintUsage(optCon, stderr, 0);
	exit(1);
    }
    if (poptPeekArg(optCon) != NULL) {
	poptPrintUsage(optCon, stderr, 0);
	exit(1);
    }
    module_global();
    module1();
    module2();
    return 1;
}

/* Main */

int 
main(int argc, char *argv[])
{
    const char *command;
    const char **sub_argv;
    int sub_argc;
    int opt;
    int ret;
    struct poptOption options[] = {
	GLOBAL_POPT_TABLE
	{"help", '?', POPT_ARG_NONE, NULL, 1, "Show this help message", NULL},
	{"usage", 0, POPT_ARG_NONE, NULL, 2, "Display brief usage message", NULL},
	{NULL, 0, 0, NULL, 0, NULL, NULL}
    };
    poptContext optCon = poptGetContext(NULL, argc, (const char **)argv, options, POPT_CONTEXT_POSIXMEHARDER);
    poptSetOtherOptionHelp(optCon, "<command> [OPTION...]");
    program_name = argv[0];

    while ((opt = poptGetNextOpt(optCon)) >= 0) {
	switch (opt) {
	case 1:
	    poptPrintHelp(optCon, stderr, 0);
	    fprintf(stderr, "\n");
	    fprintf(stderr, "Available commands:\n");
	    fprintf(stderr, "  %-42s %s\n", "command1", "Silly example command");
	    fprintf(stderr, "  %-42s %s\n", "command2", "Another silly example command");
	    exit(0);
	case 2:
	    poptPrintUsage(optCon, stderr, 0);
	    exit(0);
	default:
	    fprintf(stderr, "Unexpected option %d\n", opt);
	    exit(1);
	}
    }
    if (opt != -1) {
	fprintf(stderr, "%s: %s\n",
	    poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
	    poptStrerror(opt));
	poptPrintUsage(optCon, stderr, 0);
	exit(1);
    }
    command = poptGetArg(optCon);
    if (!command) {
	poptPrintUsage(optCon, stderr, 0);
	exit(1);
    }
    argc = poptStrippedArgv(optCon, argc, argv);
    if (strcmp(command, "command1") == 0)
	ret = sub_command1(argc, argv);
    else if (strcmp(command, "command2") == 0)
	ret = sub_command2(argc, argv);
    else {
	fprintf(stderr, "Unknown command '%s'\n", command);
	poptPrintUsage(optCon, stderr, 0);
	exit(1);
    }
    poptFreeContext(optCon);
    return ret;
}
