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