root/usr/src/cmd/sgs/elfedit/common/sys.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <strings.h>
#include <elfedit.h>
#include "_elfedit.h"
#include "msg.h"




/*
 * This file provides the builtin sys module. It is similar to the
 * other modules, but differs in several important ways:
 *
 *      - It is built as a static part of elfedit, and not
 *              as a sharable object.
 *      - It must be avaialble before the ELFCLASS of the object
 *              is known, so it is not ELFCLASS specific. We don't build
 *              it twice with <sys/machelf.h>, as we do for the loadable
 *              modules. This means that commands need to test for the type
 *              of their obj_state argument at runtime.
 *      - The init function signature is different. We build an entire
 *              module definition statically.
 */



/*
 * This function is supplied to elfedit through our elfedit_module_t
 * definition. It translates the opaque elfedit_i18nhdl_t handles
 * in our module interface into the actual strings for elfedit to
 * use.
 *
 * note:
 *      This module uses Msg codes for its i18n handle type.
 *      So the translation is simply to use MSG_INTL() to turn
 *      it into a string and return it.
 */
static const char *
mod_i18nhdl_to_str(elfedit_i18nhdl_t hdl)
{
        Msg msg = (Msg)hdl;

        return (MSG_INTL(msg));
}



/*
 * The sys_opt_t enum specifies a bit value for every optional argument
 * allowed by a command in this module.
 */
typedef enum {
        SYS_OPT_F_ALL =         1,      /* -a */
        SYS_OPT_F_FORCE =       2,      /* -f */
        SYS_OPT_F_SYNOPSIS =    4,      /* -s */
} dyn_opt_t;


/*
 * Given a generic (void *) pointer to an obj_state argument, determine
 * which type it is, and return the st_file, st_fd and st_elf fields.
 */
static void
get_obj_state_info(void *obj_state, const char **file, int *fd, Elf **elf)
{
        if (state.elf.elfclass == ELFCLASS32) {
                elfedit32_obj_state_t *s = (elfedit32_obj_state_t *)obj_state;

                *file = s->os_file;
                *fd = s->os_fd;
                *elf = s->os_elf;
        } else {
                elfedit64_obj_state_t *s = (elfedit64_obj_state_t *)obj_state;

                *file = s->os_file;
                *fd = s->os_fd;
                *elf = s->os_elf;
        }
}



/*
 * Helper for cmd_help(). Displays synopsis information for one command.
 */
static void
cmd_help_synopsis(elfeditGC_module_t *mod, elfeditGC_cmd_t *cmd)
{
        char            name_buf[128];
        const char      *name;
        const char      **cmd_name;

        if (cmd->cmd_name[1] == NULL) {   /* One name */
                name = *cmd->cmd_name;
        } else {
                const char *cname;
                int need_comma = 0;

                name = name_buf;
                (void) snprintf(name_buf, sizeof (name_buf),
                    MSG_ORIG(MSG_HLPFMT_MULTNAM), cmd->cmd_name[0]);
                for (cmd_name = cmd->cmd_name + 1;
                    *cmd_name; cmd_name++) {
                        if (need_comma)
                                (void) strlcat(name_buf,
                                    MSG_ORIG(MSG_STR_COMMA_SP),
                                    sizeof (name_buf));
                        need_comma = 1;
                        cname = (cmd_name[0][0] == '\0') ?
                            MSG_INTL(MSG_HLPFMT_MODDEFCMD) : *cmd_name;
                        (void) strlcat(name_buf, cname,
                            sizeof (name_buf));
                }
                (void) strlcat(name_buf, MSG_ORIG(MSG_STR_CPAREN),
                    sizeof (name_buf));
        }
        elfedit_printf(MSG_ORIG(MSG_HLPFMT_NAMSUMHDR), name,
            (* mod->mod_i18nhdl_to_str)(cmd->cmd_desc));
        elfedit_printf(MSG_INTL(MSG_HLPFMT_SUMSYNOPSIS),
            elfedit_format_command_usage(mod, cmd,
            MSG_ORIG(MSG_STR_HLPSUMINDENT),
            strlen(MSG_ORIG(MSG_STR_HLPSUMINDENT))));
}


/*
 * Helper for cmd_help(). Displays synopsis information for one module.
 */
