/*
 * 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 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include	<stdlib.h>
#include	<stdio.h>
#include	<unistd.h>
#include	<libintl.h>
#include	<libelf.h>
#include	<sys/machelf.h>
#include	<link.h>
#include	<strings.h>
#include	<ctype.h>
#include	<elfedit.h>
#include	<_elfedit.h>
#include	<sys/elf_SPARC.h>
#include	<sys/elf_amd64.h>
#include	<msg.h>



/*
 * This file contains utility functions that are of general use
 * to different elfedit modules for solving common problems.
 * The functions in this file are not ELFCLASS specific. Those
 * functions are found in util_machelf.c
 *
 * NOTE: This module contains functions with names
 * elfedit_atoi, and elfedit_atoui, that are otherwise identical.
 * These functions are for signed, and unsigned integers, respectively.
 * In general, I supply one comment header for each such pair,
 * and put their implementations together.
 *
 * There are also functions with names elfedit_atoconst. These are
 * convenience wrappers that use the corresponding elfedit_atoui()
 * function to process an array of symbolic names provided by a call
 * elfedit_const_to_atoui().
 */




/*
 * Given a value and an array of elfedit_ato[u]i items, return a pointer
 * to the symbolic name for the value.
 *
 * entry:
 *	sym - NULL terminated array of name->value mappings.
 *	value - Value to be found
 *	required - If True, and value is not found, an error is issued.
 *		Callers should only set required to True when they know
 *		a priori that the value will be found --- the error
 *		is reported as an internal programming error.
 *
 * exit:
 *	If the array contains an entry with the given value, the
 *	name for the first such entry will be returned.
 *
 *	If no entry is found: If required is True (1), an error is
 *	issued and this routine does not return to the caller. If required
 *	is False (0), then NULL is returned.
 */
const char *
elfedit_atoi_value_to_str(const elfedit_atoi_sym_t *sym, elfedit_atoi_t value,
    int required)
{
	for (; sym->sym_name != NULL; sym++)
		if (value == sym->sym_value)
			return (sym->sym_name);

	/* Value did not match any of the entries */
	if (required)
		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADGETVAL));
	return (NULL);
}
const char *
elfedit_atoui_value_to_str(const elfedit_atoui_sym_t *sym,
    elfedit_atoui_t value, int required)
{
	for (; sym->sym_name != NULL; sym++)
		if (value == sym->sym_value)
			return (sym->sym_name);

	/* Value did not match any of the entries */
	if (required)
		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_BADGETVAL));
	return (NULL);
}
const char *
elfedit_atoconst_value_to_str(elfedit_const_t const_type, elfedit_atoui_t value,
    int required)
{
	return (elfedit_atoui_value_to_str(elfedit_const_to_atoui(const_type),
	    value, required));
}


/*
 * Process the symbolic name to value mappings passed to the
 * atoi and atoui  functions.
 *
 * entry:
 *	sym - NULL terminated array of name->value mappings.
 *	value - Address of variable to recieve corresponding value.
 *
 * exit:
 *	If a mapping is found, *value is set to it, and True is returned.
 *	Otherwise False is returned.
 */
static int
atoi_sym_process(const char *str, const elfedit_atoi_sym_t *sym,
    elfedit_atoi_t *value)
{
	size_t		cmp_len;
	const char	*tail;

	while (isspace(*str))
		str++;

	tail = str + strlen(str);
	while ((tail > str) && isspace(*(tail - 1)))
		tail--;

	cmp_len = tail - str;

	for (; sym->sym_name != NULL; sym++) {
		if ((strlen(sym->sym_name) == cmp_len) &&
		    (strncasecmp(sym->sym_name, str, cmp_len) == 0)) {
			*value = sym->sym_value;
			return (1);
		}
	}

	/* No symbolic mapping was found */
	return (0);
}
static int
atoui_sym_process(const char *str, const elfedit_atoui_sym_t *sym,
    elfedit_atoui_t *value)
{
	size_t		cmp_len;
	const char	*tail;

	while (isspace(*str))
		str++;

	tail = str + strlen(str);
	while ((tail > str) && isspace(*(tail - 1)))
		tail--;

	cmp_len = tail - str;

	for (; sym->sym_name != NULL; sym++) {
		if ((strlen(sym->sym_name) == cmp_len) &&
		    (strncasecmp(sym->sym_name, str, cmp_len) == 0)) {
			*value = sym->sym_value;
			return (1);
		}
	}

	/* No symbolic mapping was found */
	return (0);
}



