root/usr/src/cmd/sgs/elfedit/modules/common/cap.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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2012 Milan Jurik. All rights reserved.
 * Copyright 2022 Oxide Computer Company
 */

#include        <ctype.h>
#include        <elfedit.h>
#include        <sys/elf_SPARC.h>
#include        <strings.h>
#include        <debug.h>
#include        <conv.h>
#include        <cap_msg.h>


/*
 * Capabilities section
 */




/*
 * This module uses shared code for several of the commands.
 * It is sometimes necessary to know which specific command
 * is active.
 */
typedef enum {
        /* Dump command, used as module default to display dynamic section */
        CAP_CMD_T_DUMP =        0,      /* cap:dump */

        /* Commands that do not correspond directly to a specific DT tag */
        CAP_CMD_T_TAG =         1,      /* cap:tag */
        CAP_CMD_T_VALUE =       2,      /* cap:value */
        CAP_CMD_T_DELETE =      3,      /* cap:delete */
        CAP_CMD_T_MOVE =        4,      /* cap:shift */

        /* Commands that embody tag specific knowledge */
        CAP_CMD_T_HW1 =         5,      /* cap:hw1 */
        CAP_CMD_T_SF1 =         6,      /* cap:sf1 */
        CAP_CMD_T_HW2 =         7,      /* cap:hw2 */
        CAP_CMD_T_HW3 =         8,      /* cap:hw3 */
} CAP_CMD_T;



#ifndef _ELF64
/*
 * We supply this function for the msg module
 */
const char *
_cap_msg(Msg mid)
{
        return (gettext(MSG_ORIG(mid)));
}
#endif


/*
 * 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 cap_opt_t enum specifies a bit value for every optional
 * argument allowed by a command in this module.
 */
typedef enum {
        CAP_OPT_F_AND =         1,      /* -and: AND (&) values to dest */
        CAP_OPT_F_CMP =         2,      /* -cmp: Complement (~) values */
        CAP_OPT_F_CAPID =       4,      /* -capid id: elt limited to given */
                                        /*      capabilities group */
        CAP_OPT_F_CAPNDX =      8,      /* -capndx: elt is tag index, */
                                        /*      not name */
        CAP_OPT_F_OR =          16,     /* -or: OR (|) values to dest */
        CAP_OPT_F_STRVAL =      32      /* -s: value is string, not integer */
} cap_opt_t;


/*
 * A variable of type ARGSTATE is used by each command to maintain
 * information about the arguments and related things. It is
 * initialized by process_args(), and used by the other routines.
 */
typedef struct {
        elfedit_obj_state_t     *obj_state;
        struct {
                elfedit_section_t *sec; /* Capabilities section reference */
                Cap     *data;          /* Start of capabilities section data */
                Word    num;            /* # Capabilities elts */
                Boolean grp_set;        /* TRUE when cap group is set */
                Word    grp_start_ndx;  /* capabilities group starting index */
                Word    grp_end_ndx;    /* capabilities group ending index */
        } cap;
        struct {                        /* String table */
                elfedit_section_t *sec;
        } str;
        cap_opt_t       optmask;        /* Mask of options used */
        int             argc;           /* # of plain arguments */
        const char      **argv;         /* Plain arguments */
} ARGSTATE;



/*
 * Lookup the string table associated with the capabilities
 * section.
 *
 * entry:
 *      argstate - Argument state block
 *      required - If TRUE, failure to obtain a string table should be
 *              considered to be an error.
 *
 * exit:
 *      If a string table is found, argstate->str is updated to reference it.
 *      If no string table is found, and required is TRUE, an error is issued
 *      and this routine does not return to the caller. Otherwise, this
 *      routine returns quietly without modifying argstate->str.
 */
static void
argstate_add_str(ARGSTATE *argstate, Boolean required)
{
        /* String table already loaded? */
        if (argstate->str.sec != NULL)
                return;

        /*
         * We can't proceed if the capabilities section does not have
         * an associated string table.
         */
        if (argstate->cap.sec->sec_shdr->sh_info == 0) {
                /* Error if the operation requires a string table */
                if (required)
                        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOSTRTAB),
                            EC_WORD(argstate->cap.sec->sec_shndx),
                            argstate->cap.sec->sec_name);
                return;
        }

        argstate->str.sec = elfedit_sec_getstr(argstate->obj_state,
            argstate->cap.sec->sec_shdr->sh_info, 0);
}

/*
 * Given an index into the capabilities array, locate the index of the
 * initial element in its capabilities group, and the number of elements
 * in the group.
 */
static void
cap_group_extents(ARGSTATE *argstate, Word ndx, Word *ret_start_ndx,
    Word *ret_end_ndx)
{
        *ret_end_ndx = ndx;

        /*
         * The group starts with a non-NULL tag that is either the
         * first tag in the array, or is preceded by a NULL tag.
         */
        while ((ndx > 0) && (argstate->cap.data[ndx].c_tag == CA_SUNW_NULL))
                ndx--;
        while ((ndx > 0) && (argstate->cap.data[ndx - 1].c_tag != CA_SUNW_NULL))
                ndx--;
        *ret_start_ndx = ndx;


        /*
         * The group is terminated by a series of 1 or more NULL tags.
         */
        ndx = *ret_end_ndx;
        while (((ndx + 1) < argstate->cap.num) &&
            (argstate->cap.data[ndx].c_tag != CA_SUNW_NULL))
                ndx++;
        while (((ndx + 1) < argstate->cap.num) &&
            (argstate->cap.data[ndx + 1].c_tag == CA_SUNW_NULL))
                ndx++;
        *ret_end_ndx = ndx;
}

/*
 * If a CA_SUNW_ID element exists within the current capabilities group
 * in the given argument state, return the string pointer to the name.
 * Otherwise return a pointer to a descriptive "noname" string.
 */
static const char *
cap_group_id(ARGSTATE *argstate)
{
        Word            ndx = argstate->cap.grp_start_ndx;
        Cap             *cap = argstate->cap.data + ndx;

        for (; ndx <= argstate->cap.grp_end_ndx; ndx++, cap++) {
                if (cap->c_tag == CA_SUNW_ID) {
                        argstate_add_str(argstate, TRUE);
                        return (elfedit_offset_to_str(argstate->str.sec,
                            cap->c_un.c_val, ELFEDIT_MSG_ERR, 0));
                }

                if (cap->c_tag == CA_SUNW_NULL)
                        break;
        }

        return ((argstate->cap.grp_start_ndx == 0) ?
            MSG_INTL(MSG_STR_OBJECT) : MSG_INTL(MSG_STR_NONAME));
}


/*
 * Given an index into the capabilities array, set the argstate cap.grp_*
 * fields to reflect the capabilities group containing the index.
 *
 * The group concept is used to limit operations to a related group
 * of capabilities, and prevent insert/delete/move operations from
 * spilling across groups.
 */
