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