static void
cmd_help_showmod(elfeditGC_module_t *mod)
{
        elfeditGC_cmd_t *cmd;

        elfedit_printf(MSG_ORIG(MSG_HLPFMT_NAMDSCHDR),
            mod->mod_name, (* mod->mod_i18nhdl_to_str)(mod->mod_desc));
        for (cmd = mod->mod_cmds; cmd->cmd_func != NULL; cmd++) {
                if (cmd != mod->mod_cmds)
                        elfedit_printf(MSG_ORIG(MSG_STR_NL));
                elfedit_printf(MSG_ORIG(MSG_STR_NL));
                cmd_help_synopsis(mod, cmd);
        }
}


/*
 * Given a string containing newline characters, break it into
 * individual lines, and output each line with the given
 * prefix string in front.
 */
static void
write_help_str(const char *str, const char *prefix)
{
        size_t i;

        if (str == NULL)
                return;
        while (*str) {
                i = strcspn(str, MSG_ORIG(MSG_STR_NL));
                if (*(str + i) != '\0')
                        i++;
                elfedit_printf(prefix);
                elfedit_write(str, i);
                str += i;
        }
}


/*
 * Given a title, and a NULL terminated list of option/argument
 * descriptors, output the list contents.
 */
static void
write_optarg(elfeditGC_module_t *mod, const char *title,
    elfedit_cmd_optarg_t *optarg)
{
        int                     cnt;
        int                     len;
        const char              *help;
        elfedit_optarg_item_t   item;

        elfedit_printf(title);
        for (cnt = 0; optarg->oa_name != NULL; cnt++) {
                elfedit_next_optarg(&optarg, &item);

                /* Insert a blank line between items */
                if (cnt > 0)
                        elfedit_printf(MSG_ORIG(MSG_STR_NL));

                /* Indentation */
                elfedit_printf(MSG_ORIG(MSG_STR_HLPINDENT));
                len = strlen(item.oai_name);
                help = elfedit_optarg_helpstr(mod, &item);
                if (item.oai_flags & ELFEDIT_CMDOA_F_VALUE) {
                        len += 1 + strlen(item.oai_vname);
                        elfedit_printf(MSG_ORIG(MSG_STR_HLPOPTARG2),
                            item.oai_name, item.oai_vname);
                } else {
                        elfedit_printf(MSG_ORIG(MSG_STR_HLPOPTARG),
                            item.oai_name);
                }

                /*
                 * If name is too long, inject a newline to avoid
                 * crowding the help text.
                 */
                if (len > 3)
                        elfedit_printf(MSG_ORIG(MSG_STR_NL));

                /* Output the help text with a tab prefix */
                write_help_str(help, MSG_ORIG(MSG_STR_TAB));
        }
}


/*
 * Implementation of sys:help
 */