static void
argstate_cap_group(ARGSTATE *argstate, Word ndx)
{
        if (argstate->cap.grp_set == TRUE)
                return;

        cap_group_extents(argstate, ndx, &argstate->cap.grp_start_ndx,
            &argstate->cap.grp_end_ndx);

        argstate->cap.grp_set = TRUE;
        elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_CAPGRP),
            EC_WORD(argstate->cap.sec->sec_shndx), argstate->cap.sec->sec_name,
            EC_WORD(argstate->cap.grp_start_ndx),
            EC_WORD(argstate->cap.grp_end_ndx), cap_group_id(argstate));
}

/*
 * Given an index into the capabilities array, issue a group title for
 * the capabilities group that contains it.
 */
static void
group_title(ARGSTATE *argstate, Word ndx)
{
        ARGSTATE        loc_argstate;

        loc_argstate = *argstate;
        cap_group_extents(argstate, ndx, &loc_argstate.cap.grp_start_ndx,
            &loc_argstate.cap.grp_end_ndx);
        elfedit_printf(MSG_INTL(MSG_FMT_CAPGRP),
            EC_WORD(loc_argstate.cap.grp_start_ndx),
            EC_WORD(loc_argstate.cap.grp_end_ndx), cap_group_id(&loc_argstate));
}

/*
 * Standard argument processing for cap module
 *
 * entry
 *      obj_state, argc, argv - Standard command arguments
 *      argstate - Address of ARGSTATE block to be initialized
 *
 * exit:
 *      On success, *argstate is initialized. On error,
 *      an error is issued and this routine does not return.
 */
static void
process_args(elfedit_obj_state_t *obj_state, int argc, const char *argv[],
    ARGSTATE *argstate)
{
        elfedit_getopt_state_t  getopt_state;
        elfedit_getopt_ret_t    *getopt_ret;
        const char              *capid = NULL;

        bzero(argstate, sizeof (*argstate));
        argstate->obj_state = obj_state;

        elfedit_getopt_init(&getopt_state, &argc, &argv);

        /* Add each new option to the options mask */
        while ((getopt_ret = elfedit_getopt(&getopt_state)) != NULL) {
                argstate->optmask |= getopt_ret->gor_idmask;

                if (getopt_ret->gor_idmask == CAP_OPT_F_CAPID)
                        capid = getopt_ret->gor_value;
        }

        /* If there may be an arbitrary amount of output, use a pager */
        if (argc == 0)
                elfedit_pager_init();

        /* Return the updated values of argc/argv */
        argstate->argc = argc;
        argstate->argv = argv;

        /* Locate the capabilities section */
        argstate->cap.sec = elfedit_sec_getcap(obj_state, &argstate->cap.data,
            &argstate->cap.num);

        /*
         * If -capid was specified, locate the specified capabilities group,
         * and narrow the section data to use only that group. Otherwise,
         * use the whole array.
         */
        if (capid != NULL) {
                Word    i;
                Cap     *cap = argstate->cap.data;

                /*
                 * -capid requires the capability section to have an
                 * associated string table.
                 */
                argstate_add_str(argstate, TRUE);

                for (i = 0; i < argstate->cap.num; i++, cap++)
                        if ((cap->c_tag == CA_SUNW_ID) &&
                            (strcmp(capid, elfedit_offset_to_str(
                            argstate->str.sec, cap->c_un.c_val,
                            ELFEDIT_MSG_ERR, 0)) == 0))
                                break;

                if (i == argstate->cap.num)
                        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADCAPID),
                            EC_WORD(argstate->cap.sec->sec_shndx),
                            argstate->cap.sec->sec_name, capid);
                argstate_cap_group(argstate, i);
        } else {
                argstate->cap.grp_start_ndx = 0;
                argstate->cap.grp_end_ndx = argstate->cap.num - 1;
        }
}



/*
 * Print ELF capabilities values, taking the calling command, and output style
 * into account.
 *
 * entry:
 *      cmd - CAP_CMD_T_* value giving identify of caller
 *      autoprint - If True, output is only produced if the elfedit
 *              autoprint flag is set. If False, output is always produced.
 *      argstate - Argument state block
 *      print_type - Specifies which capabilities elements to display.
 *      ndx = If print_type is PRINT_CAP_T_NDX, displays the index specified.
 *              Otherwise ignored.
 */
typedef enum {
        PRINT_CAP_T_ALL =       0,      /* Show all indexes */
        PRINT_CAP_T_NDX =       1,      /* Show capabilities[arg] only */
        PRINT_CAP_T_TAG =       2       /* Show all elts with tag type */
                                        /*      given by arg */
} PRINT_CAP_T;

