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
mdb_argvec_create(mdb_argvec_t * vec)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
mdb_argvec_destroy(mdb_argvec_t * vec)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
mdb_argvec_append(mdb_argvec_t * vec,const mdb_arg_t * arg)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
mdb_argvec_reset(mdb_argvec_t * vec)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
mdb_argvec_zero(mdb_argvec_t * vec)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
mdb_argvec_copy(mdb_argvec_t * dst,const mdb_argvec_t * src)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
argvec_process_subopt(const mdb_opt_t * opt,const mdb_arg_t * arg)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
argvec_process_opt(const mdb_opt_t * opt,const mdb_arg_t * arg)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 *
argvec_findopt(const mdb_opt_t * opts,char c)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
argvec_getopts(const mdb_opt_t * opts,const mdb_arg_t * argv,int argc)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
mdb_getopts(int argc,const mdb_arg_t * argv,...)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 *
mdb_argv_to_str(int argc,const mdb_arg_t * argv)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