/*
 * A command completion function for atoi and atoui mappings.
 */
void
elfedit_cpl_atoi(void *cpldata, const elfedit_atoi_sym_t *sym)
{
	for (; sym->sym_name != NULL; sym++)
		elfedit_cpl_match(cpldata, sym->sym_name, 1);
}
void
elfedit_cpl_atoui(void *cpldata, const elfedit_atoui_sym_t *sym)
{
	for (; sym->sym_name != NULL; sym++)
		elfedit_cpl_match(cpldata, sym->sym_name, 1);
}
void
elfedit_cpl_atoconst(void *cpldata, elfedit_const_t const_type)
{
	elfedit_cpl_atoui(cpldata, elfedit_const_to_atoui(const_type));
}





/*
 * Convert a string to a numeric value. Strings starting with '0'
 * are taken to be octal, those staring with '0x' are hex, and all
 * others are decimal.
 *
 * entry:
 *	str - String to be converted
 *	sym - NULL, or NULL terminated array of name/value pairs.
 *
 *	[elfedit_atoi2() and elfedit_atoui2() only]
 *	v - Address of variable to receive resulting value.
 *
 * exit:
 *	elfedit_atoi2() and elfedit_atoui2():
 *		On success, returns True (1) and *v is set to the value.
 *		On failure, returns False (0) and *v is undefined.
 *
 *	elfedit_atoi() and elfedit_atoui():
 *		If the string is convertable, the value is returned.
 *		Otherwise an error is issued and this routine does
 *		not return to the caller.
 */
int
elfedit_atoi2(const char *str, const elfedit_atoi_sym_t *sym, elfedit_atoi_t *v)
{
	char		*endptr;

	if (sym && atoi_sym_process(str, sym, v))
		return (1);

	*v = strtoll(str, &endptr, 0);

	/* If the left over part contains anything but whitespace, fail */
	for (; *endptr; endptr++)
		if (!isspace(*endptr))
			return (0);
	return (1);
}
elfedit_atoi_t
elfedit_atoi(const char *str, const elfedit_atoi_sym_t *sym)
{
	elfedit_atoi_t v;
	if (elfedit_atoi2(str, sym, &v) == 0)
		elfedit_msg(ELFEDIT_MSG_ERR,
		    MSG_INTL(MSG_ERR_BADATOISTR), str);
	return (v);
}
int
elfedit_atoui2(const char *str, const elfedit_atoui_sym_t *sym,
    elfedit_atoui_t *v)
{
	char		*endptr;

	if (sym && atoui_sym_process(str, sym, v))
		return (1);

	*v = strtoull(str, &endptr, 0);

	/* If the left over part contains anything but whitespace, fail */
	for (; *endptr; endptr++)
		if (!isspace(*endptr))
			return (0);
	return (1);
}
elfedit_atoui_t
elfedit_atoui(const char *str, const elfedit_atoui_sym_t *sym)
{
	elfedit_atoui_t v;
	if (elfedit_atoui2(str, sym, &v) == 0)
		elfedit_msg(ELFEDIT_MSG_ERR,
		    MSG_INTL(MSG_ERR_BADATOISTR), str);
	return (v);
}
int
elfedit_atoconst2(const char *str, elfedit_const_t const_type,
    elfedit_atoui_t *v)
{
	return (elfedit_atoui2(str, elfedit_const_to_atoui(const_type), v));
}
elfedit_atoui_t
elfedit_atoconst(const char *str, elfedit_const_t const_type)
{
	return (elfedit_atoui(str, elfedit_const_to_atoui(const_type)));
}