static void
print_cap(CAP_CMD_T cmd, int autoprint, ARGSTATE *argstate,
    PRINT_CAP_T print_type, Word arg)
{
        elfedit_outstyle_t      outstyle;
        Word            cnt, ndx, printed = 0;
        Cap             *cap;
        Boolean         header_done = FALSE, null_seen = FALSE;
        const char      *str;
        size_t          str_size;

        if (autoprint && ((elfedit_flags() & ELFEDIT_F_AUTOPRINT) == 0))
                return;

        /*
         * Pick an output style. cap:dump is required to use the default
         * style. The other commands use the current output style.
         */
        outstyle = (cmd == CAP_CMD_T_DUMP) ?
            ELFEDIT_OUTSTYLE_DEFAULT : elfedit_outstyle();

        /* How many elements do we examine? */
        if (print_type == PRINT_CAP_T_NDX) {
                if (arg >= argstate->cap.num)
                        return;         /* Out of range */
                ndx = arg;
                cnt = 1;
        } else {
                ndx = argstate->cap.grp_start_ndx;
                cnt = argstate->cap.grp_end_ndx - ndx + 1;
        }

        /* Load string table if there is one */
        argstate_add_str(argstate, FALSE);
        if (argstate->str.sec == NULL) {
                str = NULL;
                str_size = 0;
        } else {
                str = (const char *)argstate->str.sec->sec_data->d_buf;
                str_size = argstate->str.sec->sec_data->d_size;
        }

        cap = &argstate->cap.data[ndx];
        for (; cnt--; cap++, ndx++) {
                /*
                 * If we are only displaying certain tag types and
                 * this isn't one of those, move on to next element.
                 */
                if ((print_type == PRINT_CAP_T_TAG) && (cap->c_tag != arg)) {
                        if (cap->c_tag == CA_SUNW_NULL)
                                null_seen = TRUE;
                        continue;
                }

                /*
                 * If capability type requires a string table, and we don't
                 * have one, force an error.
                 */
                switch (cap->c_tag) {
                case CA_SUNW_PLAT:
                case CA_SUNW_MACH:
                case CA_SUNW_ID:
                        if (argstate->str.sec == NULL)
                                argstate_add_str(argstate, TRUE);
                        break;
                }

                if (outstyle == ELFEDIT_OUTSTYLE_DEFAULT) {
                        if (null_seen && (cap->c_tag != CA_SUNW_NULL)) {
                                null_seen = FALSE;
                                if (header_done) {
                                        elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
                                            MSG_ORIG(MSG_STR_EMPTY));
                                        header_done = FALSE;
                                }
                        }

                        if (header_done == FALSE) {
                                header_done = TRUE;
                                group_title(argstate, ndx);
                                Elf_cap_title(0);
                        }
                        Elf_cap_entry(NULL, cap, ndx, str, str_size,
                            argstate->obj_state->os_ehdr->e_machine);
                } else {
                        /*
                         * If CAP_CMD_T_TAG, and not in default output
                         * style, display the tag rather than the value.
                         */
                        if (cmd == CAP_CMD_T_TAG) {
                                if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
                                        Conv_inv_buf_t  inv_buf;

                                        elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
                                            conv_cap_tag(cap->c_tag, 0,
                                            &inv_buf));
                                } else {
                                        elfedit_printf(
                                            MSG_ORIG(MSG_FMT_WORDVALNL),
                                            EC_WORD(cap->c_tag));
                                }
                                printed = 1;
                                continue;
                        }

                        /* Displaying the value in simple or numeric mode */
                        if (outstyle == ELFEDIT_OUTSTYLE_SIMPLE) {
                                Conv_cap_val_buf_t      cap_val_buf;

                                if (print_type == PRINT_CAP_T_TAG) {
                                        elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
                                            conv_cap_val_hw1(cap->c_un.c_val,
                                            argstate->obj_state->os_ehdr->
                                            e_machine, CONV_FMT_NOBKT,
                                            &cap_val_buf.cap_val_hw1_buf));
                                        printed = 1;
                                        continue;
                                }

                                switch (cap->c_tag) {
                                case CA_SUNW_HW_1:
                                        elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
                                            conv_cap_val_hw1(cap->c_un.c_val,
                                            argstate->obj_state->os_ehdr->
                                            e_machine, CONV_FMT_NOBKT,
                                            &cap_val_buf.cap_val_hw1_buf));
                                        printed = 1;
                                        continue;
                                case CA_SUNW_SF_1:
                                        elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
                                            conv_cap_val_sf1(cap->c_un.c_val,
                                            argstate->obj_state->os_ehdr->
                                            e_machine, CONV_FMT_NOBKT,
                                            &cap_val_buf.cap_val_sf1_buf));
                                        printed = 1;
                                        continue;
                                case CA_SUNW_HW_2:
                                        elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
                                            conv_cap_val_hw2(cap->c_un.c_val,
                                            argstate->obj_state->os_ehdr->
                                            e_machine, CONV_FMT_NOBKT,
                                            &cap_val_buf.cap_val_hw2_buf));
                                        printed = 1;
                                        continue;
                                case CA_SUNW_PLAT:
                                case CA_SUNW_MACH:
                                case CA_SUNW_ID:
                                        elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
                                            elfedit_offset_to_str(
                                            argstate->str.sec, cap->c_un.c_val,
                                            ELFEDIT_MSG_ERR, 0));
                                        printed = 1;
                                        continue;
                                case CA_SUNW_HW_3:
                                        elfedit_printf(MSG_ORIG(MSG_FMT_STRNL),
                                            conv_cap_val_hw3(cap->c_un.c_val,
                                            argstate->obj_state->os_ehdr->
                                            e_machine, CONV_FMT_NOBKT,
                                            &cap_val_buf.cap_val_hw3_buf));
                                        printed = 1;
                                        continue;
                                }
                        }
                        elfedit_printf(MSG_ORIG(MSG_FMT_HEXXWORDNL),
                            EC_XWORD(cap->c_un.c_val));
                }
                printed = 1;
                if (cap->c_tag == CA_SUNW_NULL)
                        null_seen = TRUE;
        }

        /*
         * If nothing was output under the print types that are
         * based on tag type, issue an error saying it doesn't exist.
         */
        if (!printed && (print_type == PRINT_CAP_T_TAG)) {
                Conv_inv_buf_t  inv_buf;

                elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOCAELT),
                    EC_WORD(argstate->cap.sec->sec_shndx),
                    argstate->cap.sec->sec_name, argstate->cap.grp_start_ndx,
                    argstate->cap.grp_end_ndx, cap_group_id(argstate),
                    conv_cap_tag(arg, 0, &inv_buf));
        }
}


/*
 * Process the elt argument: This will be a tag type if -capndx is
 * not present and this is a print request. It will be an index otherwise.
 *
 * entry:
 *      argstate - Argument state block
 *      arg - Argument string to be converted into an index
 *      argname - String giving the name by which the argument is
 *              referred in the online help for the command.
 *      print_request - True if the command is to print the current
 *              value(s) and return without changing anything.
 *      print_type - Address of variable containing PRINT_CAP_T_
 *              code specifying how the elements will be displayed.
 *
 * exit:
 *      If print_request is False: arg is converted into an integer value.
 *      If -capndx was used, we convert it into an integer. If it was not
 *      used, then arg is a tag name --- we find the first capabilities entry
 *      that matches. If no entry matches, and there is an extra CA_NULL,
 *      it is added. Otherwise an error is issued. *print_type is set
 *      to PRINT_CAP_T_NDX.
 *
 *      If print_request is True: If -capndx was used, arg is converted into
 *      an integer value, *print_type is set to PRINT_CAP_T_NDX, and
 *      the value is returned. If -capndx was not used, *print_type is set to
 *      PRINT_CAP_T_TAG, and the tag value is returned.
 */
static Word
arg_to_index(ARGSTATE *argstate, const char *arg, const char *argname,
    int print_request, PRINT_CAP_T *print_type)
{
        Word            ndx, ca_value;


        /* Assume we are returning an index, alter as needed below */
        *print_type = PRINT_CAP_T_NDX;

        /*
         * If -capndx was used, this is a simple numeric index.
         * Determine its capability group because some operations
         * (move, delete) are limited to operate within it.
         */
        if ((argstate->optmask & CAP_OPT_F_CAPNDX) != 0) {
                ndx = (Word) elfedit_atoui_range(arg, argname, 0,
                    argstate->cap.num - 1, NULL);
                argstate_cap_group(argstate, ndx);
                return (ndx);
        }

        /* The argument is a CA_ tag type, not a numeric index */
        ca_value = (Word) elfedit_atoconst(arg, ELFEDIT_CONST_CA);

        /*
         * If this is a printing request, then we let print_cap() show
         * all the items with this tag type.
         */
        if (print_request) {
                *print_type = PRINT_CAP_T_TAG;
                return (ca_value);
        }

        /*
         * If we haven't determined a capability group yet, either via
         * -capid, or -capndx, then make it the initial group, which
         * represent the object capabilities.
         */
        if (!argstate->cap.grp_set)
                argstate_cap_group(argstate, 0);

        /*
         * Locate the first entry with the given tag type within the
         * capabilities group.
         */
        for (ndx = argstate->cap.grp_start_ndx;
            ndx <= argstate->cap.grp_end_ndx; ndx++) {
                if (argstate->cap.data[ndx].c_tag == ca_value) {
                        elfedit_msg(ELFEDIT_MSG_DEBUG,
                            MSG_INTL(MSG_DEBUG_CA2NDX),
                            EC_WORD(argstate->cap.sec->sec_shndx),
                            argstate->cap.sec->sec_name, EC_WORD(ndx), arg);
                        return (ndx);
                }

                /*
                 * If we hit a NULL, then only more NULLs can follow it and
                 * there's no need to look further. If there is more than
                 * one NULL, we can grab the first one and turn it into
                 * an element of the desired type.
                 */
                if (argstate->cap.data[ndx].c_tag == CA_SUNW_NULL) {
                        if (ndx < argstate->cap.grp_end_ndx) {
                                Conv_inv_buf_t  inv_buf;

                                elfedit_msg(ELFEDIT_MSG_DEBUG,
                                    MSG_INTL(MSG_DEBUG_CONVNULL),
                                    EC_WORD(argstate->cap.sec->sec_shndx),
                                    argstate->cap.sec->sec_name, EC_WORD(ndx),
                                    conv_cap_tag(ca_value, 0, &inv_buf));
                                argstate->cap.data[ndx].c_tag = ca_value;
                                bzero(&argstate->cap.data[ndx].c_un,
                                    sizeof (argstate->cap.data[ndx].c_un));
                                return (ndx);
                        }
                        break;
                }
        }

        /* No room to create one, so we're out of options and must fail */
        elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_NOCAELT),
            EC_WORD(argstate->cap.sec->sec_shndx),
            argstate->cap.sec->sec_name, argstate->cap.grp_start_ndx,
            argstate->cap.grp_end_ndx, cap_group_id(argstate), arg);

        /*NOTREACHED*/
        return (0);             /* For lint */
}


