/* * 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 #include #include #include #include #include #include #include #include #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); }