/*
 * Convert a string to a numeric value using elfedit_ato[u]i and
 * ensure that the resulting value lies within a given range.
 * elfedit_ato[u]i_range() requires values to be in the range
 * (min <= value <= max).
 *
 * entry:
 *	str - String to be converted
 *	min, max - If check_range is true, the allowed range that the
 *		resulting value must lie in.
 *	sym - NULL, or NULL terminated array of name/value pairs.
 *
 * entry [elfedit_atoi_range() and elfedit_atoui_range() only]:
 *	item_name - String describing item for which value is being read.
 *
 * entry [elfedit_atoi_range2() and elfedit_atoui_range2() only]:
 *	v - Address of variable to receive resulting value.
 *
 * exit:
 *	elfedit_atoi_range2() and elfedit_atoui_range2():
 *		On success, returns True (1) and *v is set to the value.
 *		On failure, returns False (0) and *v is undefined.
 *
 *	elfedit_atoi_range() and elfedit_atoui_range():
 *		If the string is convertable, the value is returned.
 *		Otherwise an error is issued and this routine does
 *		not return to the caller.
 */
int
elfedit_atoi_range2(const char *str, elfedit_atoi_t min, elfedit_atoi_t max,
    const elfedit_atoi_sym_t *sym, elfedit_atoi_t *v)
{
	return ((elfedit_atoi2(str, sym, v) != 0) &&
	    (*v >= min) && (*v <= max));
}
elfedit_atoi_t
elfedit_atoi_range(const char *str, const char *item_name,
    elfedit_atoi_t min, elfedit_atoi_t max, const elfedit_atoi_sym_t *sym)
{
	elfedit_atoi_t v = elfedit_atoi(str, sym);

	if ((v < min) || (v > max))
		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_ATOIRANGE),
		    item_name, EC_XWORD(min), EC_XWORD(max), EC_XWORD(v));

	return (v);
}
int
elfedit_atoui_range2(const char *str, elfedit_atoui_t min, elfedit_atoui_t max,
    const elfedit_atoui_sym_t *sym, elfedit_atoui_t *v)
{
	return ((elfedit_atoui2(str, sym, v) != 0) &&
	    (*v >= min) && (*v <= max));
}
elfedit_atoui_t
elfedit_atoui_range(const char *str, const char *item_name,
    elfedit_atoui_t min, elfedit_atoui_t max, const elfedit_atoui_sym_t *sym)
{
	elfedit_atoui_t v = elfedit_atoui(str, sym);

	if ((v < min) || (v > max))
		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_ATOUIRANGE),
		    item_name, EC_XWORD(min), EC_XWORD(max), EC_XWORD(v));

	return (v);
}
int
elfedit_atoconst_range2(const char *str, elfedit_atoui_t min,
    elfedit_atoui_t max, elfedit_const_t const_type, elfedit_atoui_t *v)
{
	return (elfedit_atoui_range2(str, min, max,
	    elfedit_const_to_atoui(const_type), v));
}
elfedit_atoui_t
elfedit_atoconst_range(const char *str, const char *item_name,
    elfedit_atoui_t min, elfedit_atoui_t max, elfedit_const_t const_type)
{
	return (elfedit_atoui_range(str, item_name, min, max,
	    elfedit_const_to_atoui(const_type)));
}


/*
 * Convenience wrapper on elfedit_atoui_range() that expects to see
 * boolean values. Returns 1 for true, and 0 for false.
 */
int
elfedit_atobool(const char *str, const char *item_name)
{

	return (elfedit_atoconst_range(str, item_name, 0, 1,
	    ELFEDIT_CONST_BOOL) != 0);
}



/*
 * Convenience wrapper on elfedit_atoui() to read a section index
 * that understands the special SHN_ names.
 *
 * entry:
 *	str - String to process
 *	shnum - Number of sections in the ELF file
 *
 * exit:
 *	If it is possible to convert str to a number, that value
 *	is returned. If the value is out of range for the file,
 *	a warning message to that effect is issued. On failure,
 *	an error is issued and this routine does not return to
 *	the caller.
 */