/*
 * Argument processing for the bitmask commands. Convert the arguments
 * to integer form, apply -and/-cmp/-or, and return the resulting value.
 *
 * entry:
 *      argstate - Argument state block
 *      orig - Value of original bitmask
 *      const_sym - NULL, or array of name->integer mappings for
 *              applicable symbolic constant names.
 */
static Word
flag_bitop(ARGSTATE *argstate, Word orig, const elfedit_atoui_sym_t *const_sym)
{
        Word flags = 0;
        int i;

        /* Collect the arguments */
        for (i = 0; i < argstate->argc; i++)
                flags |= (Word) elfedit_atoui(argstate->argv[i], const_sym);

        /* Complement the value? */
        if (argstate->optmask & CAP_OPT_F_CMP)
                flags = ~flags;

        /* Perform any requested bit operations */
        if (argstate->optmask & CAP_OPT_F_AND)
                flags &= orig;
        else if (argstate->optmask & CAP_OPT_F_OR)
                flags |= orig;

        return (flags);
}

/*
 * Common processing for capabilities value setting.
 *
 * entry:
 *      argstate - Argument state block
 *      cap - capabilities data pointer
 *      ndx - capabilities data index
 *      cap_ndx - capabilities section index
 *      cap_name - capabilities section name
 *      cap_tag - capabilities tag
 *      const_type - data conversion type
 */
static elfedit_cmdret_t
cap_set(ARGSTATE *argstate, Cap *cap, Word ndx, Word cap_ndx,
    const char *cap_name, Xword cap_tag, elfedit_const_t const_type)
{
        Conv_cap_val_buf_t      buf1, buf2;
        Half                    mach = argstate->obj_state->os_ehdr->e_machine;
        Xword                   ncap, ocap;

        ncap = flag_bitop(argstate, cap[ndx].c_un.c_val,
            elfedit_const_to_atoui(const_type));

        /* Set the value */
        if ((ocap = cap[ndx].c_un.c_val) == ncap) {
                elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_BSB_OK),
                    cap_ndx, cap_name, EC_WORD(ndx),
                    conv_cap_val(cap_tag, ocap, mach, CONV_FMT_NOBKT, &buf1));

                return (ELFEDIT_CMDRET_NONE);
        } else {
                elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_BSB_CHG),
                    cap_ndx, cap_name, EC_WORD(ndx),
                    conv_cap_val(cap_tag, ocap, mach, CONV_FMT_NOBKT, &buf1),
                    conv_cap_val(cap_tag, ncap, mach, CONV_FMT_NOBKT, &buf2));

                cap[ndx].c_un.c_val = ncap;
                return (ELFEDIT_CMDRET_MOD);
        }
}

/*
 * Common body for the cap: module commands. These commands
 * share a large amount of common behavior, so it is convenient
 * to centralize things and use the cmd argument to handle the
 * small differences.
 *
 * entry:
 *      cmd - One of the CAP_CMD_T_* constants listed above, specifying
 *              which command to implement.
 *      obj_state, argc, argv - Standard command arguments
 */
