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