elfedit_atoui_t
elfedit_atoshndx(const char *str, size_t shnum)
{
	elfedit_atoui_t ndx;

	ndx = elfedit_atoconst(str, ELFEDIT_CONST_SHN);
	if ((ndx >= shnum) && ((ndx < SHN_LORESERVE) || (ndx > SHN_HIRESERVE)))
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_SHNDX_RANGE),
		    EC_WORD(ndx), EC_WORD(shnum-1));

	return (ndx);
}



/*
 * Convert an output style string into it's integer constant. This
 * routine reports success/failure via the return value rather than
 * by throwing errors so that it can be used to process command
 * line options at program startup, before
 * the elfedit framework is initialized.
 */
int
elfedit_atooutstyle(const char *str, elfedit_outstyle_t *outstyle)
{
	int		ret;
	elfedit_atoui_t	value;

	ret = atoui_sym_process(str,
	    elfedit_const_to_atoui(ELFEDIT_CONST_OUTSTYLE), &value);
	if (ret != 0)
		*outstyle = value;
	return (ret);
}




/*
 * Initialize a state block for processing by elfedit_getopt().
 *
 * entry:
 *	state - State block to initialize
 *	cmd_name - NULL, or name of command for which we are processing
 *		options.
 *	argc, argv - Address of variables giving number of options and
 *		access to the option strings.
 *
 * note:
 *	cmd_name can only be set to NULL when this routine is called
 *	by, or below, a currently active command. Otherwise, results
 *	are undefined (crashing or corruption) if there isn't one.
 */
void
elfedit_getopt_init(elfedit_getopt_state_t *state,
    int *argc, const char **argv[])
{
	elfeditGC_cmd_t *cmd = elfedit_curcmd();

	state->go_argc = argc;
	state->go_argv = argv;
	state->go_optarg = cmd->cmd_opt;
	state->go_idmask = 0;
	state->go_done = 0;
	state->go_sglgrp = NULL;
}



/*
 * elfedit-centric version of getopt()
 *
 * entry:
 *	state - Getopt state, which must have been previously initialized
 *		via a call to elfedit_getopt_init.
 *
 * exit:
 *	If an option is matched, this routine returns a pointer to an
 *	elfedit_getopt_ret_t buffer (which comes from the storage used
 *	for state). If there are no more options to process, NULL is returned.
 *
 *	Syntax errors are reported via elfedit_command_usage(), and this
 *	routine does not return to the caller.
 *
 * note:
 *	- The caller should not access the contents of state directly.
 *		Those contents are private, and subject to change.
 *	- Once a call to this routine returns NULL, the argc/argv have
 *		have been ajusted so that they reference the plain arguments.
 */