static elfedit_cmdret_t
cmd_body(CAP_CMD_T cmd, elfedit_obj_state_t *obj_state,
    int argc, const char *argv[])
{
        ARGSTATE                argstate;
        Cap                     *cap;
        const char              *cap_name;
        Word                    cap_ndx;
        elfedit_cmdret_t        ret = ELFEDIT_CMDRET_NONE;
        PRINT_CAP_T             print_type = PRINT_CAP_T_ALL;
        Word                    ndx;
        int                     print_only = 0;
        int                     do_autoprint = 1;

        /* Process the optional arguments */
        process_args(obj_state, argc, argv, &argstate);

        cap = argstate.cap.data;
        cap_name = argstate.cap.sec->sec_name;
        cap_ndx = argstate.cap.sec->sec_shndx;

        /* Check number of arguments, gather information */
        switch (cmd) {
        case CAP_CMD_T_DUMP:
                /* cap:dump can accept an optional index argument */
                if (argstate.argc > 1)
                        elfedit_command_usage();
                print_only = 1;
                if (argstate.argc == 1)
                        ndx = arg_to_index(&argstate, argstate.argv[0],
                            MSG_ORIG(MSG_STR_ELT), print_only, &print_type);
                break;

        case CAP_CMD_T_TAG:
        case CAP_CMD_T_VALUE:
                print_only = (argstate.argc != 2);
                if (argstate.argc > 0) {
                        if (argstate.argc > 2)
                                elfedit_command_usage();
                        ndx = arg_to_index(&argstate, argstate.argv[0],
                            MSG_ORIG(MSG_STR_ELT), print_only, &print_type);
                }
                break;

        case CAP_CMD_T_DELETE:
                if ((argstate.argc < 1) || (argstate.argc > 2))
                        elfedit_command_usage();
                ndx = arg_to_index(&argstate, argstate.argv[0],
                    MSG_ORIG(MSG_STR_ELT),
                    0, &print_type);
                do_autoprint = 0;
                break;

        case CAP_CMD_T_MOVE:
                if ((argstate.argc < 2) || (argstate.argc > 3))
                        elfedit_command_usage();
                ndx = arg_to_index(&argstate, argstate.argv[0],
                    MSG_ORIG(MSG_STR_ELT), 0, &print_type);
                do_autoprint = 0;
                break;

        case CAP_CMD_T_HW1:
                print_only = (argstate.argc == 0);
                ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str(
                    ELFEDIT_CONST_CA, CA_SUNW_HW_1, 1),
                    MSG_ORIG(MSG_STR_VALUE), print_only, &print_type);
                break;

        case CAP_CMD_T_SF1:
                print_only = (argstate.argc == 0);
                ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str(
                    ELFEDIT_CONST_CA, CA_SUNW_SF_1, 1),
                    MSG_ORIG(MSG_STR_VALUE), print_only, &print_type);
                break;

        case CAP_CMD_T_HW2:
                print_only = (argstate.argc == 0);
                ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str(
                    ELFEDIT_CONST_CA, CA_SUNW_HW_2, 1),
                    MSG_ORIG(MSG_STR_VALUE), print_only, &print_type);
                break;

        case CAP_CMD_T_HW3:
                print_only = (argstate.argc == 0);
                ndx = arg_to_index(&argstate, elfedit_atoconst_value_to_str(
                    ELFEDIT_CONST_CA, CA_SUNW_HW_3, 1),
                    MSG_ORIG(MSG_STR_VALUE), print_only, &print_type);
                break;

        default:
                /* Note expected: All commands should have been caught above */
                elfedit_command_usage();
                break;
        }


        /* If this is a request to print current values, do it and return */
        if (print_only) {
                print_cap(cmd, 0, &argstate, print_type, ndx);
                return (ELFEDIT_CMDRET_NONE);
        }


        switch (cmd) {
                /*
                 * CAP_CMD_T_DUMP can't get here: It is a print-only
                 * command.
                 */

        case CAP_CMD_T_TAG:
                {
                        Conv_inv_buf_t  inv_buf1, inv_buf2;
                        Word c_tag = (Word) elfedit_atoconst(argstate.argv[1],
                            ELFEDIT_CONST_CA);

                        if (cap[ndx].c_tag == c_tag) {
                                elfedit_msg(ELFEDIT_MSG_DEBUG,
                                    MSG_INTL(MSG_DEBUG_S_OK),
                                    cap_ndx, cap_name, EC_WORD(ndx),
                                    conv_cap_tag(c_tag, 0, &inv_buf1));
                        } else {
                                elfedit_msg(ELFEDIT_MSG_DEBUG,
                                    MSG_INTL(MSG_DEBUG_S_CHG),
                                    cap_ndx, cap_name, EC_WORD(ndx),
                                    conv_cap_tag(cap[ndx].c_tag, 0, &inv_buf1),
                                    conv_cap_tag(c_tag, 0, &inv_buf2));
                                cap[ndx].c_tag = c_tag;
                                ret = ELFEDIT_CMDRET_MOD;
                        }
                }
                break;

        case CAP_CMD_T_VALUE:
                {
                        Xword c_val;

                        if (argstate.optmask & CAP_OPT_F_STRVAL) {
                                argstate_add_str(&argstate, TRUE);
                                c_val = elfedit_strtab_insert(obj_state,
                                    argstate.str.sec, NULL, argstate.argv[1]);
                        } else {
                                c_val = (Xword)
                                    elfedit_atoui(argstate.argv[1], NULL);
                        }

                        if (cap[ndx].c_un.c_val == c_val) {
                                elfedit_msg(ELFEDIT_MSG_DEBUG,
                                    MSG_INTL(MSG_DEBUG_X_OK),
                                    argstate.cap.sec->sec_shndx,
                                    argstate.cap.sec->sec_name,
                                    EC_WORD(ndx), EC_XWORD(c_val));
                        } else {
                                elfedit_msg(ELFEDIT_MSG_DEBUG,
                                    MSG_INTL(MSG_DEBUG_X_CHG),
                                    argstate.cap.sec->sec_shndx,
                                    argstate.cap.sec->sec_name,
                                    EC_WORD(ndx), EC_XWORD(cap[ndx].c_un.c_val),
                                    EC_XWORD(c_val));
                                cap[ndx].c_un.c_val = c_val;
                                ret = ELFEDIT_CMDRET_MOD;
                        }
                }
                break;

        case CAP_CMD_T_DELETE:
                {
                        Word cnt = (argstate.argc == 1) ? 1 :
                            (Word) elfedit_atoui_range(argstate.argv[1],
                            MSG_ORIG(MSG_STR_COUNT), 1,
                            argstate.cap.grp_end_ndx - ndx + 1, NULL);
                        const char *msg_prefix =
                            elfedit_sec_msgprefix(argstate.cap.sec);

                        /*
                         * We want to limit the deleted elements to be
                         * in the range of the current capabilities group,
                         * and for the resulting NULL elements to be inserted
                         * at the end of the group, rather than at the end
                         * of the section. To do this, we set the array length
                         * in the call to the delete function so that it thinks
                         * the array ends with the current group.
                         *
                         * The delete function will catch attempts to delete
                         * past this virtual end, but the error message will
                         * not make sense to the user. In order to prevent that,
                         * we check for the condition here and provide a more
                         * useful error.
                         */
                        if ((ndx + cnt - 1) > argstate.cap.grp_end_ndx)
                                elfedit_msg(ELFEDIT_MSG_ERR,
                                    MSG_INTL(MSG_ERR_GRPARRBNDS), msg_prefix,
                                    argstate.cap.grp_start_ndx,
                                    argstate.cap.grp_end_ndx,
                                    cap_group_id(&argstate));
                        elfedit_array_elts_delete(msg_prefix, cap, sizeof (Cap),
                            argstate.cap.grp_end_ndx + 1, ndx, cnt);
                        ret = ELFEDIT_CMDRET_MOD;
                }
                break;

        case CAP_CMD_T_MOVE:
                {
                        Cap     save;
                        Word    cnt;
                        Word    dstndx;
                        const char *msg_prefix =
                            elfedit_sec_msgprefix(argstate.cap.sec);

                        dstndx = (Word)
                            elfedit_atoui_range(argstate.argv[1],
                            MSG_ORIG(MSG_STR_DST_INDEX),
                            argstate.cap.grp_start_ndx,
                            argstate.cap.grp_end_ndx, NULL);
                        if (argstate.argc == 2) {
                                cnt = 1;
                        } else {
                                Word max;

                                max = argstate.cap.grp_end_ndx -
                                    ((ndx > dstndx) ? ndx : dstndx) + 1;
                                cnt = (Word) elfedit_atoui_range(
                                    argstate.argv[2], MSG_ORIG(MSG_STR_COUNT),
                                    1, max, NULL);
                        }

                        /*
                         * Moves are required to be self contained within
                         * the bounds of the selected capability group.
                         * The move utility function contains bounds checking,
                         * but is not sub-array aware. Hence, we bounds check
                         * check it here, and then hand of the validated
                         * operation to the move utility function to execute.
                         */
                        if ((ndx < argstate.cap.grp_start_ndx) ||
                            ((ndx + cnt) > argstate.cap.grp_end_ndx) ||
                            (dstndx < argstate.cap.grp_start_ndx) ||
                            ((dstndx + cnt) > argstate.cap.grp_end_ndx))
                                elfedit_msg(ELFEDIT_MSG_ERR,
                                    MSG_INTL(MSG_ERR_GRPARRBNDS), msg_prefix,
                                    argstate.cap.grp_start_ndx,
                                    argstate.cap.grp_end_ndx,
                                    cap_group_id(&argstate));
                        elfedit_array_elts_move(msg_prefix, cap, sizeof (save),
                            argstate.cap.grp_end_ndx + 1, ndx, dstndx,
                            cnt, &save);
                        ret = ELFEDIT_CMDRET_MOD;
                }
                break;


        case CAP_CMD_T_HW1:
                {
                        ret = cap_set(&argstate, cap, ndx, cap_ndx, cap_name,
                            CA_SUNW_HW_1, ELFEDIT_CONST_HW1_SUNW);
                }
                break;

        case CAP_CMD_T_SF1:
                {
                        ret = cap_set(&argstate, cap, ndx, cap_ndx, cap_name,
                            CA_SUNW_SF_1, ELFEDIT_CONST_SF1_SUNW);
                }
                break;

        case CAP_CMD_T_HW2:
                {
                        ret = cap_set(&argstate, cap, ndx, cap_ndx, cap_name,
                            CA_SUNW_HW_2, ELFEDIT_CONST_HW2_SUNW);
                }
                break;

        case CAP_CMD_T_HW3:
                {
                        ret = cap_set(&argstate, cap, ndx, cap_ndx, cap_name,
                            CA_SUNW_HW_3, ELFEDIT_CONST_HW3_SUNW);
                }
                break;

        }

        /*
         * If we modified the capabilities section header, tell libelf.
         */
        if (ret == ELFEDIT_CMDRET_MOD)
                elfedit_modified_data(argstate.cap.sec);

        /* Do autoprint */
        if (do_autoprint)
                print_cap(cmd, 1, &argstate, print_type, ndx);

        return (ret);
}



