view usr/src/cmd/mdb/common/mdb/mdb_argvec.c @ 14167:7ac6fb740bcf

3946 ::gcore (fix sparc build)
author Christopher Siden <chris.siden@delphix.com>
date Tue, 27 Aug 2013 10:51:34 -0800
parents 68f95e015346
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (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 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <mdb/mdb_types.h>
#include <mdb/mdb_argvec.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb_stdlib.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_frame.h>
#include <mdb/mdb.h>

#include <alloca.h>

#define	AV_DEFSZ	16	/* Initial size of argument vector */
#define	AV_GROW		2	/* Multiplier for growing argument vector */

void
mdb_argvec_create(mdb_argvec_t *vec)
{
	vec->a_data = NULL;
	vec->a_nelems = 0;
	vec->a_size = 0;
}

void
mdb_argvec_destroy(mdb_argvec_t *vec)
{
	if (vec->a_data != NULL) {
		mdb_argvec_reset(vec);
		mdb_free(vec->a_data, sizeof (mdb_arg_t) * vec->a_size);
	}
}

void
mdb_argvec_append(mdb_argvec_t *vec, const mdb_arg_t *arg)
{
	if (vec->a_nelems >= vec->a_size) {
		size_t size = vec->a_size ? vec->a_size * AV_GROW : AV_DEFSZ;
		void *data = mdb_alloc(sizeof (mdb_arg_t) * size, UM_NOSLEEP);

		if (data == NULL) {
			warn("failed to grow argument vector");
			longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
		}

		bcopy(vec->a_data, data, sizeof (mdb_arg_t) * vec->a_size);
		mdb_free(vec->a_data, sizeof (mdb_arg_t) * vec->a_size);

		vec->a_data = data;
		vec->a_size = size;
	}

	bcopy(arg, &vec->a_data[vec->a_nelems++], sizeof (mdb_arg_t));
}

void
mdb_argvec_reset(mdb_argvec_t *vec)
{
	size_t nelems = vec->a_nelems;
	mdb_arg_t *arg;

	for (arg = vec->a_data; nelems != 0; nelems--, arg++) {
		if (arg->a_type == MDB_TYPE_STRING && arg->a_un.a_str != NULL)
			strfree((char *)arg->a_un.a_str);
	}

	vec->a_nelems = 0;
}

void
mdb_argvec_zero(mdb_argvec_t *vec)
{
#ifdef DEBUG
	size_t i;

	for (i = 0; i < vec->a_size; i++) {
		vec->a_data[i].a_type = UMEM_UNINITIALIZED_PATTERN;
		vec->a_data[i].a_un.a_val =
		    ((u_longlong_t)UMEM_UNINITIALIZED_PATTERN << 32) |
		    ((u_longlong_t)UMEM_UNINITIALIZED_PATTERN);
	}
#endif
	vec->a_nelems = 0;
}

void
mdb_argvec_copy(mdb_argvec_t *dst, const mdb_argvec_t *src)
{
	if (src->a_nelems > dst->a_size) {
		mdb_arg_t *data =
		    mdb_alloc(sizeof (mdb_arg_t) * src->a_nelems, UM_NOSLEEP);

		if (data == NULL) {
			warn("failed to grow argument vector");
			longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
		}

		if (dst->a_data != NULL)
			mdb_free(dst->a_data, sizeof (mdb_arg_t) * dst->a_size);

		dst->a_data = data;
		dst->a_size = src->a_nelems;
	}

	bcopy(src->a_data, dst->a_data, sizeof (mdb_arg_t) * src->a_nelems);
	dst->a_nelems = src->a_nelems;
}

static int
argvec_process_subopt(const mdb_opt_t *opt, const mdb_arg_t *arg)
{
	mdb_subopt_t *sop;
	const char *start;
	const char *next;
	char error[32];
	size_t len;
	uint_t value = 0;
	uint_t i;

	start = arg->a_un.a_str;

	for (i = 0; ; i++) {
		next = strchr(start, ',');

		if (next == NULL)
			len = strlen(start);
		else
			len = next - start;

		/*
		 * Record the index of the subopt if a match if found.
		 */
		for (sop = opt->opt_subopts; sop->sop_flag; sop++) {
			if (strlen(sop->sop_str) == len &&
			    strncmp(sop->sop_str, start, len) == 0) {
				value |= sop->sop_flag;
				sop->sop_index = i;
				goto found;
			}
		}
		(void) mdb_snprintf(error, len + 1, "%s", start);
		warn("invalid option for -%c: \"%s\"\n", opt->opt_char, error);

		return (-1);

found:
		if (next == NULL)
			break;
		start = next + 1;
	}

	*((uint_t *)opt->opt_valp) = value;

	return (0);
}


static int
argvec_process_opt(const mdb_opt_t *opt, const mdb_arg_t *arg)
{
	uint64_t ui64;
	uintptr_t uip;

	switch (opt->opt_type) {
	case MDB_OPT_SETBITS:
		*((uint_t *)opt->opt_valp) |= opt->opt_bits;
		break;

	case MDB_OPT_CLRBITS:
		*((uint_t *)opt->opt_valp) &= ~opt->opt_bits;
		break;

	case MDB_OPT_STR:
		if (arg->a_type != MDB_TYPE_STRING) {
			warn("string argument required for -%c\n",
			    opt->opt_char);
			return (-1);
		}
		*((const char **)opt->opt_valp) = arg->a_un.a_str;
		break;

	case MDB_OPT_UINTPTR_SET:
		*opt->opt_flag = TRUE;
		/* FALLTHROUGH */
	case MDB_OPT_UINTPTR:
		if (arg->a_type == MDB_TYPE_STRING)
			uip = (uintptr_t)mdb_strtoull(arg->a_un.a_str);
		else
			uip = (uintptr_t)arg->a_un.a_val;
		*((uintptr_t *)opt->opt_valp) = uip;
		break;

	case MDB_OPT_UINT64:
		if (arg->a_type == MDB_TYPE_STRING)
			ui64 = mdb_strtoull(arg->a_un.a_str);
		else
			ui64 = arg->a_un.a_val;
		*((uint64_t *)opt->opt_valp) = ui64;
		break;

	case MDB_OPT_SUBOPTS:
		if (arg->a_type != MDB_TYPE_STRING) {
			warn("string argument required for -%c\n",
			    opt->opt_char);
			return (-1);
		}
		return (argvec_process_subopt(opt, arg));

	default:
		warn("internal: bad opt=%p type=%hx\n",
		    (void *)opt, opt->opt_type);
		return (-1);
	}

	return (0);
}

static const mdb_opt_t *
argvec_findopt(const mdb_opt_t *opts, char c)
{
	const mdb_opt_t *optp;

	for (optp = opts; optp->opt_char != 0; optp++) {
		if (optp->opt_char == c)
			return (optp);
	}

	return (NULL);
}

static int
argvec_getopts(const mdb_opt_t *opts, const mdb_arg_t *argv, int argc)
{
	const mdb_opt_t *optp;
	const mdb_arg_t *argp;

	mdb_arg_t arg;

	const char *p;
	int i;
	int nargs;	/* Number of arguments consumed in an iteration */

	for (i = 0; i < argc; i++, argv++) {
		/*
		 * Each option must begin with a string argument whose first
		 * character is '-' and has additional characters afterward.
		 */
		if (argv->a_type != MDB_TYPE_STRING ||
		    argv->a_un.a_str[0] != '-' || argv->a_un.a_str[1] == '\0')
			return (i);

		/*
		 * The special prefix '--' ends option processing.
		 */
		if (strncmp(argv->a_un.a_str, "--", 2) == 0)
			return (i);

		for (p = &argv->a_un.a_str[1]; *p != '\0'; p++) {
			/*
			 * Locate an option struct whose opt_char field
			 * matches the current option letter.
			 */
			if ((optp = argvec_findopt(opts, *p)) == NULL) {
				warn("illegal option -- %c\n", *p);
				return (i);
			}

			/*
			 * Require an argument for strings, immediate
			 * values, subopt-lists and callback functions
			 * which require arguments.
			 */
			if (optp->opt_type == MDB_OPT_STR ||
			    optp->opt_type == MDB_OPT_UINTPTR ||
			    optp->opt_type == MDB_OPT_UINTPTR_SET ||
			    optp->opt_type == MDB_OPT_SUBOPTS ||
			    optp->opt_type == MDB_OPT_UINT64) {
				/*
				 * More text after the option letter:
				 * forge a string argument from remainder.
				 */
				if (p[1] != '\0') {
					arg.a_type = MDB_TYPE_STRING;
					arg.a_un.a_str = ++p;
					argp = &arg;
					p += strlen(p) - 1;

					nargs = 0;
				/*
				 * Otherwise use the next argv element as
				 * the argument if there is one.
				 */
				} else if (++i == argc) {
					warn("option requires an "
					    "argument -- %c\n", *p);
					return (i - 1);
				} else {
					argp = ++argv;
					nargs = 1;
				}
			} else {
				argp = NULL;
				nargs = 0;
			}

			/*
			 * Perform type-specific handling for this option.
			 */
			if (argvec_process_opt(optp, argp) == -1)
				return (i - nargs);
		}
	}

	return (i);
}

int
mdb_getopts(int argc, const mdb_arg_t *argv, ...)
{
	/*
	 * For simplicity just declare enough options on the stack to handle
	 * a-z and A-Z and an extra terminator.
	 */
	mdb_opt_t opts[53], *op = &opts[0];
	va_list alist;
	int c, i = 0;
	mdb_subopt_t *sop;

	va_start(alist, argv);

	for (i = 0; i < (sizeof (opts) / sizeof (opts[0]) - 1); i++, op++) {
		if ((c = va_arg(alist, int)) == 0)
			break; /* end of options */

		op->opt_char = (char)c;
		op->opt_type = va_arg(alist, uint_t);

		if (op->opt_type == MDB_OPT_SETBITS ||
		    op->opt_type == MDB_OPT_CLRBITS) {
			op->opt_bits = va_arg(alist, uint_t);
		} else if (op->opt_type == MDB_OPT_UINTPTR_SET) {
			op->opt_flag = va_arg(alist, boolean_t *);
		} else if (op->opt_type == MDB_OPT_SUBOPTS) {
			op->opt_subopts = va_arg(alist, mdb_subopt_t *);

			for (sop = op->opt_subopts; sop->sop_flag; sop++)
				sop->sop_index = -1;
		}

		op->opt_valp = va_arg(alist, void *);
	}

	bzero(&opts[i], sizeof (mdb_opt_t));
	va_end(alist);

	return (argvec_getopts(opts, argv, argc));
}

/*
 * The old adb breakpoint and watchpoint routines did not accept any arguments;
 * all characters after the verb were concatenated to form the string callback.
 * This utility function concatenates all arguments in argv[] into a single
 * string to simplify the implementation of these legacy routines.
 */
char *
mdb_argv_to_str(int argc, const mdb_arg_t *argv)
{
	char *s = NULL;
	size_t n = 0;
	int i;

	for (i = 0; i < argc; i++) {
		if (argv[i].a_type == MDB_TYPE_STRING)
			n += strlen(argv[i].a_un.a_str);
	}

	if (n != 0) {
		s = mdb_zalloc(n + argc, UM_SLEEP);

		for (i = 0; i < argc - 1; i++, argv++) {
			(void) strcat(s, argv->a_un.a_str);
			(void) strcat(s, " ");
		}

		(void) strcat(s, argv->a_un.a_str);
	}

	return (s);
}