/*ARGSUSED*/
static elfedit_cmdret_t
cmd_help(void *obj_state, int argc, const char *argv[])
{
#define INITIAL_ITEM_ALLOC 4


        /*
         * An array of this type is used to collect the data needed to
         * generate help output.
         */
        typedef struct {
                elfeditGC_cmd_t         *cmd;
                elfeditGC_module_t      *cmd_mod;       /* Used with cmd */
                elfeditGC_module_t      *mod;
        } ITEM;

        static ITEM     *item;
        static int      item_cnt;

        MODLIST_T               *modlist;
        int                     dispcnt;
        size_t                  i;
        elfeditGC_module_t      *mod;
        elfeditGC_cmd_t         *cmd;
        int                     minus_s = 0;
        elfedit_getopt_state_t  getopt_state;
        ITEM                    *cur_item;

        /*
         * Process options. The only option accepted is -s, so we
         * don't even have to check the idmask to know.
         */
        elfedit_getopt_init(&getopt_state, &argc, &argv);
        while (elfedit_getopt(&getopt_state) != NULL)
                minus_s = 1;

        /*
         * This command can produce an arbitrary amount of output, so
         * run a pager.
         */
        elfedit_pager_init();

        if (argc == 0) {
                if (minus_s) {
                        /* Force all modules to load so we have data */
                        elfedit_load_modpath();
                        for (modlist = state.modlist; modlist;
                            modlist = modlist->ml_next) {
                                cmd_help_showmod(modlist->ml_mod);
                                if (modlist->ml_next != NULL) {
                                        elfedit_printf(MSG_ORIG(MSG_STR_NL));
                                        elfedit_printf(MSG_ORIG(MSG_STR_NL));
                                }
                        }
                        return (ELFEDIT_CMDRET_NONE);
                }

                /*
                 * If no arguments are present, we display a simple
                 * "how to use help" tutorial, which will hopefully
                 * bootstrap the user into a position where they
                 * know how to run the help command, and then find
                 * what they're really after.
                 */
                elfedit_printf(MSG_INTL(MSG_SYS_HELP_HELP_NOARG));
                return (ELFEDIT_CMDRET_NONE);
        }


        /*
         * As we process the arguments, we are willing to treat each
         * one as either a module or a command:
         *      1) An item without a colon can be a module,
         *              or a command from the sys: module.
         *      2) An item with a colon, and no command part is
         *              a module, and it can also be the default
         *              command for the module, if it has one. We choose
         *              to only display the module info in this case, since
         *              the use of "" to represent the default command is
         *              an implementation detail, not a user-facing concept.
         *      3) An item with a colon and a command part can only be
         *              a command.
         *
         * Note that there are cases where one argument can have two
         * valid interpretations. In this case, we display them both.
         *
         * Pass over the arguments and determine how many distinct
         * "things" we need to display. At the same time, force any
         * needed modules to load so that the debug load messages won't
         * show up in between the displayed items, and save the command
         * and module definitions we will need to generate the output.
         */
        if (argc > item_cnt) {
                int n = (item_cnt == 0) ? INITIAL_ITEM_ALLOC : item_cnt;

                while (n < argc)
                        n *= 2;

                item = elfedit_realloc(MSG_INTL(MSG_ALLOC_HELPITEM), item,
                    n * sizeof (*item));
                item_cnt = n;
        }

        dispcnt = 0;
        for (i = 0; i < argc; i++) {
                const char *colon = strchr(argv[i], ':');

                if (colon == NULL) {    /* No colon: sys: cmd or module */
                        item[i].cmd =
                            elfedit_find_command(argv[i], 0, &item[i].cmd_mod);
                        if (item[i].cmd != NULL)
                                dispcnt++;

                        /*
                         * Also try to load it as a module. If a command
                         * was found, then this need not succeed. Otherwise,
                         * it has to be a module, and we cause an error
                         * to be issued if not.
                         */
                        item[i].mod = elfedit_load_module(argv[i],
                            item[i].cmd == NULL, 0);
                        if (item[i].mod != NULL)
                                dispcnt++;
                } else if (*(colon + 1) == '\0') {
                        /* Just colon: Module (and maybe default command) */
                        char buf[ELFEDIT_MAXMODNAM + 1];
                        const char *str = argv[i];
                        int len = colon - str;

                        item[i].cmd = NULL;
                        /* Strip off the colon */
                        if (len < sizeof (buf)) {
                                (void) strncpy(buf, str, len);
                                buf[len] = '\0';
                                str = buf;
                        }
                        item[i].mod = elfedit_load_module(str, 1, 0);
                        dispcnt++;
                } else {        /* A command */
                        item[i].cmd =
                            elfedit_find_command(argv[i], 1, &item[i].cmd_mod);
                        dispcnt++;
                        item[i].mod = NULL;
                }
        }

        /*
         * Having validated the items, loop over them again and produce
         * the required help output.
         */
        for (cur_item = item; argc--; argv++, cur_item++) {


                /* Help for a module? */
                if (cur_item->mod != NULL) {
                        if (dispcnt > 1)
                                elfedit_printf(MSG_ORIG(MSG_HLPFMT_MULTIHDR),
                                    *argv);
                        cmd_help_showmod(cur_item->mod);
                        if ((dispcnt > 1) && (argc > 0))
                                elfedit_printf(MSG_INTL(MSG_HLPFMT_MULTIEND),
                                    argv[0], argv[1]);
                        /* An empty line after the last line of output */
                        elfedit_printf(MSG_ORIG(MSG_STR_NL));
                }

                /* Help for a command? */
                if (cur_item->cmd == NULL)
                        continue;
                cmd = cur_item->cmd;
                mod = cur_item->cmd_mod;
                if (dispcnt > 1)
                        elfedit_printf(MSG_ORIG(MSG_HLPFMT_MULTIHDR), *argv);

                /* If -s, display quick synopsis rather than the whole thing */
                if (minus_s) {
                        cmd_help_synopsis(mod, cmd);
                        continue;
                }

                elfedit_printf(MSG_INTL(MSG_HLPFMT_MOD), mod->mod_name,
                    (* mod->mod_i18nhdl_to_str)(mod->mod_desc));
                elfedit_printf(MSG_INTL(MSG_HLPFMT_NAME),
                    *cmd->cmd_name,
                    (* mod->mod_i18nhdl_to_str)(cmd->cmd_desc));
                elfedit_printf(MSG_INTL(MSG_HLPFMT_SYNOPSIS),
                    elfedit_format_command_usage(mod, cmd,
                    MSG_ORIG(MSG_STR_HLPUSEINDENT),
                    strlen(MSG_ORIG(MSG_STR_HLPINDENT))));
                /* If there are alias names, show them */
                if (cmd->cmd_name[1] != NULL) {
                        const char **alias = cmd->cmd_name + 1;

                        elfedit_printf(MSG_INTL(MSG_HLPFMT_ALIASES));
                        do {
                                elfedit_printf(
                                    MSG_ORIG(MSG_STR_HLPINDENT));
                                elfedit_printf(
                                    MSG_ORIG(MSG_FMT_MODCMD),
                                    mod->mod_name, *alias);
                                if (**alias == '\0')
                                        elfedit_printf(
                                            MSG_INTL(MSG_HLPFMT_DEFCMD));
                                elfedit_printf(MSG_ORIG(MSG_STR_NL));
                                alias++;
                        } while (*alias);
                }
                elfedit_printf(MSG_INTL(MSG_HLPFMT_DESC));
                write_help_str(
                    (* mod->mod_i18nhdl_to_str)(cmd->cmd_help),
                    MSG_ORIG(MSG_STR_HLPINDENT));
                if (cmd->cmd_args != NULL)
                        write_optarg(mod, MSG_INTL(MSG_HLPFMT_ARGS),
                            cmd->cmd_args);
                if (cmd->cmd_opt != NULL)
                        write_optarg(mod, MSG_INTL(MSG_HLPFMT_OPT),
                            cmd->cmd_opt);
                if ((dispcnt > 1) && (argc > 0))
                        elfedit_printf(MSG_INTL(MSG_HLPFMT_MULTIEND),
                            argv[0], argv[1]);
                /* An empty line after the last line of output */
                elfedit_printf(MSG_ORIG(MSG_STR_NL));
        }

        return (ELFEDIT_CMDRET_NONE);

#undef  INITIAL_ITEM_ALLOC
}