/*
 * Command completion functions for the commands
 */

/*
 * -capid command completion: Supply all CA_SUNW_ID names found in the object.
 */
static void
cpl_capid_opt(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
        elfedit_section_t       *cap_sec, *str_sec;
        Cap                     *cap;
        Word                    num;

        if (obj_state == NULL)   /* No object available */
                return;

        if ((argc > num_opt) || (argc < 2) ||
            (strcmp(argv[argc - 2], MSG_ORIG(MSG_STR_MINUS_CAPID)) != 0))
                return;

        cap_sec = elfedit_sec_getcap(obj_state, &cap, &num);

        /* If no associated string table, we have no strings to complete */
        if (cap_sec->sec_shdr->sh_info == 0)
                return;

        str_sec = elfedit_sec_getstr(obj_state, cap_sec->sec_shdr->sh_info, 0);

        for (; num--; cap++)
                if (cap->c_tag == CA_SUNW_ID)
                        elfedit_cpl_match(cpldata, elfedit_offset_to_str(
                            str_sec, cap->c_un.c_val, ELFEDIT_MSG_ERR, 0), 0);
}

/*
 * Command completion for the first argument, which specifies
 * the capabilities element to use. Examines the options to see if
 * -capndx is present, and if not, supplies the completion
 * strings for argument 1.
 */
/*ARGSUSED*/
static void
cpl_eltarg(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
        Word    i;

        /* -capid id_name */
        if (argc <= num_opt) {
                cpl_capid_opt(obj_state, cpldata, argc, argv, num_opt);
                return;
        }

        /* Make sure it's the first argument */
        if ((argc - num_opt) != 1)
                return;

        /* Is -capndx present? If so, we don't complete tag types */
        for (i = 0; i < num_opt; i++)
                if (strcmp(argv[i], MSG_ORIG(MSG_STR_MINUS_CAPNDX)) == 0)
                        return;

        /*
         * Supply capability tag names. There are very few of these, so
         * rather than worry about whether a given tag exists in the
         * file or not, we simply serve up all the possibilities.
         */
        elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_CA);
}

/*ARGSUSED*/
static void
cpl_tag(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
        /* -capid id_name */
        if (argc <= num_opt) {
                cpl_capid_opt(obj_state, cpldata, argc, argv, num_opt);
                return;
        }

        /* First plain argument */
        if ((argc - num_opt) == 1) {
                cpl_eltarg(obj_state, cpldata, argc, argv, num_opt);
                return;
        }

        /* The second argument is always a tag value */
        if ((argc - num_opt) == 2)
                elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_CA);
}

/*ARGSUSED*/
static void
cpl_hw1(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
        /* -capid id_name */
        if (argc <= num_opt) {
                cpl_capid_opt(obj_state, cpldata, argc, argv, num_opt);
                return;
        }

        /* This routine allows multiple flags to be specified */
        elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_HW1_SUNW);
}

/*ARGSUSED*/
static void
cpl_sf1(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
        /* -capid id_name */
        if (argc <= num_opt) {
                cpl_capid_opt(obj_state, cpldata, argc, argv, num_opt);
                return;
        }

        /* This routine allows multiple flags to be specified */
        elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_SF1_SUNW);
}

/*ARGSUSED*/
static void
cpl_hw2(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
        /* -capid id_name */
        if (argc <= num_opt) {
                cpl_capid_opt(obj_state, cpldata, argc, argv, num_opt);
                return;
        }

        /* This routine allows multiple flags to be specified */
        elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_HW2_SUNW);
}

static void
cpl_hw3(elfedit_obj_state_t *obj_state, void *cpldata, int argc,
    const char *argv[], int num_opt)
{
        /* -capid id_name */
        if (argc <= num_opt) {
                cpl_capid_opt(obj_state, cpldata, argc, argv, num_opt);
                return;
        }

        /* This routine allows multiple flags to be specified */
        elfedit_cpl_atoconst(cpldata, ELFEDIT_CONST_HW3_SUNW);
}
/*
 * Implementation functions for the commands
 */
