1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <mdb/mdb_types.h> 30 #include <mdb/mdb_argvec.h> 31 #include <mdb/mdb_string.h> 32 #include <mdb/mdb_err.h> 33 #include <mdb/mdb_stdlib.h> 34 #include <mdb/mdb_modapi.h> 35 #include <mdb/mdb_frame.h> 36 #include <mdb/mdb.h> 37 38 #include <alloca.h> 39 40 #define AV_DEFSZ 16 /* Initial size of argument vector */ 41 #define AV_GROW 2 /* Multiplier for growing argument vector */ 42 43 void 44 mdb_argvec_create(mdb_argvec_t *vec) 45 { 46 vec->a_data = NULL; 47 vec->a_nelems = 0; 48 vec->a_size = 0; 49 } 50 51 void 52 mdb_argvec_destroy(mdb_argvec_t *vec) 53 { 54 if (vec->a_data != NULL) { 55 mdb_argvec_reset(vec); 56 mdb_free(vec->a_data, sizeof (mdb_arg_t) * vec->a_size); 57 } 58 } 59 60 void 61 mdb_argvec_append(mdb_argvec_t *vec, const mdb_arg_t *arg) 62 { 63 if (vec->a_nelems >= vec->a_size) { 64 size_t size = vec->a_size ? vec->a_size * AV_GROW : AV_DEFSZ; 65 void *data = mdb_alloc(sizeof (mdb_arg_t) * size, UM_NOSLEEP); 66 67 if (data == NULL) { 68 warn("failed to grow argument vector"); 69 longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM); 70 } 71 72 bcopy(vec->a_data, data, sizeof (mdb_arg_t) * vec->a_size); 73 mdb_free(vec->a_data, sizeof (mdb_arg_t) * vec->a_size); 74 75 vec->a_data = data; 76 vec->a_size = size; 77 } 78 79 bcopy(arg, &vec->a_data[vec->a_nelems++], sizeof (mdb_arg_t)); 80 } 81 82 void 83 mdb_argvec_reset(mdb_argvec_t *vec) 84 { 85 size_t nelems = vec->a_nelems; 86 mdb_arg_t *arg; 87 88 for (arg = vec->a_data; nelems != 0; nelems--, arg++) { 89 if (arg->a_type == MDB_TYPE_STRING && arg->a_un.a_str != NULL) 90 strfree((char *)arg->a_un.a_str); 91 } 92 93 vec->a_nelems = 0; 94 } 95 96 void 97 mdb_argvec_zero(mdb_argvec_t *vec) 98 { 99 #ifdef DEBUG 100 size_t i; 101 102 for (i = 0; i < vec->a_size; i++) { 103 vec->a_data[i].a_type = UMEM_UNINITIALIZED_PATTERN; 104 vec->a_data[i].a_un.a_val = 105 ((u_longlong_t)UMEM_UNINITIALIZED_PATTERN << 32) | 106 ((u_longlong_t)UMEM_UNINITIALIZED_PATTERN); 107 } 108 #endif 109 vec->a_nelems = 0; 110 } 111 112 void 113 mdb_argvec_copy(mdb_argvec_t *dst, const mdb_argvec_t *src) 114 { 115 if (src->a_nelems > dst->a_size) { 116 mdb_arg_t *data = 117 mdb_alloc(sizeof (mdb_arg_t) * src->a_nelems, UM_NOSLEEP); 118 119 if (data == NULL) { 120 warn("failed to grow argument vector"); 121 longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM); 122 } 123 124 if (dst->a_data != NULL) 125 mdb_free(dst->a_data, sizeof (mdb_arg_t) * dst->a_size); 126 127 dst->a_data = data; 128 dst->a_size = src->a_nelems; 129 } 130 131 bcopy(src->a_data, dst->a_data, sizeof (mdb_arg_t) * src->a_nelems); 132 dst->a_nelems = src->a_nelems; 133 } 134 135 static int 136 argvec_process_subopt(const mdb_opt_t *opt, const mdb_arg_t *arg) 137 { 138 mdb_subopt_t *sop; 139 const char *start; 140 const char *next; 141 char error[32]; 142 size_t len; 143 uint_t value = 0; 144 uint_t i; 145 146 start = arg->a_un.a_str; 147 148 for (i = 0; ; i++) { 149 next = strchr(start, ','); 150 151 if (next == NULL) 152 len = strlen(start); 153 else 154 len = next - start; 155 156 /* 157 * Record the index of the subopt if a match if found. 158 */ 159 for (sop = opt->opt_subopts; sop->sop_flag; sop++) { 160 if (strlen(sop->sop_str) == len && 161 strncmp(sop->sop_str, start, len) == 0) { 162 value |= sop->sop_flag; 163 sop->sop_index = i; 164 goto found; 165 } 166 } 167 (void) mdb_snprintf(error, len + 1, "%s", start); 168 warn("invalid option for -%c: \"%s\"\n", opt->opt_char, error); 169 170 return (-1); 171 172 found: 173 if (next == NULL) 174 break; 175 start = next + 1; 176 } 177 178 *((uint_t *)opt->opt_valp) = value; 179 180 return (0); 181 } 182 183 184 static int 185 argvec_process_opt(const mdb_opt_t *opt, const mdb_arg_t *arg) 186 { 187 uint64_t ui64; 188 uintptr_t uip; 189 190 switch (opt->opt_type) { 191 case MDB_OPT_SETBITS: 192 *((uint_t *)opt->opt_valp) |= opt->opt_bits; 193 break; 194 195 case MDB_OPT_CLRBITS: 196 *((uint_t *)opt->opt_valp) &= ~opt->opt_bits; 197 break; 198 199 case MDB_OPT_STR: 200 if (arg->a_type != MDB_TYPE_STRING) { 201 warn("string argument required for -%c\n", 202 opt->opt_char); 203 return (-1); 204 } 205 *((const char **)opt->opt_valp) = arg->a_un.a_str; 206 break; 207 208 case MDB_OPT_UINTPTR_SET: 209 *opt->opt_flag = TRUE; 210 /* FALLTHROUGH */ 211 case MDB_OPT_UINTPTR: 212 if (arg->a_type == MDB_TYPE_STRING) 213 uip = (uintptr_t)mdb_strtoull(arg->a_un.a_str); 214 else 215 uip = (uintptr_t)arg->a_un.a_val; 216 *((uintptr_t *)opt->opt_valp) = uip; 217 break; 218 219 case MDB_OPT_UINT64: 220 if (arg->a_type == MDB_TYPE_STRING) 221 ui64 = mdb_strtoull(arg->a_un.a_str); 222 else 223 ui64 = arg->a_un.a_val; 224 *((uint64_t *)opt->opt_valp) = ui64; 225 break; 226 227 case MDB_OPT_SUBOPTS: 228 if (arg->a_type != MDB_TYPE_STRING) { 229 warn("string argument required for -%c\n", 230 opt->opt_char); 231 return (-1); 232 } 233 return (argvec_process_subopt(opt, arg)); 234 235 default: 236 warn("internal: bad opt=%p type=%hx\n", 237 (void *)opt, opt->opt_type); 238 return (-1); 239 } 240 241 return (0); 242 } 243 244 static const mdb_opt_t * 245 argvec_findopt(const mdb_opt_t *opts, char c) 246 { 247 const mdb_opt_t *optp; 248 249 for (optp = opts; optp->opt_char != 0; optp++) { 250 if (optp->opt_char == c) 251 return (optp); 252 } 253 254 return (NULL); 255 } 256 257 static int 258 argvec_getopts(const mdb_opt_t *opts, const mdb_arg_t *argv, int argc) 259 { 260 const mdb_opt_t *optp; 261 const mdb_arg_t *argp; 262 263 mdb_arg_t arg; 264 265 const char *p; 266 int i; 267 int nargs; /* Number of arguments consumed in an iteration */ 268 269 for (i = 0; i < argc; i++, argv++) { 270 /* 271 * Each option must begin with a string argument whose first 272 * character is '-' and has additional characters afterward. 273 */ 274 if (argv->a_type != MDB_TYPE_STRING || 275 argv->a_un.a_str[0] != '-' || argv->a_un.a_str[1] == '\0') 276 return (i); 277 278 /* 279 * The special prefix '--' ends option processing. 280 */ 281 if (strncmp(argv->a_un.a_str, "--", 2) == 0) 282 return (i); 283 284 for (p = &argv->a_un.a_str[1]; *p != '\0'; p++) { 285 /* 286 * Locate an option struct whose opt_char field 287 * matches the current option letter. 288 */ 289 if ((optp = argvec_findopt(opts, *p)) == NULL) { 290 warn("illegal option -- %c\n", *p); 291 return (i); 292 } 293 294 /* 295 * Require an argument for strings, immediate 296 * values, subopt-lists and callback functions 297 * which require arguments. 298 */ 299 if (optp->opt_type == MDB_OPT_STR || 300 optp->opt_type == MDB_OPT_UINTPTR || 301 optp->opt_type == MDB_OPT_UINTPTR_SET || 302 optp->opt_type == MDB_OPT_SUBOPTS || 303 optp->opt_type == MDB_OPT_UINT64) { 304 /* 305 * More text after the option letter: 306 * forge a string argument from remainder. 307 */ 308 if (p[1] != '\0') { 309 arg.a_type = MDB_TYPE_STRING; 310 arg.a_un.a_str = ++p; 311 argp = &arg; 312 p += strlen(p) - 1; 313 314 nargs = 0; 315 /* 316 * Otherwise use the next argv element as 317 * the argument if there is one. 318 */ 319 } else if (++i == argc) { 320 warn("option requires an " 321 "argument -- %c\n", *p); 322 return (i - 1); 323 } else { 324 argp = ++argv; 325 nargs = 1; 326 } 327 } else { 328 argp = NULL; 329 nargs = 0; 330 } 331 332 /* 333 * Perform type-specific handling for this option. 334 */ 335 if (argvec_process_opt(optp, argp) == -1) 336 return (i - nargs); 337 } 338 } 339 340 return (i); 341 } 342 343 int 344 mdb_getopts(int argc, const mdb_arg_t *argv, ...) 345 { 346 /* 347 * For simplicity just declare enough options on the stack to handle 348 * a-z and A-Z and an extra terminator. 349 */ 350 mdb_opt_t opts[53], *op = &opts[0]; 351 va_list alist; 352 int c, i = 0; 353 mdb_subopt_t *sop; 354 355 va_start(alist, argv); 356 357 for (i = 0; i < (sizeof (opts) / sizeof (opts[0]) - 1); i++, op++) { 358 if ((c = va_arg(alist, int)) == 0) 359 break; /* end of options */ 360 361 op->opt_char = (char)c; 362 op->opt_type = va_arg(alist, uint_t); 363 364 if (op->opt_type == MDB_OPT_SETBITS || 365 op->opt_type == MDB_OPT_CLRBITS) { 366 op->opt_bits = va_arg(alist, uint_t); 367 } else if (op->opt_type == MDB_OPT_UINTPTR_SET) { 368 op->opt_flag = va_arg(alist, boolean_t *); 369 } else if (op->opt_type == MDB_OPT_SUBOPTS) { 370 op->opt_subopts = va_arg(alist, mdb_subopt_t *); 371 372 for (sop = op->opt_subopts; sop->sop_flag; sop++) 373 sop->sop_index = -1; 374 } 375 376 op->opt_valp = va_arg(alist, void *); 377 } 378 379 bzero(&opts[i], sizeof (mdb_opt_t)); 380 va_end(alist); 381 382 return (argvec_getopts(opts, argv, argc)); 383 } 384 385 /* 386 * The old adb breakpoint and watchpoint routines did not accept any arguments; 387 * all characters after the verb were concatenated to form the string callback. 388 * This utility function concatenates all arguments in argv[] into a single 389 * string to simplify the implementation of these legacy routines. 390 */ 391 char * 392 mdb_argv_to_str(int argc, const mdb_arg_t *argv) 393 { 394 char *s = NULL; 395 size_t n = 0; 396 int i; 397 398 for (i = 0; i < argc; i++) { 399 if (argv[i].a_type == MDB_TYPE_STRING) 400 n += strlen(argv[i].a_un.a_str); 401 } 402 403 if (n != 0) { 404 s = mdb_zalloc(n + argc, UM_SLEEP); 405 406 for (i = 0; i < argc - 1; i++, argv++) { 407 (void) strcat(s, argv->a_un.a_str); 408 (void) strcat(s, " "); 409 } 410 411 (void) strcat(s, argv->a_un.a_str); 412 } 413 414 return (s); 415 } 416