/*
 * Command completion function for sys:help
 */
/*ARGSUSED*/
static void
cpl_help(void *obj_state, void *cpldata, int argc, const char *argv[],
    int num_opt)
{
        /*
         * The arguments can be any module or command. Supplying the
         * commands implicitly supplies the modules too.
         */
        elfedit_cpl_command(cpldata);
}


/*
 * Implementation of sys:load
 */
/*ARGSUSED*/
static elfedit_cmdret_t
cmd_load(void *obj_state, int argc, const char *argv[])
{
        elfedit_getopt_state_t  getopt_state;
        elfedit_getopt_ret_t    *getopt_ret;
        struct stat             statbuf;

        elfedit_getopt_init(&getopt_state, &argc, &argv);
        while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
                switch (getopt_ret->gor_idmask) {
                case SYS_OPT_F_ALL:
                        elfedit_load_modpath();
                        break;
                }
        }

        /* For each remaining argument, load them individually */
        for (; argc-- > 0; argv++) {
                /* Is it a directory? Load everything in it */
                if ((stat(*argv, &statbuf) == 0) &&
                    (statbuf.st_mode & S_IFDIR)) {
                        elfedit_load_moddir(*argv, 1, 1);
                } else {        /* Not a directory. Normal load */
                        (void) elfedit_load_module(*argv, 1, 1);
                }
        }

        return (0);
}


/*
 * Command completion function for sys:load
 */
/*ARGSUSED*/
static void
cpl_load(void *obj_state, void *cpldata, int argc, const char *argv[],
    int num_opt)
{
        /*
         * Module names. Note that this causes elfedit to load all
         * of the modules, which probably makes the current load
         * operation unnecessary. This could be improved, but I don't
         * see it as worth the complexity. Explicit load calls are
         * rare, and the user will usually not use command completion.
         */
        elfedit_cpl_module(cpldata, 1);
}


/*
 * Implementation of sys:quit
 */