elfedit_getopt_ret_t *
elfedit_getopt(elfedit_getopt_state_t *state)
{
	elfedit_cmd_optarg_t	*optarg;
	const char		*argstr;
	int			argc = *(state->go_argc);
	const char		**argv = *(state->go_argv);
	elfedit_optarg_item_t	item;
	struct {
		int			valid;
		int			is_outstyle;
		elfedit_getopt_ret_t	ret;
		elfedit_cmd_oa_mask_t	excmask;
	} sgl_with_value;

	if (state->go_sglgrp == NULL) {
		/*
		 * Reasons to bail out immediately:
		 *	- The command does not accept options
		 *	- We've already reported the final option.
		 *	- There are no more arguments.
		 *	- The next argument does not start with '-'
		 */
		if ((state->go_optarg == NULL) || state->go_done ||
		    (argc <= 0) || (*(argv[0]) != '-')) {
			state->go_done = 1;
			return (NULL);
		}

		argstr = argv[0];

		/* A '-' by itself is a syntax error */
		if (argstr[1] == '\0')
			elfedit_command_usage();

		/* A '--' option means we should stop at this point */
		if ((argstr[1] == '-') && (argstr[2] == '\0')) {
			(*state->go_argc)--;
			(*state->go_argv)++;
			return (NULL);
		}

		/*
		 * We have a string that starts with a '-'.
		 * Does it match an option?
		 */
		sgl_with_value.valid = 0;
		for (optarg = state->go_optarg; optarg->oa_name != NULL; ) {
			int is_outstyle =
			    (optarg->oa_flags & ELFEDIT_CMDOA_F_INHERIT) &&
			    (optarg->oa_name == ELFEDIT_STDOA_OPT_O);
			int need_value;

			elfedit_next_optarg(&optarg, &item);
			need_value = item.oai_flags & ELFEDIT_CMDOA_F_VALUE;

			/*
			 * If the option is a single letter that accepts
			 * a value, then we allow the combined syntax
			 * -ovalue, where no space is reqired between the
			 * option flag and the value string.
			 */
			if ((item.oai_name[2] == '\0') && need_value &&
			    (argstr[1] == item.oai_name[1]) &&
			    (argstr[2] != '\0')) {
				/*
				 * We have a match. However, there may also
				 * be a straightforward match that we have
				 * not yet found. If so, we want to prefer that
				 * case over this one. So rather than return
				 * it immediately, we capture the information
				 * and keep looking. If nothing else surfaces,
				 * we'll use this later.
				 */
				sgl_with_value.valid = 1;
				sgl_with_value.ret.gor_idmask = item.oai_idmask;
				sgl_with_value.excmask = item.oai_excmask;
				sgl_with_value.ret.gor_value = argstr + 2;
				sgl_with_value.is_outstyle = is_outstyle;
				continue;
			}

			/* Try for a straightforward match */
			if (strcmp(argstr, item.oai_name) == 0) {
				(*state->go_argc) = --argc;
				(*state->go_argv) = ++argv;

				/* Mutually exclusive option already seen? */
				if (item.oai_excmask & state->go_idmask)
					elfedit_command_usage();

				/* Return the match */
				state->go_idmask |= item.oai_idmask;
				state->go_ret.gor_idmask = item.oai_idmask;
				if (need_value) {
					    /* If out of args, syntax error */
					if (argc <= 0)
						elfedit_command_usage();
					state->go_ret.gor_value = argv[0];
					(*state->go_argc)--;
					(*state->go_argv)++;
				} else {
					state->go_ret.gor_value = NULL;
				}
				if (is_outstyle)
					elfedit_set_cmd_outstyle(
					    state->go_ret.gor_value);
				return (&state->go_ret);
			}
		}

		/*
		 * No straightforward matches: Did we get a match with
		 * the special single letter and combined value? If so
		 * return that now.
		 */
		if (sgl_with_value.valid) {
			(*state->go_argc)--;
			(*state->go_argv)++;

			/* Mutually exclusive option already seen? */
			if (sgl_with_value.excmask & state->go_idmask)
				elfedit_command_usage();

			state->go_idmask |= sgl_with_value.ret.gor_idmask;
			state->go_ret = sgl_with_value.ret;
			if (sgl_with_value.is_outstyle)
				elfedit_set_cmd_outstyle(
				    state->go_ret.gor_value);

			return (&state->go_ret);
		}

		/*
		 * If nothing above matched, make this option the single
		 * group string and see if the characters in it all match
		 * as single letter options without values.
		 */
		state->go_sglgrp = argstr + 1;	/* Skip '-' */
	}

	/*
	 * If there is a single group string, take the first character
	 * and try to match it to an 1-letter option that does not
	 * require a value.
	 */
	if (state->go_sglgrp != NULL) {
		int ch = *state->go_sglgrp++;

		/* If that is the last character, clear single group mode */
		if (*state->go_sglgrp == '\0') {
			(*state->go_argc)--;
			(*state->go_argv)++;
			state->go_sglgrp = NULL;
		}

		for (optarg = state->go_optarg; optarg->oa_name != NULL; ) {
			elfedit_next_optarg(&optarg, &item);

			if ((item.oai_name[2] == '\0') &&
			    (ch == item.oai_name[1])) {
				/*
				 * It matches. If the option requires a value
				 * then it cannot be in a group.
				 */
				if (item.oai_flags & ELFEDIT_CMDOA_F_VALUE)
					elfedit_command_usage();

				/* Mutually exclusive option already seen? */
				if (item.oai_excmask & state->go_idmask)
					elfedit_command_usage();

				/* Return the match */
				state->go_idmask |= item.oai_idmask;
				state->go_ret.gor_idmask = item.oai_idmask;
				state->go_ret.gor_value = NULL;
				return (&state->go_ret);
			}
		}
	}

	/* Nothing matched. We have a syntax error */
	elfedit_command_usage();
	/*NOTREACHED*/
	return (NULL);
}


