Blame src/lib/parse_options.c

Packit Service 779887
/*
Packit Service 779887
    Copyright (C) 2010  ABRT team
Packit Service 779887
    Copyright (C) 2010  RedHat Inc
Packit Service 779887
Packit Service 779887
    This program is free software; you can redistribute it and/or modify
Packit Service 779887
    it under the terms of the GNU General Public License as published by
Packit Service 779887
    the Free Software Foundation; either version 2 of the License, or
Packit Service 779887
    (at your option) any later version.
Packit Service 779887
Packit Service 779887
    This program is distributed in the hope that it will be useful,
Packit Service 779887
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 779887
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service 779887
    GNU General Public License for more details.
Packit Service 779887
Packit Service 779887
    You should have received a copy of the GNU General Public License along
Packit Service 779887
    with this program; if not, write to the Free Software Foundation, Inc.,
Packit Service 779887
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Packit Service 779887
*/
Packit Service 779887
#include <getopt.h>
Packit Service 779887
#include "internal_libreport.h"
Packit Service 779887
Packit Service 779887
#define USAGE_OPTS_WIDTH 30
Packit Service 779887
#define USAGE_GAP         2
Packit Service 779887
Packit Service 779887
const char *g_progname;
Packit Service 779887
Packit Service 779887
const char *abrt_init(char **argv)
Packit Service 779887
{
Packit Service 779887
    if (!load_global_configuration())
Packit Service 779887
        error_msg_and_die("Cannot continue without libreport global configuration.");
Packit Service 779887
Packit Service 779887
    char *env_verbose = getenv("ABRT_VERBOSE");
Packit Service 779887
    if (env_verbose)
Packit Service 779887
        g_verbose = atoi(env_verbose);
Packit Service 779887
Packit Service 779887
    g_progname = strrchr(argv[0], '/');
Packit Service 779887
    if (g_progname)
Packit Service 779887
        g_progname++;
Packit Service 779887
    else
Packit Service 779887
        g_progname = argv[0];
Packit Service 779887
Packit Service 779887
    char *pfx = getenv("ABRT_PROG_PREFIX");
Packit Service 779887
    if (pfx && string_to_bool(pfx))
Packit Service 779887
        msg_prefix = g_progname;
Packit Service 779887
Packit Service 779887
    return g_progname;
Packit Service 779887
}
Packit Service 779887
Packit Service 779887
void export_abrt_envvars(int pfx)
Packit Service 779887
{
Packit Service 779887
    putenv(xasprintf("ABRT_VERBOSE=%u", g_verbose));
Packit Service 779887
    if (pfx)
Packit Service 779887
    {
Packit Service 779887
        putenv((char*)"ABRT_PROG_PREFIX=1");
Packit Service 779887
        msg_prefix = g_progname;
Packit Service 779887
    }
Packit Service 779887
}
Packit Service 779887
Packit Service 779887
void show_usage_and_die(const char *usage, const struct options *opt)
Packit Service 779887
{
Packit Service 779887
    INITIALIZE_LIBREPORT();
Packit Service 779887
Packit Service 779887
    fputs(_("Usage: "), stderr);
Packit Service 779887
    while (*usage)
Packit Service 779887
    {
Packit Service 779887
        int len = strchrnul(usage, '&') - usage;
Packit Service 779887
        if (len > 0)
Packit Service 779887
        {
Packit Service 779887
            fprintf(stderr, "%.*s", len, usage);
Packit Service 779887
            usage += len;
Packit Service 779887
        }
Packit Service 779887
        if (*usage == '&')
Packit Service 779887
        {
Packit Service 779887
            /* Only '&' *followed by whitespace* is substituted
Packit Service 779887
             * with program name - all other &'s are printed literally.
Packit Service 779887
             */
Packit Service 779887
            if (usage[1] == '\0' || isspace(usage[1]))
Packit Service 779887
                fputs(g_progname, stderr);
Packit Service 779887
            else
Packit Service 779887
                fputc('&', stderr);
Packit Service 779887
            usage++;
Packit Service 779887
        }
Packit Service 779887
    }
Packit Service 779887
    fputs("\n\n", stderr);
Packit Service 779887
Packit Service 779887
    for (; opt->type != OPTION_END; opt++)
Packit Service 779887
    {
Packit Service 779887
        size_t pos;
Packit Service 779887
        int pad;
Packit Service 779887
Packit Service 779887
        if (opt->type == OPTION_GROUP)
Packit Service 779887
        {
Packit Service 779887
            fputc('\n', stderr);
Packit Service 779887
            if (*opt->help)
Packit Service 779887
                fprintf(stderr, "%s\n", opt->help);
Packit Service 779887
            continue;
Packit Service 779887
        }
Packit Service 779887
Packit Service 779887
        pos = fprintf(stderr, "    ");
Packit Service 779887
        if (opt->short_name)
Packit Service 779887
            pos += fprintf(stderr, "-%c", opt->short_name);
Packit Service 779887
Packit Service 779887
        if (opt->short_name && opt->long_name)
Packit Service 779887
            pos += fprintf(stderr, ", ");
Packit Service 779887
Packit Service 779887
        if (opt->long_name)
Packit Service 779887
            pos += fprintf(stderr, "--%s", opt->long_name);
Packit Service 779887
Packit Service 779887
        if (opt->argh)
Packit Service 779887
        {
Packit Service 779887
            const char *fmt = " %s";
Packit Service 779887
            if (opt->type == OPTION_OPTSTRING)
Packit Service 779887
                fmt = "[%s]";
Packit Service 779887
            pos += fprintf(stderr, fmt, opt->argh);
Packit Service 779887
        }
Packit Service 779887
Packit Service 779887
        if (pos <= USAGE_OPTS_WIDTH)
Packit Service 779887
            pad = USAGE_OPTS_WIDTH - pos;
Packit Service 779887
        else
Packit Service 779887
        {
Packit Service 779887
            fputc('\n', stderr);
Packit Service 779887
            pad = USAGE_OPTS_WIDTH;
Packit Service 779887
        }
Packit Service 779887
        fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opt->help);
Packit Service 779887
    }