/*ARGSUSED*/
static elfedit_cmdret_t
cmd_quit(void *obj_state, int argc, const char *argv[])
{
        elfedit_getopt_state_t  getopt_state;
        elfedit_getopt_ret_t    *getopt_ret;
        int                     force = 0;
        const char              *file;
        int                     fd;
        Elf                     *elf;

        elfedit_getopt_init(&getopt_state, &argc, &argv);
        while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
                switch (getopt_ret->gor_idmask) {
                case SYS_OPT_F_FORCE:
                        force = 1;
                        break;
                }
        }
        if (argc != 0)
                elfedit_command_usage();

        if (state.file.present) {
                /*
                 * If session is not READONLY, then refuse to quit if file
                 * needs flushing and -f option was not used.
                 */
                if (!(state.flags & ELFEDIT_F_READONLY) && state.file.dirty &&
                    !force)
                        elfedit_msg(ELFEDIT_MSG_ERR,
                            MSG_INTL(MSG_ERR_NODIRTYQUIT));

                get_obj_state_info(obj_state, &file, &fd, &elf);
                (void) close(fd);
                (void) elf_end(elf);
                free(obj_state);
        }

        elfedit_exit(0);
        /*NOTREACHED*/
        return (0);
}


/*
 * Implementation of sys:status
 */
/*ARGSUSED*/
static elfedit_cmdret_t
cmd_status(void *obj_state, int argc, const char *argv[])
{
        MODLIST_T       *modlist;
        const char      *s;
        size_t          i;

        if (argc > 0)
                elfedit_command_usage();

        /*
         * This command can produce an arbitrary amount of output, so
         * run a pager.
         */
        elfedit_pager_init();

        /* Files */
        if (state.file.present == 0) {
                elfedit_printf(MSG_INTL(MSG_HLPFMT_INFILENONE));
        } else if (state.flags & ELFEDIT_F_READONLY) {
                elfedit_printf(MSG_INTL(MSG_HLPFMT_INFILERO),
                    state.file.infile);
        } else {
                elfedit_printf(MSG_INTL(MSG_HLPFMT_INFILE), state.file.infile);
                elfedit_printf(MSG_INTL(MSG_HLPFMT_OUTFILE),
                    state.file.outfile);
        }
        if (state.file.dirty)
                elfedit_printf(MSG_INTL(MSG_HLPFMT_CNGPENDING));

        /* Option Variables */
        elfedit_printf(MSG_INTL(MSG_HLPFMT_VARHDR));
        elfedit_printf(MSG_INTL(MSG_HLPFMT_AFLG),
            (state.flags & ELFEDIT_F_AUTOPRINT) ? MSG_ORIG(MSG_STR_ON) :
            MSG_ORIG(MSG_STR_OFF));
        elfedit_printf(MSG_INTL(MSG_HLPFMT_DFLG),
            (state.flags & ELFEDIT_F_DEBUG) ? MSG_ORIG(MSG_STR_ON) :
            MSG_ORIG(MSG_STR_OFF));
        elfedit_printf(MSG_INTL(MSG_HLPFMT_OFLG),
            elfedit_atoconst_value_to_str(ELFEDIT_CONST_OUTSTYLE,
            state.outstyle, 1));

        /* Module Load Path */
        elfedit_printf(MSG_INTL(MSG_HLPFMT_PATHHDR));
        for (i = 0; i < state.modpath.n; i++)
                elfedit_printf(MSG_ORIG(MSG_HLPFMT_PATHELT),
                    state.modpath.seg[i]);

        /* Currently Loaded Modules */
        elfedit_printf(MSG_INTL(MSG_HLPFMT_MODHDR));
        for (modlist = state.modlist; modlist;
            modlist = modlist->ml_next) {
                s = modlist->ml_path ? modlist->ml_path :
                    MSG_INTL(MSG_FMT_BUILTIN);
                elfedit_printf(MSG_ORIG(MSG_HLPFMT_NAMDSCCOL),
                    modlist->ml_mod->mod_name, s);
        }

        return (ELFEDIT_CMDRET_NONE);
}

/*
 * Implementation of sys:set
 */
/*ARGSUSED*/
static elfedit_cmdret_t
cmd_set(void *obj_state, int argc, const char *argv[])
{
        if ((argc != 2) || (strlen(argv[0]) > 1))
                elfedit_command_usage();

        switch (**argv) {
        case 'a':
        case 'A':
                if (elfedit_atobool(argv[1], MSG_INTL(MSG_SYSSET_A)))
                        state.flags |= ELFEDIT_F_AUTOPRINT;
                else
                        state.flags &= ~ELFEDIT_F_AUTOPRINT;
                break;

        case 'd':
        case 'D':
                if (elfedit_atobool(argv[1], MSG_INTL(MSG_SYSSET_D)))
                        state.flags |= ELFEDIT_F_DEBUG;
                else
                        state.flags &= ~ELFEDIT_F_DEBUG;
                break;

        case 'o':
        case 'O':
                if (elfedit_atooutstyle(argv[1], &state.outstyle) == 0)
                        elfedit_msg(ELFEDIT_MSG_ERR,
                            MSG_INTL(MSG_ERR_BADOSTYLE), argv[1]);
                break;

        default:
                elfedit_command_usage();
        }

        return (0);
}