/*
 * Return the count of non-zero bits in the value v.
 *
 * entry:
 *	v - Value to test
 *	sizeof_orig_v - The result of using the sizeof operator
 *		on the original value of v. The value received
 *		by this routine has been cast to an unsigned 64-bit
 *		integer, so having the caller use sizeof allows us to
 *		avoid testing bits that were not in the original.
 */
int
elfedit_bits_set(u_longlong_t v, int sizeof_orig_v)
{
	int	nbits = sizeof_orig_v * 8;
	int	mask;
	int	cnt = 0;

	for (mask = 1; (nbits-- > 0) && (cnt < 2); mask *= 2)
		if (v & mask)
			cnt++;

	return (cnt);
}


/*
 * "delete" items in an array by copying the following items up
 * over the "deleted" items and then zero filling the vacated
 * slots at the bottom.
 *
 * entry:
 *	name_str - Array identification prefix to use for debug message
 *	data_start - Address of 1st byte in array
 *	entsize - sizeof a single element of the array
 *	num_ent - # of elements in array
 *	start_ndx - Index of first item to be deleted
 *	cnt - # of items to delete
 *
 * exit:
 *	Any errors are issued and control does not return to the
 *	caller. On success, the items have been removed, zero filling
 *	has been done, and debug messages issued.
 */
void
elfedit_array_elts_delete(const char *name_str, void *data_start,
    size_t entsize, size_t num_ent, size_t start_ndx, size_t cnt)
{
	char	*data = data_start;

	/* The specified index and range must be in bounds */
	if ((start_ndx + cnt) > num_ent)
		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_ARRBNDS),
		    name_str, EC_WORD(num_ent), EC_WORD(num_ent - 1));

	/*
	 * Everything below the deleted items moves up.
	 * Note that bcopy() is documented to handle overlapping
	 * src/dst correctly, so we make no effort to handle this
	 * element by element, but issue a single operation.
	 *
	 * If we're doing the last element, there is nothing to
	 * move up, and we skip this step, moving on to the zeroing below.
	 */
	if (start_ndx < (num_ent - 1)) {
		size_t ncpy = num_ent - (start_ndx + cnt);

		bcopy(data + ((start_ndx + cnt) * entsize),
		    data + (start_ndx * entsize), ncpy * entsize);
		if (ncpy == 1) {
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_ARRCPY_1), name_str,
			    EC_WORD(start_ndx + cnt), EC_WORD(start_ndx));
		} else {
			elfedit_msg(ELFEDIT_MSG_DEBUG,
			    MSG_INTL(MSG_DEBUG_ARRCPY_N), name_str,
			    EC_WORD(start_ndx + cnt),
			    EC_WORD(start_ndx + cnt + ncpy - 1),
			    EC_WORD(start_ndx),
			    EC_WORD(start_ndx + ncpy - 1));
		}
	}

	/* Zero out the vacated elements at the end */
	bzero(data + ((num_ent - cnt) * entsize), entsize * cnt);

	if (cnt == 1) {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_ARRZERO_1),
		    name_str, EC_WORD(num_ent - 1));
	} else {
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_ARRZERO_N),
		    name_str, EC_WORD(num_ent - cnt),
		    EC_WORD(num_ent - 1), EC_WORD(cnt));
	}
}


/*
 * move the location of items in an array by shifting the surround
 * items into the vacated hole and them putting the values into
 * the new location.
 *
 * entry:
 *	name_str - Array identification prefix to use for debug message
 *	data_start - Address of 1st byte in array
 *	entsize - sizeof a single element of the array
 *	num_ent - # of elements in array
 *	start_ndx - Index of first item to be moved
 *	dst_ndx - Index to receive the moved block
 *	cnt - # of items to move
 *	scr_item - Space allocated by the caller sufficient to hold
 *		one item from the array. Used to swap elements.
 *
 * exit:
 *	Any errors are issued and control does not return to the
 *	caller. On success, the items have been moved, and debug
 *	messages issued.
 */