Packit Service 779887
    fputc('\n', stderr);
Packit Service 779887
    xfunc_die();
Packit Service 779887
}
Packit Service 779887
Packit Service 779887
static int parse_opt_size(const struct options *opt)
Packit Service 779887
{
Packit Service 779887
    unsigned size = 0;
Packit Service 779887
    for (; opt->type != OPTION_END; opt++)
Packit Service 779887
        size++;
Packit Service 779887
Packit Service 779887
    return size;
Packit Service 779887
}
Packit Service 779887
Packit Service 779887
unsigned parse_opts(int argc, char **argv, const struct options *opt,
Packit Service 779887
                const char *usage)
Packit Service 779887
{
Packit Service 779887
    int help = 0;
Packit Service 779887
    int size = parse_opt_size(opt);
Packit Service 779887
    const int LONGOPT_OFFSET = 256;
Packit Service 779887
Packit Service 779887
    struct strbuf *shortopts = strbuf_new();
Packit Service 779887
Packit Service 779887
    struct option *longopts = xzalloc(sizeof(longopts[0]) * (size+2));
Packit Service 779887
    struct option *curopt = longopts;
Packit Service 779887
    int ii;
Packit Service 779887
    for (ii = 0; ii < size; ++ii)
Packit Service 779887
    {
Packit Service 779887
        curopt->name = opt[ii].long_name;
Packit Service 779887
        /*curopt->flag = 0; - xzalloc did it */
Packit Service 779887
        if (opt[ii].short_name)
Packit Service 779887
            curopt->val = opt[ii].short_name;
Packit Service 779887
        else
Packit Service 779887
            curopt->val = LONGOPT_OFFSET + ii;
Packit Service 779887
Packit Service 779887
        switch (opt[ii].type)
Packit Service 779887
        {
Packit Service 779887
            case OPTION_BOOL:
Packit Service 779887
                curopt->has_arg = no_argument;
Packit Service 779887
                if (opt[ii].short_name)
Packit Service 779887
                    strbuf_append_char(shortopts, opt[ii].short_name);
Packit Service 779887
                break;
Packit Service 779887
            case OPTION_INTEGER:
Packit Service 779887
            case OPTION_STRING:
Packit Service 779887
            case OPTION_LIST:
Packit Service 779887
                curopt->has_arg = required_argument;
Packit Service 779887
                if (opt[ii].short_name)
Packit Service 779887
                    strbuf_append_strf(shortopts, "%c:", opt[ii].short_name);
Packit Service 779887
                break;
Packit Service 779887
            case OPTION_OPTSTRING:
Packit Service 779887
                curopt->has_arg = optional_argument;
Packit Service 779887
                if (opt[ii].short_name)
Packit Service 779887
                    strbuf_append_strf(shortopts, "%c::", opt[ii].short_name);
Packit Service 779887
                break;
Packit Service 779887
            case OPTION_GROUP:
Packit Service 779887
            case OPTION_END:
Packit Service 779887
                break;
Packit Service 779887
        }
Packit Service 779887
        //log_warning("curopt[%d].name:'%s' .has_arg:%d .flag:%p .val:%d", (int)(curopt-longopts),
Packit Service 779887
        //      curopt->name, curopt->has_arg, curopt->flag, curopt->val);
Packit Service 779887
        /*
Packit Service 779887
         * getopt_long() thinks that NULL name marks the end of longopts.
Packit Service 779887
         * Example:
Packit Service 779887
         * [0] name:'verbose' val:'v'
Packit Service 779887
         * [1] name:NULL      val:'c'
Packit Service 779887
         * [2] name:'force'   val:'f'
Packit Service 779887
         * ... ... ...
Packit Service 779887
         * In this case, --force won't be accepted!
Packit Service 779887
         * Therefore we can only advance if name is not NULL.
Packit Service 779887
         */
Packit Service 779887
        if (curopt->name)
Packit Service 779887
            curopt++;
Packit Service 779887
    }
Packit Service 779887
    curopt->name = "help";
Packit Service 779887
    curopt->has_arg = no_argument;
Packit Service 779887
    curopt->flag = &help;
Packit Service 779887
    curopt->val = 1;
Packit Service 779887
    /* xzalloc did it already:
Packit Service 779887
    curopt++;
Packit Service 779887
    curopt->name = NULL;
Packit Service 779887
    curopt->has_arg = 0;
Packit Service 779887
    curopt->flag = NULL;
Packit Service 779887
    curopt->val = 0;
Packit Service 779887
    */
Packit Service 779887
Packit Service 779887
    unsigned retval = 0;
Packit Service 779887
    while (1)
Packit Service 779887
    {
Packit Service 779887
        int c = getopt_long(argc, argv, shortopts->buf, longopts, NULL);
Packit Service 779887
Packit Service 779887
        if (c == -1)
Packit Service 779887
            break;
Packit Service 779887
Packit Service 779887
        if (c == '?' || help)
Packit Service 779887
        {
Packit Service 779887
            free(longopts);
Packit Service 779887
            strbuf_free(shortopts);
Packit Service 779887
            xfunc_error_retval = 0; /* this isn't error, exit code = 0 */
Packit Service 779887
            show_usage_and_die(usage, opt);
Packit Service 779887
        }
Packit Service 779887
Packit Service 779887
        for (ii = 0; ii < size; ++ii)
Packit Service 779887
        {
Packit Service 779887
            if (opt[ii].short_name == c || LONGOPT_OFFSET + ii == c)
Packit Service 779887
            {
Packit Service 779887
                if (ii < sizeof(retval)*8)
Packit Service 779887
                    retval |= (1 << ii);
Packit Service 779887
Packit Service 779887
                if (opt[ii].value != NULL) switch (opt[ii].type)
Packit Service 779887
                {
Packit Service 779887
                    case OPTION_BOOL:
Packit Service 779887
                        *(int*)(opt[ii].value) += 1;
Packit Service 779887
                        break;
Packit Service 779887
                    case OPTION_INTEGER:
Packit Service 779887
                        *(int*)(opt[ii].value) = xatoi(optarg);
Packit Service 779887
                        break;
Packit Service 779887
                    case OPTION_STRING:
Packit Service 779887
                    case OPTION_OPTSTRING:
Packit Service 779887
                        if (optarg)
Packit Service 779887
                            *(char**)(opt[ii].value) = (char*)optarg;
Packit Service 779887
                        break;
Packit Service 779887
                    case OPTION_LIST:
Packit Service 779887
                        *(GList**)(opt[ii].value) = g_list_append(*(GList**)(opt[ii].value), optarg);
Packit Service 779887
                        break;
Packit Service 779887
                    case OPTION_GROUP:
Packit Service 779887
                    case OPTION_END:
Packit Service 779887
                        break;
Packit Service 779887
                }
Packit Service 779887
            }
Packit Service 779887
        }
Packit Service 779887
    }
Packit Service 779887
Packit Service 779887
    free(longopts);
Packit Service 779887
    strbuf_free(shortopts);
Packit Service 779887
Packit Service 779887
    return retval;
Packit Service 779887
}