/*
 * Command completion function for sys:set
 */
/*ARGSUSED*/
static void
cpl_set(void *obj_state, void *cpldata, int argc, const char *argv[],
    int num_opt)
{
        const char *s;

        /*
         * This command doesn't accept options, so num_opt should be
         * 0. This is a defensive measure, in case that should change.
         */
        argc -= num_opt;
        argv += num_opt;

        if ((argc < 1) || (argc > 2))
                return;

        if (argc == 1) {        /* The first argument is a variable letter */
                elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_A), 1);
                elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_D), 1);
                elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_O), 1);
                elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_W), 1);
                return;
        }

        /* We're dealing with the second argument, the value */
        s = argv[0];
        if (strlen(s) > 1)      /* One letter variables */
                return;
        switch (*s) {
        case 'a':               /* Booleans */
        case 'A':
        case 'd':
        case 'D':
        case 'w':
        case 'W':
                /* The second argument is a boolean */
                elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_BOOL);

                /* The numbers are not symbolic, but we want them in the list */
                elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_0), 1);
                elfedit_cpl_match(cpldata, MSG_ORIG(MSG_STR_1), 1);
                break;

        case 'o':               /* Output style */
        case 'O':
                elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_OUTSTYLE);
                break;
        }
}


/*
 * Implementation of sys:unload
 */
/*ARGSUSED*/
static elfedit_cmdret_t
cmd_unload(void *obj_state, int argc, const char *argv[])
{
        elfedit_getopt_state_t  getopt_state;
        elfedit_getopt_ret_t    *getopt_ret;
        MODLIST_T               *moddef;
        int                     do_all = 0;

        elfedit_getopt_init(&getopt_state, &argc, &argv);
        while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
                switch (getopt_ret->gor_idmask) {
                case SYS_OPT_F_ALL:
                        do_all = 1;
                        break;
                }
        }

        /*
         * If -a is specified, unload everything except builtins. Don't
         * allow plain arguments in this case because there is nothing
         * left to unload after -a.
         */
        if (do_all) {
                if (argc > 0)
                        elfedit_command_usage();
                /*
                 * Until we run out of non-builtin modules, take the first
                 * one from the list and unload it. Each removal alters
                 * the list, so we always start at the beginning, but this
                 * is efficient since we always remove the first available item
                 */
                while (state.modlist != NULL) {
                        for (moddef = state.modlist; moddef != NULL;
                            moddef = moddef->ml_next)
                                if (moddef->ml_dl_hdl != NULL) break;

                        /* If we made it to the end, then the list is empty */
                        if (moddef == NULL)
                                break;

                        elfedit_unload_module(moddef->ml_mod->mod_name);
                }
                return (0);
        }

        /* Unload each module individually */
        for (; argc-- > 0; argv++)
                elfedit_unload_module(*argv);

        return (0);
}


/*
 * Command completion function for sys:unload
 */
/*ARGSUSED*/
static void
cpl_unload(void *obj_state, void *cpldata, int argc, const char *argv[],
    int num_opt)
{
        /*
         * Module names. Don't allow elfedit to load all the modules,
         * as the only modules we want to unload are those already
         * in memory.
         */
        elfedit_cpl_module(cpldata, 0);
}


/*
 * Implementation of sys:write
 */
/*ARGSUSED2*/
static elfedit_cmdret_t
cmd_write(void *obj_state, int argc, const char *argv[])
{
        const char      *file;
        int             fd;
        Elf             *elf;

        if (argc != 0)
                elfedit_command_usage();

        if (state.file.present != 0) {
                if (state.flags & ELFEDIT_F_READONLY)
                        elfedit_msg(ELFEDIT_MSG_ERR,
                            MSG_INTL(MSG_ERR_READONLY));

                get_obj_state_info(obj_state, &file, &fd, &elf);
                if (elf_update(elf, ELF_C_WRITE) == -1)
                        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_LIBELF),
                            file, MSG_ORIG(MSG_ELF_UPDATE),
                            elf_errmsg(elf_errno()));

                /*
                 * An update has succeeded for this file, so revoke the need
                 * to unlink it on exit.
                 */
                state.file.unlink_on_exit = 0;
        }

        return (ELFEDIT_CMDRET_FLUSH);
}