void
elfedit_array_elts_move(const char *name_str, void *data_start,
    size_t entsize, size_t num_ent, size_t srcndx,
    size_t dstndx, size_t cnt, void *scr_item)
{
	char	*data = data_start;

	/* The specified source and destination ranges must be in bounds */
	if (((srcndx + cnt) > num_ent) || ((dstndx + cnt) > num_ent))
		elfedit_msg(ELFEDIT_MSG_ERR, MSG_INTL(MSG_ERR_ARRBNDS),
		    name_str, EC_WORD(num_ent), EC_WORD(num_ent - 1));

	/* If source and destination are same, there's nothing to do */
	if (srcndx == dstndx)
		return;

	/*
	 * It is meaningless to do a move where the source and destination
	 * are overlapping, because this "move" amounts to shifting
	 * the existing items around into a new position. If there is
	 * more than one element, then overlap is possible and we need
	 * to test for it.
	 */
	if (cnt > 1) {
		size_t low, hi;

		if (srcndx > dstndx) {
			low = dstndx;
			hi = srcndx;
		} else {
			low = srcndx;
			hi = dstndx;
		}
		/* Ensure that the src and dst don't overlap */
		if ((low + cnt) > hi)
			elfedit_msg(ELFEDIT_MSG_ERR,
			    MSG_INTL(MSG_ERR_ARRMVOVERLAP), name_str,
			    EC_WORD(srcndx), EC_WORD(srcndx + cnt - 1),
			    EC_WORD(dstndx), EC_WORD(dstndx + cnt - 1));
	}

	if (cnt == 1)
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_ARRMOVE_1),
		    name_str, EC_WORD(srcndx), EC_WORD(dstndx));
	else
		elfedit_msg(ELFEDIT_MSG_DEBUG, MSG_INTL(MSG_DEBUG_ARRMOVE_N),
		    name_str, EC_WORD(cnt),
		    EC_WORD(srcndx), EC_WORD(srcndx + cnt - 1),
		    EC_WORD(dstndx), EC_WORD(dstndx + cnt - 1));

	if (srcndx < dstndx) {
		srcndx += cnt - 1;
		dstndx += cnt - 1;
		for (; cnt-- > 0; srcndx--, dstndx--) {
			/*
			 * Copy item at srcndx to scratch location
			 *
			 *	save = dyn[srcndx];
			 */
			bcopy(data + (srcndx * entsize), scr_item, entsize);

			/*
			 * Shift items after source up through destination
			 * to source. bcopy() handles overlapped copies.
			 *
			 *	for (i = srcndx; i < dstndx; i++)
			 *		dyn[i] = dyn[i + 1];
			 */
			bcopy(data + ((srcndx + 1) * entsize),
			    data + (srcndx * entsize),
			    (dstndx - srcndx) * entsize);

			/*
			 * Copy saved item into destination slot
			 *
			 *	dyn[dstndx] = save;
			 */
			bcopy(scr_item, data + (dstndx * entsize), entsize);
		}
	} else {
		for (; cnt-- > 0; srcndx++, dstndx++) {
			/*
			 * Copy item at srcndx to scratch location
			 *
			 *	save = dyn[srcndx];
			 */
			bcopy(data + (srcndx * entsize), scr_item, entsize);

			/*
			 * Shift items from destination through item below
			 * source up one. bcopy() handles overlapped copies.
			 *
			 *	for (i = srcndx; i > dstndx; i--)
			 *		dyn[i] = dyn[i - 1];
			 */
			bcopy(data + (dstndx * entsize),
			    data + ((dstndx + 1) * entsize),
			    (srcndx - dstndx) * entsize);

			/*
			 * Copy saved item into destination slot
			 *
			 *	dyn[dstndx] = save;
			 */
			bcopy(scr_item, data + (dstndx * entsize), entsize);
		}
	}
}