static elfedit_cmdret_t
cmd_dump(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
        return (cmd_body(CAP_CMD_T_DUMP, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_tag(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
        return (cmd_body(CAP_CMD_T_TAG, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_value(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
        return (cmd_body(CAP_CMD_T_VALUE, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_delete(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
        return (cmd_body(CAP_CMD_T_DELETE, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_move(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
        return (cmd_body(CAP_CMD_T_MOVE, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_hw1(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
        return (cmd_body(CAP_CMD_T_HW1, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_sf1(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
        return (cmd_body(CAP_CMD_T_SF1, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_hw2(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
        return (cmd_body(CAP_CMD_T_HW2, obj_state, argc, argv));
}

static elfedit_cmdret_t
cmd_hw3(elfedit_obj_state_t *obj_state, int argc, const char *argv[])
{
        return (cmd_body(CAP_CMD_T_HW3, obj_state, argc, argv));
}

/*ARGSUSED*/
elfedit_module_t *
elfedit_init(elfedit_module_version_t version)
{
        /* For commands that only accept -capid, -and, -cmp, -o, and -or */
        static elfedit_cmd_optarg_t opt_ostyle_capid_bitop[] = {
                { ELFEDIT_STDOA_OPT_AND, 0,
                    ELFEDIT_CMDOA_F_INHERIT, CAP_OPT_F_AND, CAP_OPT_F_OR },
                { MSG_ORIG(MSG_STR_MINUS_CAPID),
                    /* MSG_INTL(MSG_OPTDESC_CAPID) */
                    ELFEDIT_I18NHDL(MSG_OPTDESC_CAPID), ELFEDIT_CMDOA_F_VALUE,
                    CAP_OPT_F_CAPID, CAP_OPT_F_CAPNDX },
                { MSG_ORIG(MSG_STR_IDNAME), 0, 0 },
                { ELFEDIT_STDOA_OPT_CMP, 0,
                    ELFEDIT_CMDOA_F_INHERIT, CAP_OPT_F_CMP, 0 },
                { ELFEDIT_STDOA_OPT_O, 0,
                    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
                { ELFEDIT_STDOA_OPT_OR, 0,
                    ELFEDIT_CMDOA_F_INHERIT, CAP_OPT_F_OR, CAP_OPT_F_AND },
                { NULL }
        };

        /* For commands that only accept -capid and -capndx */
        static elfedit_cmd_optarg_t opt_capid_capndx[] = {
                { MSG_ORIG(MSG_STR_MINUS_CAPID),
                    /* MSG_INTL(MSG_OPTDESC_CAPID) */
                    ELFEDIT_I18NHDL(MSG_OPTDESC_CAPID), ELFEDIT_CMDOA_F_VALUE,
                    CAP_OPT_F_CAPID, CAP_OPT_F_CAPNDX },
                { MSG_ORIG(MSG_STR_IDNAME), 0, 0 },
                { MSG_ORIG(MSG_STR_MINUS_CAPNDX),
                    /* MSG_INTL(MSG_OPTDESC_CAPNDX) */
                    ELFEDIT_I18NHDL(MSG_OPTDESC_CAPNDX), 0,
                    CAP_OPT_F_CAPNDX, CAP_OPT_F_CAPID },
                { NULL }
        };


        /* cap:dump */
        static const char *name_dump[] = {
            MSG_ORIG(MSG_CMD_DUMP),
            MSG_ORIG(MSG_STR_EMPTY),    /* "" makes this the default command */
            NULL
        };
        static elfedit_cmd_optarg_t arg_dump[] = {
                { MSG_ORIG(MSG_STR_ELT),
                    /* MSG_INTL(MSG_ARGDESC_ELT) */
                    ELFEDIT_I18NHDL(MSG_ARGDESC_ELT),
                    ELFEDIT_CMDOA_F_OPT },
                { NULL }
        };


        /* cap:tag */
        static const char *name_tag[] = { MSG_ORIG(MSG_CMD_TAG), NULL };
        static elfedit_cmd_optarg_t opt_tag[] = {
                { MSG_ORIG(MSG_STR_MINUS_CAPID),
                    /* MSG_INTL(MSG_OPTDESC_CAPID) */
                    ELFEDIT_I18NHDL(MSG_OPTDESC_CAPID), ELFEDIT_CMDOA_F_VALUE,
                    CAP_OPT_F_CAPID, CAP_OPT_F_CAPNDX },
                { MSG_ORIG(MSG_STR_IDNAME), 0, 0 },
                { MSG_ORIG(MSG_STR_MINUS_CAPNDX),
                    /* MSG_INTL(MSG_OPTDESC_CAPNDX) */
                    ELFEDIT_I18NHDL(MSG_OPTDESC_CAPNDX), 0,
                    CAP_OPT_F_CAPNDX, 0 },
                { ELFEDIT_STDOA_OPT_O, 0,
                    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
                { NULL }
        };
        static elfedit_cmd_optarg_t arg_tag[] = {
                { MSG_ORIG(MSG_STR_ELT),
                    /* MSG_INTL(MSG_A1_TAG_ELT) */
                    ELFEDIT_I18NHDL(MSG_A1_TAG_ELT),
                    ELFEDIT_CMDOA_F_OPT },
                { MSG_ORIG(MSG_STR_VALUE),
                    /* MSG_INTL(MSG_A2_TAG_VALUE) */
                    ELFEDIT_I18NHDL(MSG_A2_TAG_VALUE),
                    ELFEDIT_CMDOA_F_OPT },
                { NULL }
        };


        /* cap:value */
        static const char *name_value[] = { MSG_ORIG(MSG_CMD_VALUE), NULL };
        static elfedit_cmd_optarg_t opt_value[] = {
                { MSG_ORIG(MSG_STR_MINUS_CAPID),
                    /* MSG_INTL(MSG_OPTDESC_CAPID) */
                    ELFEDIT_I18NHDL(MSG_OPTDESC_CAPID), ELFEDIT_CMDOA_F_VALUE,
                    CAP_OPT_F_CAPID, CAP_OPT_F_CAPNDX },
                { MSG_ORIG(MSG_STR_IDNAME), 0, 0 },
                { MSG_ORIG(MSG_STR_MINUS_CAPNDX),
                    /* MSG_INTL(MSG_OPTDESC_CAPNDX) */
                    ELFEDIT_I18NHDL(MSG_OPTDESC_CAPNDX), 0,
                    CAP_OPT_F_CAPNDX, 0 },
                { ELFEDIT_STDOA_OPT_O, 0,
                    ELFEDIT_CMDOA_F_INHERIT, 0, 0 },
                { MSG_ORIG(MSG_STR_MINUS_S),
                    /* MSG_INTL(MSG_OPTDESC_S) */
                    ELFEDIT_I18NHDL(MSG_OPTDESC_S), 0,
                    CAP_OPT_F_STRVAL, 0 },
                { NULL }
        };
        static elfedit_cmd_optarg_t arg_value[] = {
                { MSG_ORIG(MSG_STR_ELT),
                    /* MSG_INTL(MSG_ARGDESC_ELT) */
                    ELFEDIT_I18NHDL(MSG_ARGDESC_ELT),
                    ELFEDIT_CMDOA_F_OPT },
                { MSG_ORIG(MSG_STR_VALUE),
                    /* MSG_INTL(MSG_A2_VALUE_VALUE) */
                    ELFEDIT_I18NHDL(MSG_A2_VALUE_VALUE),
                    ELFEDIT_CMDOA_F_OPT },
                { NULL }
        };

        /* cap:delete */
        static const char *name_delete[] = { MSG_ORIG(MSG_CMD_DELETE), NULL };
        static elfedit_cmd_optarg_t arg_delete[] = {
                { MSG_ORIG(MSG_STR_ELT),
                    /* MSG_INTL(MSG_ARGDESC_ELT) */
                    ELFEDIT_I18NHDL(MSG_ARGDESC_ELT),
                    0 },
                { MSG_ORIG(MSG_STR_COUNT),
                    /* MSG_INTL(MSG_A2_DELETE_COUNT) */
                    ELFEDIT_I18NHDL(MSG_A2_DELETE_COUNT),
                    ELFEDIT_CMDOA_F_OPT },
                { NULL }
        };

        /* cap:move */
        static const char *name_move[] = { MSG_ORIG(MSG_CMD_MOVE), NULL };
        static elfedit_cmd_optarg_t arg_move[] = {
                { MSG_ORIG(MSG_STR_ELT),
                    /* MSG_INTL(MSG_ARGDESC_ELT) */
                    ELFEDIT_I18NHDL(MSG_ARGDESC_ELT),
                    0 },
                { MSG_ORIG(MSG_STR_DST_INDEX),
                    /* MSG_INTL(MSG_A2_MOVE_DST_INDEX) */
                    ELFEDIT_I18NHDL(MSG_A2_MOVE_DST_INDEX),
                    0 },
                { MSG_ORIG(MSG_STR_COUNT),
                    /* MSG_INTL(MSG_A3_MOVE_COUNT) */
                    ELFEDIT_I18NHDL(MSG_A3_MOVE_COUNT),
                    ELFEDIT_CMDOA_F_OPT },
                { NULL }
        };

        /* cap:hw1 */
        static const char *name_hw1[] = { MSG_ORIG(MSG_CMD_HW1), NULL };
        static elfedit_cmd_optarg_t arg_hw1[] = {
                { MSG_ORIG(MSG_STR_VALUE),
                    /* MSG_INTL(MSG_A1_HW1_VALUE) */
                    ELFEDIT_I18NHDL(MSG_A1_HW1_VALUE),
                    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
                { NULL }
        };

        /* cap:sf1 */
        static const char *name_sf1[] = { MSG_ORIG(MSG_CMD_SF1), NULL };
        static elfedit_cmd_optarg_t arg_sf1[] = {
                { MSG_ORIG(MSG_STR_VALUE),
                    /* MSG_INTL(MSG_A1_SF1_VALUE) */
                    ELFEDIT_I18NHDL(MSG_A1_SF1_VALUE),
                    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
                { NULL }
        };

        /* cap:hw2 */
        static const char *name_hw2[] = { MSG_ORIG(MSG_CMD_HW2), NULL };
        static elfedit_cmd_optarg_t arg_hw2[] = {
                { MSG_ORIG(MSG_STR_VALUE),
                    /* MSG_INTL(MSG_A1_HW2_VALUE) */
                    ELFEDIT_I18NHDL(MSG_A1_HW2_VALUE),
                    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
                { NULL }
        };

        /* cap:hw3 */
        static const char *name_hw3[] = { MSG_ORIG(MSG_CMD_HW3), NULL };
        static elfedit_cmd_optarg_t arg_hw3[] = {
                { MSG_ORIG(MSG_STR_VALUE),
                    /* MSG_INTL(MSG_A1_HW3_VALUE) */
                    ELFEDIT_I18NHDL(MSG_A1_HW3_VALUE),
                    ELFEDIT_CMDOA_F_OPT | ELFEDIT_CMDOA_F_MULT },
                { NULL }
        };

        static elfedit_cmd_t cmds[] = {
                /* cap:dump */
                { cmd_dump, cpl_eltarg, name_dump,
                    /* MSG_INTL(MSG_DESC_DUMP) */
                    ELFEDIT_I18NHDL(MSG_DESC_DUMP),
                    /* MSG_INTL(MSG_HELP_DUMP) */
                    ELFEDIT_I18NHDL(MSG_HELP_DUMP),
                    opt_capid_capndx, arg_dump },

                /* cap:tag */
                { cmd_tag, cpl_tag, name_tag,
                    /* MSG_INTL(MSG_DESC_TAG) */
                    ELFEDIT_I18NHDL(MSG_DESC_TAG),
                    /* MSG_INTL(MSG_HELP_TAG) */
                    ELFEDIT_I18NHDL(MSG_HELP_TAG),
                    opt_tag, arg_tag },

                /* cap:value */
                { cmd_value, cpl_eltarg, name_value,
                    /* MSG_INTL(MSG_DESC_VALUE) */
                    ELFEDIT_I18NHDL(MSG_DESC_VALUE),
                    /* MSG_INTL(MSG_HELP_VALUE) */
                    ELFEDIT_I18NHDL(MSG_HELP_VALUE),
                    opt_value, arg_value },

                /* cap:delete */
                { cmd_delete, cpl_eltarg, name_delete,
                    /* MSG_INTL(MSG_DESC_DELETE) */
                    ELFEDIT_I18NHDL(MSG_DESC_DELETE),
                    /* MSG_INTL(MSG_HELP_DELETE) */
                    ELFEDIT_I18NHDL(MSG_HELP_DELETE),
                    opt_capid_capndx, arg_delete },

                /* cap:move */
                { cmd_move, cpl_eltarg, name_move,
                    /* MSG_INTL(MSG_DESC_MOVE) */
                    ELFEDIT_I18NHDL(MSG_DESC_MOVE),
                    /* MSG_INTL(MSG_HELP_MOVE) */
                    ELFEDIT_I18NHDL(MSG_HELP_MOVE),
                    opt_capid_capndx, arg_move },

                /* cap:hw1 */
                { cmd_hw1, cpl_hw1, name_hw1,
                    /* MSG_INTL(MSG_DESC_HW1) */
                    ELFEDIT_I18NHDL(MSG_DESC_HW1),
                    /* MSG_INTL(MSG_HELP_HW1) */
                    ELFEDIT_I18NHDL(MSG_HELP_HW1),
                    opt_ostyle_capid_bitop, arg_hw1 },

                /* cap:sf1 */
                { cmd_sf1, cpl_sf1, name_sf1,
                    /* MSG_INTL(MSG_DESC_SF1) */
                    ELFEDIT_I18NHDL(MSG_DESC_SF1),
                    /* MSG_INTL(MSG_HELP_SF1) */
                    ELFEDIT_I18NHDL(MSG_HELP_SF1),
                    opt_ostyle_capid_bitop, arg_sf1 },

                /* cap:hw2 */
                { cmd_hw2, cpl_hw2, name_hw2,
                    /* MSG_INTL(MSG_DESC_HW2) */
                    ELFEDIT_I18NHDL(MSG_DESC_HW2),
                    /* MSG_INTL(MSG_HELP_HW2) */
                    ELFEDIT_I18NHDL(MSG_HELP_HW2),
                    opt_ostyle_capid_bitop, arg_hw2 },

                /* cap:hw3 */
                { cmd_hw3, cpl_hw3, name_hw3,
                    /* MSG_INTL(MSG_DESC_HW3) */
                    ELFEDIT_I18NHDL(MSG_DESC_HW3),
                    /* MSG_INTL(MSG_HELP_HW3) */
                    ELFEDIT_I18NHDL(MSG_HELP_HW3),
                    opt_ostyle_capid_bitop, arg_hw3 },

                { NULL }
        };

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

        return (&module);
}