/*ARGSUSED*/
MODLIST_T *
elfedit_sys_init(elfedit_module_version_t version)
{
        /* sys:help */
        static const char *name_help[] = { MSG_ORIG(MSG_SYS_CMD_HELP),
            MSG_ORIG(MSG_SYS_CMD_HELP_A1), MSG_ORIG(MSG_SYS_CMD_HELP_A2),
            NULL };
        static elfedit_cmd_optarg_t opt_help[] = {
                { MSG_ORIG(MSG_STR_MINUS_S),
                    /* MSG_INTL(MSG_SYS_OPTDESC_HELP_S) */
                    ELFEDIT_I18NHDL(MSG_SYS_OPTDESC_HELP_S), 0,
                    SYS_OPT_F_SYNOPSIS, 0 },
                { NULL }
        };
        static elfedit_cmd_optarg_t arg_help[] = {
                { MSG_ORIG(MSG_STR_ARG),
                    /* MSG_INTL(MSG_ARGDESC_HELP_ARG) */
                    ELFEDIT_I18NHDL(MSG_ARGDESC_HELP_ARG),
                    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
                { NULL }
        };

        /* sys:load */
        static const char *name_load[] = {
            MSG_ORIG(MSG_SYS_CMD_LOAD), NULL };
        static elfedit_cmd_optarg_t opt_load[] = {
                { MSG_ORIG(MSG_STR_MINUS_A),
                    /* MSG_INTL(MSG_SYS_OPTDESC_LOAD_A) */
                    ELFEDIT_I18NHDL(MSG_SYS_OPTDESC_LOAD_A), 0,
                    SYS_OPT_F_ALL, 0 },
                { NULL }
        };
        static elfedit_cmd_optarg_t arg_load[] = {
                { MSG_ORIG(MSG_STR_MODNAME),
                    /* MSG_INTL(MSG_ARGDESC_LOAD_MODNAME) */
                    ELFEDIT_I18NHDL(MSG_ARGDESC_LOAD_MODNAME),
                    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
                { NULL }
        };

        /* sys:quit */
        static const char *name_quit[] = { MSG_ORIG(MSG_SYS_CMD_QUIT),
            MSG_ORIG(MSG_SYS_CMD_QUIT_A1), MSG_ORIG(MSG_SYS_CMD_QUIT_A2),
            NULL };
        static elfedit_cmd_optarg_t opt_quit[] = {
                { MSG_ORIG(MSG_STR_MINUS_F),
                    /* MSG_INTL(MSG_SYS_OPTDESC_QUIT_F) */
                    ELFEDIT_I18NHDL(MSG_SYS_OPTDESC_QUIT_F), 0,
                    SYS_OPT_F_FORCE, 0 },
                { NULL }
        };

        /* sys:status */
        static const char *name_status[] = {
            MSG_ORIG(MSG_SYS_CMD_STATUS), NULL };

        /* sys:set */
        static const char *name_set[] = {
            MSG_ORIG(MSG_SYS_CMD_SET), NULL };
        static elfedit_cmd_optarg_t arg_set[] = {
                { MSG_ORIG(MSG_STR_OPTION),
                    /* MSG_INTL(MSG_ARGDESC_SET_OPTION) */
                    ELFEDIT_I18NHDL(MSG_ARGDESC_SET_OPTION), 0 },
                { MSG_ORIG(MSG_STR_VALUE),
                    /* MSG_INTL(MSG_ARGDESC_SET_VALUE) */
                    ELFEDIT_I18NHDL(MSG_ARGDESC_SET_VALUE), 0 },
                { NULL }
        };

        /* sys:unload */
        static const char *name_unload[] = {
            MSG_ORIG(MSG_SYS_CMD_UNLOAD), NULL };
        static elfedit_cmd_optarg_t opt_unload[] = {
                { MSG_ORIG(MSG_STR_MINUS_A),
                    /* MSG_INTL(MSG_SYS_OPTDESC_UNLOAD_A) */
                    ELFEDIT_I18NHDL(MSG_SYS_OPTDESC_UNLOAD_A), 0,
                    SYS_OPT_F_ALL, 0},
                { NULL }
        };
        static elfedit_cmd_optarg_t arg_unload[] = {
                { MSG_ORIG(MSG_STR_MODNAME),
                    /* MSG_INTL(MSG_ARGDESC_UNLOAD_MODNAME) */
                    ELFEDIT_I18NHDL(MSG_ARGDESC_UNLOAD_MODNAME),
                    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
                { NULL }
        };

        /* sys:write */
        static const char *name_write[] = { MSG_ORIG(MSG_SYS_CMD_WRITE),
            MSG_ORIG(MSG_SYS_CMD_WRITE_A1), MSG_ORIG(MSG_SYS_CMD_WRITE_A2),
            NULL };

        static elfedit_cmd_t cmds[] = {
                /* sym:help */
                { (elfedit_cmd_func_t *)cmd_help,
                    (elfedit_cmdcpl_func_t *)cpl_help, name_help,
                    /* MSG_INTL(MSG_SYS_DESC_HELP) */
                    ELFEDIT_I18NHDL(MSG_SYS_DESC_HELP),
                    /* MSG_INTL(MSG_SYS_HELP_HELP) */
                    ELFEDIT_I18NHDL(MSG_SYS_HELP_HELP),
                    opt_help, arg_help },

                /* sym:load */
                { (elfedit_cmd_func_t *)cmd_load,
                    (elfedit_cmdcpl_func_t *)cpl_load, name_load,
                    /* MSG_INTL(MSG_SYS_DESC_LOAD) */
                    ELFEDIT_I18NHDL(MSG_SYS_DESC_LOAD),
                    /* MSG_INTL(MSG_SYS_HELP_LOAD) */
                    ELFEDIT_I18NHDL(MSG_SYS_HELP_LOAD),
                    opt_load, arg_load },

                /* sym:quit */
                { (elfedit_cmd_func_t *)cmd_quit, NULL, name_quit,
                    /* MSG_INTL(MSG_SYS_DESC_QUIT) */
                    ELFEDIT_I18NHDL(MSG_SYS_DESC_QUIT),
                    /* MSG_INTL(MSG_SYS_HELP_QUIT) */
                    ELFEDIT_I18NHDL(MSG_SYS_HELP_QUIT),
                    opt_quit, NULL },

                /* sym:status */
                { (elfedit_cmd_func_t *)cmd_status, NULL, name_status,
                    /* MSG_INTL(MSG_SYS_DESC_STATUS) */
                    ELFEDIT_I18NHDL(MSG_SYS_DESC_STATUS),
                    /* MSG_INTL(MSG_SYS_HELP_STATUS) */
                    ELFEDIT_I18NHDL(MSG_SYS_HELP_STATUS),
                    NULL, NULL },

                /* sym:set */
                { (elfedit_cmd_func_t *)cmd_set,
                    (elfedit_cmdcpl_func_t *)cpl_set, name_set,
                    /* MSG_INTL(MSG_SYS_DESC_SET) */
                    ELFEDIT_I18NHDL(MSG_SYS_DESC_SET),
                    /* MSG_INTL(MSG_SYS_HELP_SET) */
                    ELFEDIT_I18NHDL(MSG_SYS_HELP_SET),
                    NULL, arg_set },

                /* sym:unload */
                { (elfedit_cmd_func_t *)cmd_unload,
                    (elfedit_cmdcpl_func_t *)cpl_unload, name_unload,
                    /* MSG_INTL(MSG_SYS_DESC_UNLOAD) */
                    ELFEDIT_I18NHDL(MSG_SYS_DESC_UNLOAD),
                    /* MSG_INTL(MSG_SYS_HELP_UNLOAD) */
                    ELFEDIT_I18NHDL(MSG_SYS_HELP_UNLOAD),
                    opt_unload, arg_unload },

                /* sym:write */
                { (elfedit_cmd_func_t *)cmd_write, NULL, name_write,
                    /* MSG_INTL(MSG_SYS_DESC_WRITE) */
                    ELFEDIT_I18NHDL(MSG_SYS_DESC_WRITE),
                    /* MSG_INTL(MSG_SYS_HELP_WRITE) */
                    ELFEDIT_I18NHDL(MSG_SYS_HELP_WRITE),
                    NULL, NULL},

                { NULL }
        };

        static elfedit_module_t module = {
            ELFEDIT_VER_CURRENT, MSG_ORIG(MSG_MOD_SYS),
            /* MSG_INTL(MSG_MOD_SYS_DESC) */
            ELFEDIT_I18NHDL(MSG_MOD_SYS_DESC),
            cmds, mod_i18nhdl_to_str };

        static MODLIST_T moddef = {
                NULL,           /* next */
                (elfeditGC_module_t *)&module,  /* Module definition */
                NULL,           /* Didn't dlopen() it, so NULL handle */
                NULL            /* Didn't dlopen() it, so no file path */
        };

        return (&moddef);
}